C++官方参考链接:Dynamic memory - C++ Tutorials (cplusplus.com)
动态内存
在前几章看到的程序中,所有的内存需求都是在程序执行之前通过定义所需的变量来确定的。但在某些情况下,程序的内存需求只能在运行时确定。例如,当需要的内存取决于用户输入时。在这些情况下,程序需要动态分配内存,C++语言为此集成了操作符new和delete。
操作符new和new[]
使用操作符new分配动态内存。new后面跟着一个数据类型说明符,如果需要包含多个元素的序列,则使用括号[]内的数据类型说明符的数量。它返回一个指针,指向新分配的内存块的开头。它的语法是:
pointer = new type
pointer = new type [number_of_elements]
第一个表达式用于分配内存以包含类型type的单个元素。第二个用于分配类型type的元素块(数组),其中number_of_elements是表示这些元素数量的整数值。例如:
int * foo;
foo = new int [5];
在本例中,系统动态地为5个int类型的元素分配空间,并返回指向序列第一个元素的指针,该元素被赋值给foo(一个指针)。因此,foo现在指向一个有效的内存块,该块的空间可以容纳5个int类型的元素。
在这里,foo是一个指针,因此,foo指向的第一个元素可以用表达式foo[0]或表达式*foo访问(两者是等价的)。第二个元素可以通过foo[1]或*(foo+1)访问,以此类推。
声明普通数组和使用new为内存块分配动态内存之间有很大的区别。最重要的区别是,普通数组的大小需要是一个常量表达式,因此它的大小必须在程序运行之前设计时确定,而new执行的动态内存分配允许在运行时使用任何变量值作为大小分配内存。
程序请求的动态内存由系统从内存堆中分配。然而,计算机内存是一种有限的资源,它可能会被耗尽。因此,不能保证所有使用操作符new分配内存的请求都将被系统批准。
C++提供了两种标准机制来检查分配是否成功:
一种是处理异常。使用此方法,在分配失败时抛出bad_alloc类型的异常。异常是本教程后面介绍的一个功能强大的C++特性。但是现在,您应该知道,如果抛出了这个异常,并且它没有被特定的处理程序处理,则程序执行将被终止。
这个异常方法是new默认使用的方法,是在如下声明中使用的方法:
foo = new int [5]; // if allocation fails, an exception is thrown
另一种方法称为nothrow,当使用它时,当内存分配失败时,new返回的指针是空指针,而不是抛出bad_alloc异常或终止程序,程序继续正常执行。
该方法可以通过使用一个名为nothrow的特殊对象来指定,该对象在头文件<new>中声明,作为new的实参:
foo = new (nothrow) int [5];
在这种情况下,如果这个内存块的分配失败,可以通过检查foo是否是空指针来检测失败:
int * foo;
foo = new (nothrow) int [5];
if (foo == nullptr) {
// error assigning memory. Take measures.
}
这个nothrow方法可能产生比异常更低效的代码,因为它意味着显式地检查每次分配后返回的指针值。因此,异常机制通常是首选的,至少对于关键分配是这样。尽管如此,由于nothrow机制的简单性,接下来的大多数示例将使用它。
操作符delete和delete[]
在大多数情况下,动态分配的内存只在程序内特定的时间段内需要;一旦不再需要它,就可以释放它,这样内存就可以再次用于其他动态内存请求。这就是操作符delete的目的,它的语法是:
delete pointer;
delete[] pointer;
第一个语句释放使用new分配的单个元素的内存,第二个语句释放使用new和括号([])中的大小分配给元素数组的内存。
传递给delete的实参值要么是指向先前用new分配的内存块的指针,要么是空指针(在空指针的情况下,delete不会产生任何效果)。
// rememb-o-matic
#include <iostream>
#include <new>
using namespace std;
int main ()
{
int i,n;
int * p;
cout << "How many numbers would you like to type? ";
cin >> i;
p= new (nothrow) int[i];
if (p == nullptr)
cout << "Error: memory could not be allocated";
else
{
for (n=0; n<i; n++)
{
cout << "Enter number: ";
cin >> p[n];
}
cout << "You have entered: ";
for (n=0; n<i; n++)
cout << p[n] << ", ";
delete[] p;
}
return 0;
}
注意,new语句中括号内的值是用户(i)输入的变量值,而不是常量表达式:
p= new (nothrow) int[i];
总是存在这样的可能性:用户为i引入的值太大,以至于系统无法为它分配足够的内存。例如,当我试图给“How many numbers”问题赋值10亿时,我的系统无法为程序分配那么多内存,我得到了我们为这种情况准备的文本消息(Error: memory could not be allocated)。
通过检查指针值(如果nothrow)或捕获适当的异常,程序总是能够处理分配内存的失败,这被认为是一个很好的实践。
C中的动态内存
C++集成了操作符new和delete来分配动态内存。但这些在C语言中是不可用的;相反,它使用了一个库解决方案,函数malloc、calloc、realloc和free定义在头文件中<cstdlib>(在C中称为<stdlib.h>)。这些函数在C++中也可用,也可以用于分配和释放动态内存。
但是请注意,这些函数分配的内存块不一定与new返回的内存块兼容,因此它们不应该混合在一起;每一个都应该用它自己的一组函数或操作符来处理。