newplace.cpp 是 C++ Primer Plus 第六版中 322 页的示例代码,比较有代表性,可以来讨论讨论运算符 new 的相关用法。
输出结果:
Calling new and placement new: Memory addresses: heap:00A72B18 static:0047CDEC Memory contents: 1000 at 00A72B18; 1000 at 0047CDEC 1020 at 00A72B20; 1020 at 0047CDF4 1040 at 00A72B28; 1040 at 0047CDFC 1060 at 00A72B30; 1060 at 0047CE04 1080 at 00A72B38; 1080 at 0047CE0C
Calling new and placement new a second time: Memory contents: 1000 at 00A72BA8; 1000 at 0047CDEC 1040 at 00A72BB0; 1040 at 0047CDF4 1080 at 00A72BB8; 1080 at 0047CDFC 1120 at 00A72BC0; 1120 at 0047CE04 1160 at 00A72BC8; 1160 at 0047CE0C
Calling new and placement new a third time: Memory contents: 1000 at 00A72B18; 1000 at 0047CE14 1060 at 00A72B20; 1060 at 0047CE1C 1120 at 00A72B28; 1120 at 0047CE24 1180 at 00A72B30; 1180 at 0047CE2C 1240 at 00A72B38; 1240 at 0047CE34 Press any key to continue
其实人家也给注释了,我们平时正常使用 new 运算符是不需要这个头文件的,但是如果想要使用它的变体——定位 new 运算符,就需要包含这个头文件。
我们从输出结果可以看到,大体分为三个部分,分别是三次内存分配以及其对应的地址的输出。
const int BUF = 512; char buffer[BUF]; // chunk of memory首先这里我们声明了一个能容纳 512 个 char 的内存空间,在这里它的作用其实是先霸占一片田地供后面的实验使用。
pd1 = new double[N]; // use heap pd2 = new (buffer) double[N]; // use buffer array这是第一部分申请空间的部分,pd1 使用的是常规的 new,从堆中申请空间并返回首地址。而 pd2 使用的是定位 new 运算符,他们都是 new 但是参数数量上有区别,功能上也有区别,它会在指定空间(buffer)中分配 N 个 double 空间。
for (i = 0; i < N; i++) { cout << pd1[i] << " at " << &pd1[i] << "; "; cout << pd2[i] << " at " << &pd2[i] << endl; }输出的其实就是在这一块儿内存上保存的值和这个值所在的地址。(后面的两次也都一样。) 1000 at 00A72B18; 1000 at 0047CDEC 是说 00A72B18是 pd1[i] 的地址,0047CDEC 是 pd2[i] 的地址。他们都存了值 1000, 这个值并不重要,只是为了区分方便,显示一下。
pd3 = new double[N]; // find new address pd4 = new (buffer) double[N]; // overwrite old data第二部分使用了新的变量名称,但我们看到,pd3 使用的是常规的 new 运算符,它负责从堆中申请一块空间,并返回首地址。而 pd4 仍然从 buffer 这里开始分配地址
所以我们看到第二次和第一次中 pd4 和 pd2 的地址一样,因为定位 new 运算符是在传入的那个地址处分配指定大小的空间而不去管这个空间之前有没有被占用。所以会出现两次地址一样(新写入的数据会复盖之前的)。
delete[] pd1; pd1 = new double[N]; pd2 = new (buffer + N * sizeof(double)) double[N];这是第三次的内存分配,我们可以在结果中看到,第三次的 pd1 和第一次的地址一样,它在 new 的时候重新占据了被 delete 的之前所占据的空间。而 pd2 通过计算,避开了正在被使用的内存空间,这里的定位 new 运算符被传入了指向buffer后面的地址。所以不会复盖之前的存在 buffer 里的数据。
定位 new 运算符创建的指针,不需要 delete,这不同于常规的 new 运算符。