Cplusplus 简明教程
C++ Dynamic Memory
真正理解动态内存如何在 C 中工作对于成为一名优秀的 C 程序员至关重要。C++ 程序中的内存分为两个部分 -
-
The stack - 在函数中声明的所有变量都将占用堆栈内存。
-
The heap - 这是程序中未使用的内存,可用于在程序运行时动态分配内存。
很多时候,您事先不知道在已定义变量中存储特定信息需要多少内存,并且所需内存的大小可以在运行时确定。
您可以在堆中为给定类型的变量在运行时分配内存,方法是在 C++ 中使用一个特殊的运算符,该运算符返回已分配空间的地址。此运算符称为 new 运算符。
如果你不再需要动态分配的内存,你可以使用 delete 运算符,它可以取消分配那些之前通过 new 运算符分配的内存。
new and delete Operators
存在以下通用的语法来使用 new 运算符,以动态地为任何数据类型分配内存。
new data-type;
此处, data-type 可以是任何内置数据类型,包括数组或任何用户自定义数据类型,包括类或结构。让我们从内置数据类型开始。例如,我们可以定义一个指向 double 类型的指针,然后要求在执行期间分配内存。我们可以使用 *new * 运算符和以下语句来执行此操作:
double* pvalue = NULL; // Pointer initialized with null
pvalue = new double; // Request memory for the variable
如果 free store 已用完,则可能无法成功分配内存。因此,最好检查 new 运算符是否返回 NULL 指针并采取如下适当措施:
double* pvalue = NULL;
if( !(pvalue = new double )) {
cout << "Error: out of memory." <<endl;
exit(1);
}
C 的 malloc() 函数在 C 中仍然存在,但建议避免使用 malloc() 函数。new 比 malloc() 的主要优势在于,new 不仅分配内存,它还构造对象,这是 C 的主要目的。
当您觉得动态分配的变量不再需要时,您可以使用 delete
运算符释放其在 free store 中占据的内存,如下所示:
delete pvalue; // Release memory pointed to by pvalue
让我们使用以上概念并形成以下示例,以展示 new
和 delete
如何工作:
#include <iostream>
using namespace std;
int main () {
double* pvalue = NULL; // Pointer initialized with null
pvalue = new double; // Request memory for the variable
*pvalue = 29494.99; // Store value at allocated address
cout << "Value of pvalue : " << *pvalue << endl;
delete pvalue; // free up the memory.
return 0;
}
如果我们编译并运行上述代码,这将产生以下结果:
Value of pvalue : 29495
Dynamic Memory Allocation for Arrays
假定您想为一个字符数组(即 20 个字符的字符串)分配内存。使用我们上面使用的相同语法,我们可以按如下所示动态分配内存。
char* pvalue = NULL; // Pointer initialized with null
pvalue = new char[20]; // Request memory for the variable
要移除我们刚刚创建的数组,语句将如下所示:
delete [] pvalue; // Delete array pointed to by pvalue
按照 new 运算符的类似通用语法,您可以为多维数组分配内存,如下所示:
double** pvalue = NULL; // Pointer initialized with null
pvalue = new double [3][4]; // Allocate memory for a 3x4 array
然而,释放多维数组内存的语法仍与上面相同:
delete [] pvalue; // Delete array pointed to by pvalue
Dynamic Memory Allocation for Objects
对象与简单数据类型没有什么区别。例如,考虑以下代码,我们将在其中使用一个对象数组来阐明这个概念:
#include <iostream>
using namespace std;
class Box {
public:
Box() {
cout << "Constructor called!" <<endl;
}
~Box() {
cout << "Destructor called!" <<endl;
}
};
int main() {
Box* myBoxArray = new Box[4];
delete [] myBoxArray; // Delete array
return 0;
}
如果您要分配一个由四个 Box 对象组成的数组,那么 Simple 构造函数将被调用四次,在删除这些对象时,析构函数也将被调用相同次数。
如果我们编译并运行上述代码,这将产生以下结果:
Constructor called!
Constructor called!
Constructor called!
Constructor called!
Destructor called!
Destructor called!
Destructor called!
Destructor called!