对 C++ 中动态内存工作原理的良好理解对于成为一名优秀的 C++ 程序员至关重要。C++ 程序中的内存分为两部分:
-
栈 — 所有在函数内部声明的变量将占用栈上的内存。
-
堆 — 这是程序未使用的内存,可以在程序运行时动态分配。
很多时候,在定义变量时并不知道需要多少内存来存储特定信息,并且所需内存的大小可以在运行时确定。
你可以在堆上为给定类型的变量分配内存,使用 C++ 中的一个特殊操作符来返回分配的空间地址。这个操作符称为 new
操作符。
如果你不再需要动态分配的内存,可以使用 delete
操作符,它会取消由 new
操作符先前分配的内存。
new
和 delete
操作符
new
操作符用于动态分配内存的通用语法如下:
new 数据类型;
这里的 数据类型
可以是任何内置数据类型,包括数组或任何用户定义的数据类型,包括类或结构体。让我们从内置数据类型开始。例如,我们可以定义一个指向 double
类型的指针,然后请求在执行时分配内存。我们可以使用 new
操作符通过以下语句来实现这一点:
double* pvalue = NULL;
pvalue = new double;
如果内存没有成功分配,因为自由存储空间已经被用完。因此,检查 new
操作符是否返回空指针并采取适当措施是良好的做法:
double* pvalue = NULL;
if( !(pvalue = new double )) {
cout << "错误: 内存不足。" << endl;
exit(1);
}
尽管 C 中的 malloc()
函数仍然存在于 C++ 中,但建议避免使用 malloc()
函数。new
操作符的主要优点在于它不仅分配内存,还构造对象,这是 C++ 的主要目标之一。
当你觉得某个动态分配的变量不再需要时,你可以使用 delete
操作符释放其在自由存储区中占用的内存:
delete pvalue;
让我们将上述概念放入以下示例中,以展示 new
和 delete
如何工作:
#include <iostream>
using namespace std;
int main () {
double* pvalue = NULL;
pvalue = new double;
*pvalue = 29494.99;
cout << "Value of pvalue : " << *pvalue << endl;
delete pvalue;
return 0;
}
如果我们编译并运行上述代码,这将产生以下结果:
Value of pvalue : 29495
动态数组内存分配
考虑你想要为一个字符数组分配内存,即长度为 20 个字符的字符串。使用与上述相同的语法,我们可以动态分配内存,如下所示:
char* pvalue = NULL;
pvalue = new char[20];
要移除刚刚创建的数组,语句如下:
delete [] pvalue;
遵循 new
操作符的相似通用语法,你可以为多维数组分配内存如下:
double** pvalue = NULL;
pvalue = new double [3][4];
然而,释放多维数组内存的语法仍然相同:
delete [] pvalue;
对象的动态内存分配
对象与简单数据类型并无二致。例如,考虑以下代码,我们将使用对象数组来澄清这一概念:
#include <iostream>
using namespace std;
class Box {
public:
Box() {
cout << "构造函数调用!" << endl;
}
~Box() {
cout << "析构函数调用!" << endl;
}
};
int main() {
Box* myBoxArray = new Box[4];
delete [] myBoxArray;
return 0;
}
如果你分配一个包含四个 Box
对象的数组,简单的构造函数将被调用四次;同样地,在删除这些对象时,析构函数也将被调用相同次数。
如果我们编译并运行上述代码,这将产生以下结果:
构造函数调用!
构造函数调用!
构造函数调用!
构造函数调用!
析构函数调用!
析构函数调用!
析构函数调用!
析构函数调用!