关于C++的定位new运算符
定位new运算符这里很有必要进行总结
new
运算符负责在堆中(heap)中找到足以能够满足要求的内存块。
定位(placement)new
运算符是new
运算符的变体,能够指定要使用的位置。可以使用这种特性来设置其内存管理规程,处理需要通过特定地址访问的硬件或在特定位置创建对象。
调用方式
要使用定位运算符首先要包含头文件#include <new>
。
使用方式是在new
运算符后面括号中添加所需地址的参数,例如:1
2
3char buffer[50];
int * p;
p = new (buffer) int[20]; // int数组放在以buffer为起始位置的静态内存中
定位new
运算的本质其实就是把传递给他的地址强制转换成void *
类型然后返回以便能够赋值给任何指针类型。
使用new
运算符时候会触发new()
函数,常规运算符的new()
接受一个参数,定位运算符的new()
接受两个参数:1
2
3int * p1 = new int; // 触发 new(sizeof(int))
int * p2 = new (buffer) int; // 触发 new(sizeof(int), buffer)
int * p3 = new (buffer) int[30]; // 触发 new(30*sizeof(int), buffer)
与初始化结合
定位new
运算符的另一种用法是将其与初始化结合使用,从而将信息放在特定的硬件地址处
调用方式是在最后面添加括号,在括号中添加参数,C++会调用相应的构造函数。例如1
2String * pstr = new String(str1); // 这将调用复制构造函数将对象str1复制并初始化
JustTesting * p = new (buffer) JustTesting("Test", 6); // 调用构造函数在相应地址处放置JustTesting对象的数据
delete 和 new
delete
和new
绝对是分不开了,而且在定位运算符中这俩还是处理起来比较复杂,很容易出错。
若是定位到由数组分配的空间,也就静态内存中,便不能使用
delete
来进行内存释放。1
2
3char buffer[50];
int * p;
p = new (buffer) int[20]; // int数组放在以buffer为起始位置的静态内存中buffer指定的就是静态内存,而
delete
只能用于这样的指针:指向常规new运算符分配的堆内存,也就是说buffer位于delete
的管辖之外,所以只能坐等代码块结束自动释放掉了。若是定位到由
new
分配的内存中,则要注意:delete
只能释放最初分配内存时候得到的内存,但是之后在这块内存上创建的对象是释放不掉的。例如我先分配了一块内存
1
char * buffer = new char[30];
然后我再上面使用定位
new
运算符初始化一个对象:1
JustTesting * p = new (buffer) JustTesting("Test", 6);
但是最后我使用
delete
释放掉buffer1
delete [] buffer;
Ok,buffer指向的内存块释放掉了,但是创建的JustTesting对象没析构。
也就是说new
和delete
只知道已经分配的30个字节但是定位new
运算符在这30个字节上面做过什么delete
和new
可都是一无所知。所以要析构上面的对象要显式的调用其析构函数:1
p->~JustTesing();
最后一点
对于使用定位new
运算符创建的对象,英语创建顺序相反的顺序进行删除。原因在于,晚创建的对象可能依赖于早创建的对象。另外仅当所有对象都被销毁后,才能释放用于存储这些对象的内存(在上面的例子中也就是buffer)。