Fork me on GitHub

More Effective C++ 8:new和delete

前言

最近在看侯捷译的More Effective C++,看的过程有不少收获,发现自己对C++很多地方其实没有过多去了解,收获很多,记录一下。

三种new和delete

我们知道,new和delete都是C++里的关键字,同时也是操作符。new负责分配内存,delete释放内存。new和delete都有new operator、operator new,placement new,delete operator。下面分别介绍这几种操作

new operator和delete operator

new operator和delete operator就是我们经常使用的new和delete,这两个操作符是由语言内建的,就像sizeof操作符一样,不能被改变意义。当我们写出这样的代码时new operatorf和new delete在执行时分为两步:

1
2
string *ps=new string("new operator");
delete ps;
  1. new operator 会先执行分配内存的操作,它会调用一个只分配内存的函数,这个函数就是operator new,它分配足够的内存,用来放置对象。new delete在释放内存前,会先调用对象的析构函数.
  2. 当new operator分配完内存的时候,它会调用一个构造函数,为刚才分配的内存中的对象设定初值。而delete operator调用完析构函数时,它会调用一个释放内存的函数 operator delete,这时候才会释放对象所占用的内存。

以上两行代码,拆分开来就是这样,先调用构造函数,分配内存,调用析构函数,释放内存,在编译器中的操作如下

1
2
3
void *memory=operator new(sizeof(string)); //取得原始内存
call string::string("Memory Management") on*memory;//将内存中的对象初始化
string *ps=static_cast<string*>(memory);
  • 注意new operator 和new delete都不可以被重载。

operator new和operator delete

上面一直说到new和delete其实在内部都会调用到这两个函数,这两个函数到底跟new和delete有什么区别呢?通常operator new ,operator delete声明如下:

1
2
void *ps=operator new(sizeof(string));
operator delete(ps);

placement new(定位new)

写这篇文章,其实主要是写这个用法,还记得看到这个用法的时候,是在侯捷翻译的另一本书《STL 源码剖析》在讲空间配置器时看见的。

placement new是operator new的一种特殊版本,也是new operator的另一种用法,在已分配的内存上构造对象。要使用placement new必须要包含头文件new,#include或者#include<new.h>,调用示例就直接用书上的了。

1
2
3
4
5
6
7
8
9
class Widget
{
public:
Widget(int widgetSize);
};
Widget *constructWidgetInBuffer(void *buffer,int widgetSize)
{
return new(buffer) Widget(widgetSize);
}

此函数返回指针,指向一个Widget对象,它被构造于传递给此函数的一块内存缓冲区上。在函数内部唯一的表达式就是:

1
new (buffer) Widget(widgetSize);

于是,在buffer指向的这边空间,就构造了一个Widget对象。

注意,如果你使用了placement new,在某块内存中产生对象,应该避免对那块内存使用delete operator。因为这个delete operator会调用operator delete来释放内存,但是该内存内含的对象最初并非是由operator new分配得来的,毕竟placement new只是返回它所接受的指针而已,应该直接调用该对象的析构,然后在释放内存。

1
2
3
4
5
6
7
8
void *mallocShared(size_t size);
void freeShared(void *memory);
void *sharedMemory=mallocShared(sizeof(Widget));
Widget *pw=constructWidgeInBuffer(sharedMemory,10);//placement new用法
...
delete pw;//错。sharedMemory来自mallocShared,不是来自operator new
pw.~Widget(); //可以,析构pw所指的Widget对象,但并未释放Widget占用的内存
freeShared(pw); //可以,释放pw所指的内存,不调用任何析构函数
您的赞赏是对我最大的支持,谢谢!