C++中的存储方案和动态分配详解
C++用来为变量(包括数组和结构)分配内存的5种方案(线程内存除外)不适用于使用 C++运算符new(或C函数 malloc())分配的内存,这种内存被称为动态内存。第4章介绍过,动态内存由运算符 new和 delete控制,而不是由作用域和链接性规则控制。因此,可以在一个函数中分配动态内存,而在另一个函数中将其释放。与自动内存不同,动态内存不是LIFO,其分配和释放顺序要取决于new 和 delete 在何时以何种方式被使用。通常,编译器使用三块独立的内存:一块用于静态变量(可能再细分),一块用于自动变量,另外一块用于动态存储。
虽然存储方案概念不适用于动态内存,但适用于用来跟踪动态内存的自动和静态指针变量。例如,假设在一个函数中包含下面的语句:
float *pfees = new float[20];
由new分配的80个字节(假设foat为4个字节)的内存将一直保留在内存中,直到使用delete 运算符将其释放。但当包含该声明的语句块执行完毕时,pfees指针将消失。如果希望另一个函数能够使用这80个字节中的内容,则必须将其地址传递或返回给该函数。另一方面,如果将pfees的链接性声明为外部的,则文件中位于该声明后面的所有函数都可以使用它。另外,通过在另一个文件中使用下述声明,便可在其中使用该指针:
extern float *p fees;
注意:在程序结束时,由 new分配的内存通常都将被释放,不过情况也并不总是这样。例如,在不那么健壮的操作系统中,在某些情况下,请求大型内存块将导致该代码块在程序结束不会被自动释放。最佳的做法是,使用 delete 来释放 new分配的内存。
如果要初始化动态分配的变量,该如何办呢?在C++98中,有时候可以这样做,C++11增加了其他可能性。下面先来看看 C++98 提供的可能性。
如果要为内置的标量类型(如int或double)分配存储空间并初始化,可在类型名后面加上初始值并将其用括号括起:
int *pi = new int(6); //*pi set to 6
double *pd = new double(99.99); // *pd set to 99.99
这种括号语法也可用于有合适构造函数的类。参见【0voice C++】
然而,要初始化常规结构或数组,需要使用大括号的列表初始化,这要求编译器支持C++11。C++11允许您这样做:
struct where {double x;double y;double z;);
where * one = new where(2.5,5.3,7.2}; // C++11
int *ar=new int[4]{2,4,6,7}; // C++11
在C++11中,还可将列表初始化用于单值变量:
int *pin = new int());// *pi set to 6
double *pdo = new double {99.99}; // *pd set to 99.99
new 可能找不到请求的内存量。在最初的10年中,C++在这种情况下让new 返回空指针,但现在将引发异常 std::bad alloc。第15 章通过一些简单的示例演示了这两种方法的工作原理。