定位new运算符这里很有必要进行总结

new运算符负责在堆中(heap)中找到足以能够满足要求的内存块。
定位(placement)new运算符是new运算符的变体,能够指定要使用的位置。可以使用这种特性来设置其内存管理规程,处理需要通过特定地址访问的硬件或在特定位置创建对象。

调用方式

要使用定位运算符首先要包含头文件#include <new>
使用方式是在new运算符后面括号中添加所需地址的参数,例如:

1
2
3
char buffer[50];
int * p;
p = new (buffer) int[20]; // int数组放在以buffer为起始位置的静态内存中

定位new运算的本质其实就是把传递给他的地址强制转换成void *类型然后返回以便能够赋值给任何指针类型。
使用new运算符时候会触发new()函数,常规运算符的new()接受一个参数,定位运算符的new()接受两个参数:

1
2
3
int * 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
2
String * pstr = new String(str1); // 这将调用复制构造函数将对象str1复制并初始化
JustTesting * p = new (buffer) JustTesting("Test", 6); // 调用构造函数在相应地址处放置JustTesting对象的数据

delete 和 new

deletenew绝对是分不开了,而且在定位运算符中这俩还是处理起来比较复杂,很容易出错。

  1. 若是定位到由数组分配的空间,也就静态内存中,便不能使用delete来进行内存释放。

    1
    2
    3
    char buffer[50];
    int * p;
    p = new (buffer) int[20]; // int数组放在以buffer为起始位置的静态内存中

    buffer指定的就是静态内存,而delete只能用于这样的指针:指向常规new运算符分配的堆内存,也就是说buffer位于delete的管辖之外,所以只能坐等代码块结束自动释放掉了。

  2. 若是定位到由new分配的内存中,则要注意:

    delete只能释放最初分配内存时候得到的内存,但是之后在这块内存上创建的对象是释放不掉的。

    例如我先分配了一块内存

    1
    char * buffer = new char[30];

    然后我再上面使用定位new运算符初始化一个对象:

    1
    JustTesting * p = new (buffer) JustTesting("Test", 6);

    但是最后我使用delete释放掉buffer

    1
    delete [] buffer;

    Ok,buffer指向的内存块释放掉了,但是创建的JustTesting对象没析构。
    也就是说newdelete只知道已经分配的30个字节但是定位new运算符在这30个字节上面做过什么deletenew可都是一无所知。所以要析构上面的对象要显式的调用其析构函数:

    1
    p->~JustTesing();

最后一点

对于使用定位new运算符创建的对象,英语创建顺序相反的顺序进行删除。原因在于,晚创建的对象可能依赖于早创建的对象。另外仅当所有对象都被销毁后,才能释放用于存储这些对象的内存(在上面的例子中也就是buffer)。

Comments