天天看点

Effective C++ 条款八 了解各种不同意义的new与delete

我们常见的 new运算符

用起来大概是这样的:

Class A{

    A(){......;}

};

A* ptr = new A;

上面这种new其实可以分解为两步:

1.分配内存

2.在该段内存上执行A的构造函数

如下所示:

void * memory = operator new(sizeof(string));        //分配内存

call string:: string("Memory Management") on *memory;    //在分配好的内存上调用构造函数

string* ps = static_cast<string*>(memory);        //将其由void强制转换为想要的类型

C++新标准中,我们虽然不能够改变new,但是可以改变这个“operator new”,即如何分配内存:比如在刚刚分配内存后还没调用构造函数的时候就给这片内存一个初值之类的操作。

我们就可以这样调用

void *rawMemory = operator new(sizeof(string));

同时,一旦我们重载了operator new,那么我们在进行动态变量创建(即使用new运算符进行变量创建时),在分配内存阶段,编译器会调用我们所重载的那个内存分配函数,而不是原始的那种。。。

另一个需要注意的时Placement new,即在一块已知的内存上去调用构造函数构建对象,而不是使用传统的new方法先分配内存(此时内存的地址时系统分配的,我们不造)再调用构造函数。

用Placement new的好处是,如果我们的对象被要求必须位于特定的地址,或者是置于以特殊函数分配出来的内存上的话,我们必须先手工分配一块我们期望的内存,然后调用Placement new在这块内存上构建对象

用法如下:

Class Widget{

public:

    Widget(int widgetSize);

};

Widget * constructWidgetInBuffer(void* buffer, int widgetSize)

{

    return new (buffer) Widget(widgetSize);        

}

总结:

1.如果你希望在堆上创建对象,最好使用new operator(即原生的new),它不但分配内存而且为该对象调用一个constructor;

  此种情况下,若要进行收尾工作,则可直接调用 delete 操作符(我们常见的delete),它首先会析构掉对象,然后释放掉内存

2.如果你只打算分配一块内存,那么最好调用operator new,它会返回一个指向所分配内存区域的指针。

  此种情况下,若要进行收尾工作,则需要调用operator delete,这样会收回由operator new分配的内存,而不会调任何类的析构函数

3.如果你打算在堆上创建内存时自己决定内存分配方式,那么你需最好写一个自己的operator new版本,并使用new operator(即我们常说的new),它会自动调用你所写的operator new来分配内存。

  此种情况下,若要进行收尾工作,则直接调用原生的delete

4.如果你打算在已分配的一段内存里构建对象,那么就是用placement new,将指向该段内存的指针以void*的类型,以new (ptr) A();的形式建构对象,这将会在ptr所指向的内存区域调用A();

  此种情况下,若要进行收尾工作,则只需要调用析构函数就好,不能调用任何释放内存的操作数(因为这里ptr所指向的内存不归收尾工作管,那片内存在placement new之前就已经存在了)

总结:

在动态分配数组的情况下会稍微复杂些。对应的操作符为operator new[] 与 operator delete[],这里不详细展开。

总之,new operator 和 delete operator都是内建操作符,无法为你所控制,但是它们所调用的内存分配/释放函数则不然。当你向要定制new operator和delete operator的行为,记住,你其实无法真正办到。

你可以修改它们完成任务的方式(如指定初始内存的值),至于它们的任务,已经被C++语言规范固定死了。