您的位置:首页 > 编程语言 > C语言/C++

深入理解C++定位new

2020-01-15 08:13 302 查看

简介


一般的new运算符负责在heap堆中找到一个足以能够满足要求的内存块。

new运算符还有另一种变体:定位new运算符(placement new),它能够让程序员指定要使用的位置。既将new运算符用于提供了的地址。

定位new运算符在头文件new中。

定位new运算符直接使用传递给它的地址,它不负责判断哪些内存单元已被使用,也不查找未使用的内存块。这将一些内存管理的负担交给了程序员。

一个例子:

下面用一个简单的程序来说明定位new的用法:

#include <new>
#include <iostream>
#include <stdlib.h>
using namespace std;
int main() {
//chunk of memory内存池
char * buffer=(char *)malloc(512*sizeof(char));
int *p1, *p2, *p3, *p4;
//常规new:
p1 = new int[10];
//定位new:
p2 = new (buffer) int[10];
for (int i = 0; i < 10; ++i)
p1[i] = p2[i] = 20 - i;
cout << "p1 = " << p1 << endl;             //常规new指向的地址
cout << "buffer = " << (void *)buffer << endl; //内存池地址
cout << "p2 = " << p2 << endl;             //定位new指向的地址
cout << "p2[0] = " << p2[0] << endl;
delete []p1;
p3 = new (buffer) int;
*p3 = 1;
cout << "p3 = " << p3 << endl;
cout << "p3[0] = " << *p3 << endl;
p4 = new (buffer + 10 * sizeof(int)) int;
cout << "p4 = " << p4 << endl;
cout<<"p2:"<<p2<<" p3:"<<p3<<" p4:"<<p4<<endl;//定位new的地址,都处于内存池中
cout<<"p2:"<<*p2<<" p3:"<<*p3<<" p4:"<<*p4<<endl;//值也符合预期
delete buffer;//释放内存池,定位new出来的指针不用delete,也不能delete
cout<<"p2:"<<p2<<" p3:"<<p3<<" p4:"<<p4<<endl;//地址不变
cout<<"p2:"<<*p2<<" p3:"<<*p3<<" p4:"<<*p4<<endl;//但内容已经是内存垃圾
return 0;
}

输出结果:

注:为了方便,这里使用malloc提供内存空间。

可以看到,第一次使用定位new时,p2直接使用了我们显示供给的内存。第二次使用定位new时,程序还是直接使用了我们提供的地址,不管它是否已经被使用,而且可以看到新值直接覆盖在旧值上面。

第三次使用定位new时,由于我们提供了相对于buffer的偏移量,所以新的指针指向的地址与buffer首地址偏移了10个int字节。

另外一点要说明的是,不同与常规的new运算符,定位new运算符不需要相应的delete运算符来释放内存。因为它本身就不开辟新的内存。只是在开辟好的指定的内存地址上调用相应的构造函数去构造对象。

工作原理:

简单来说就是定位new运算符只是返回传递给它的地址,并将其强制转换为void *,以便能够赋给任何指针类型。

隐患:

用将定位new运算符来创建新的类对象后,当该对象消亡时,程序并不会自动地调用其析构函数,所以必须显示地调用析构函数。这是少数的需要显示调用析构函数的情况之一。

这里使用了内置类型 int,不需要调用析构函数。若是对象,须显示调用析构函数。

需要注意的是,对于使用定位new运算符创建的对象,应以与创建顺序相反的顺序进行删除。原因在于,晚创建的对象可能依赖于早创建的对象。另外,仅当所有对象都被销毁后,才能释放用于储存这些对象的缓冲区。

  • 点赞
  • 收藏
  • 分享
  • 文章举报
weixin_44135544 发布了78 篇原创文章 · 获赞 1 · 访问量 1028 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: