通常,new负责在堆内存中找到一个能够满足要求的内存块。new操作符还有一种变体:placement new 操作符,它能让你指定要使用的内存的位置。 程序员可以使用这种特性来设置其内存管理规程或处理需要通过特定地址进行访问的硬件。
要使placement new,首先需要包含头文件new,下面的代码段演示了new操作符的用法:
#include <new>
#include <iostream>
using namespace std;
struct chaff{
char a[20];
int flag;
};
char buffer1[50];
char buffer2[500];
int main(){
cout<<"Buffer1 at "<<(void*)buffer1<<endl;
cout<<"Buffer2 at "<<(void*)buffer2<<endl;
chaff *p0,*p1,*p2;
int *p3,*p4,*p5;
p0=new chaff;
p3=new int[20];
cout<<"p0 at "<<p0<<endl;
cout<<"p3 at "<<p3<<endl;
delete p0;
delete [] p3;
p1=new (buffer1) chaff;
p4=new (buffer2) int [20];
cout<<"p1 at "<<p1<<endl;
cout<<"p4 at "<<p4<<endl;
p2=new (buffer1) chaff;
p5=new (buffer2+100) int [20];
cout<<"p2 at "<<p2<<endl;
cout<<"p5 at "<<p5<<endl;
return 0;
}
在上面的代码段中,对p1和p4使用了placement new操作符。这意味着,p1将使用以buffer1为首地址的内存块,而p4将使用以buffer2为首地址的内存块。下面的第二次使用placement new 操作符,p2将使用buffer1为首地址的内存块,而p5将使用buffer2首址+100的内存地址为起始地址的内存块。
程序在我机器上的输出如下:
Buffer1 at 0x437000
Buffer2 at 0x437040
p0 at 0x3d3e68
p3 at 0x3d3e88
p1 at 0x437000
p4 at 0x437040
p2 at 0x437000
p5 at 0x4370a4
当然,你可能会觉得程序有点奇怪。p1和p2都使用了以buffer1为首地址的内存。这样p2会不会将p1覆盖?另外使用placement new操作符的地方没有用delete语句释放内存。这样会不会出问题。答案是:不会。事实上,placement new操作符使用的是静态内存,而不是动态分配的内存。这就意味着,不能用delete操作符来对它进行内存释放。这就跟这样写一样:
int a;
delete a;
这反而会引发运行时错误。而你担心的p2将p1覆盖的问题,事实上并不会存在,但是p1和p2会指向同一块内存!我们可以在上面的程序的最下面加上这样的一段代码:
strcpy(p2->a,"hello");
p2->flag=100;
cout<<"p2->a = "<<p2->a<<endl;
cout<<"p2->flag = "<<p2->flag<<endl;
cout<<"p1->a = "<<p1->a<<endl;
cout<<"p1->flag = "<<p1->flag<<endl;
查看这时的输出,你会发现,p1和p2其实是完全一样的!