第二章,操作符

条款8 了解各种不同意义的new和delete

区分new operator与operator new

new operator是C++内建的,其行为有两个,一是隐式调用operator new分配足够的内存空间,二是调用构造函数返回对象给指针。

//编写的代码:
string *ps = new string("Memory Management");

//编译器解析做以下行为:
void *memory = operator new(sizeof(string));//取得原始内存以放置string对象

call string::string("Memory Managemen") on *memory;//将内存中的对象初始化

string *ps = static_cast<string*>(memory);//让ps指向对象

在重载new操作符时,实际是重载operator new,无法直接调用构造函数来完成new operator的第二个行为; operator new必须要有一个size_t自变量参数。

Placement new

要实现在已经分配好的内存上调用构造函数,可以使用placemen new,这是new operator的一种用法。

#include <new>

class Widget{
public:
Widget(int widgetSize);
...
};

Widget * constructWidgetInBuffer(void *buffer, int widgetSize)
{
return new (buffer) Widget(widgetSize);//返回被构造在内存缓冲区buffer上的一个Widget对象
}

在new之后加上一个额外的自变量(buffer),当new operator(语言内建)隐式调用operator new时,指定在这块内存上构造对象;这里的operator new 除了接收widgetSize还接收了一个void*参数,就像这样:

void * operator new(size_t,void *location)
{
return location;
}

operator new的目的就是为对象找到一块内存,然后返回指向这块内存的指针。
注意,要使用plcaement new必须包含头文件new.h

删除与内存释放

内建的delete operator与operator delete和new operator与operator new的关系同样是前者隐式调用后者。

string *ps;
...
delete ps; //使用delete operator

编译器在解析上面的代码时会生成对应的代码,确保既能够析构ps所指向的对象,又能够释放该对象占用的内存。其中内存释放动作是由函数operator delete执行的,通常声明如下:

void operator delete(void *memoryToBeDeallocated);

因此 delete ps;会让编译器产生如下代码:

ps->~string();       //调用对象的析构函数
operator delete(ps); //释放对象所占用的内存

回顾总结并强调

(1)打算处理原始未设初值的内存,应该完全回避new operator和delete operator(实际就是new与delete),而是改调用 operator new 和 operator delete(这是实际可写的代码,不是称谓); 这组行为在C++中相当于调用malloc与free;

(2)当使用Placement new在某内存中产生对象,应该避免对该内存使用delete operator, 因为这块内存不是由operator new分配得来的。Placement new只是返回它所接收的指针,但指针从何而来无人知晓。所以为了抵消改对象的构造函数的影响,应该直接调用该对象的析构函数:

#include <new>

class Widget{
public:
Widget(int widgetSize);
...
};

Widget * constructWidgetInBuffer(void *buffer, int widgetSize)
{
return new (buffer) Widget(widgetSize);//返回被构造在内存缓冲区buffer上的一个Widget对象
}

//以下函数用来分配及释放shared memory内存
void* mallocShared(size_t size);
void freeShared(void *memory);

void *sharedMemory = mallocShared(sizeof(Widget));
Widget *pw = constructWidgetInBuffer(sharedMemory,10);//在已有内存上分配10的大小空间,并返回Widget对象指针

...

delete pw;// 无定义! 因为sharedMemory来自mallocShared而不是operator new

pw->~Widget();//可以! 析构了pw所指对象,但是内存还未释放

freeShared(pw);//可以! 释放pw所指内存

数组

string *ps = new string[10]; //分配一个对象数组

上述使用的仍然是new operator,但是隐式调用的不再是operator new,而是operator new[]; 数组版的new operator会针对数组中的每一个对象调用构造函数,数组版的delete operator也会针对数组中的每一个对象调用析构函数。

operator new[]和operator delete[]都可以操作符重载