#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其实是完全一样的!
虽然有三种new的用法,但是分为两大类也未尝不可,那么是哪两类呢?
其一是new operator,也叫new表达式;
其二是operator new,也叫new操作符。
这两个英文名称起的也太绝了,很容易搞混,那就记中文名称吧。new表达式比较常见,也最常用,
例如:
string* ps = new string("abc");
上面这个new表达式完成了两件事情:申请内存和初始化对象。
new操作符类似于C语言中的malloc,只是负责申请内存,例如:
void* buffer = operator new(sizeof(string));
注意这里多了一个operator。这是new的第二个用法,也算比较常见吧。
那么第三个用法就不很常见了,官方的说法是placement new,它用于在给定的内存中初始化对象,也就是说你手中已有一块闲置的内存,例如:
void* buffer = operator new(sizeof(string));
//那么现在buffer是你所拥有闲置内存的指针
buffer = new(buffer) string("abc"); //调用了placement new,在buffer所指向的内存中初始化string类型的对象,初始值是"abc"
事实上,placement new也是new表达式的一种,但是比普通的new表达式多了一个参数,当然完成的操作和返回值也不同。
因此上面new的第一种用法可以分解两个动作,分别为后面的两种用法。
与new对应的delete没有三种语法,它只有两种,分别是delete operator和operator delete,也称为delete表达式和delete操作符。delete表达式和new表达式对应,完成对象的析构和内存的释放操作。而delete操作符只是用于内存的释放,和C语言中的free相似。例如:
string* ps = new string("abc");
...
delete ps; //调用delete表达式,先析构再释放
void* buffer = operator new(sizeof(string));
...
operator delete(buffer); //释放
那么为什么没有和placement new对应的那个delete呢?其实是有的。placement new是在指定位置初始化对象,也就是调用了构造函数,因此与之对应的就是析构函数了,只不过它不叫placement delete而已。
void *pv = operator new(sizeof(vector<int>));
pv = new(pv) vector<int>(8, 0);
...
static_cast<vector<int>* >(pv)->~vector(); // call destruct function
operator delete(pv); // free memory
pv = NULL;
评论