1️⃣ 问题的引入

  • 如何理解new operatoropertor new 之间的差异 ?
  • 什么是 placement new,它的作用是什么?

2️⃣ 案例解析

1
string* ps = new string("Memory Management");
  • 这里使用的new是所谓的new operator。这个操作符是由语言内建的,不能被改变意义。
  • 它的动作以下两方面:
    • 第一,它分配足够的内存,用来放置某类型的对象。
    • 第二,它调用一个constructor,为刚才分配的内存中的那个对象设定初值。
  • 我们能够改变的是用来容纳对象的那块内存的分配行为。
    • newo perator调用某个函数,执行必要的内存分配,我们可以重写那个函数,改变其行为。
    • 这个函数的名称叫做operator new

3️⃣ 进一步理解

  • 函数operator new 的定义式:
1
void* operator new(size_t size);
- 返回值和参数解析
    * 返回值类型为 `void*`,返回的指针,指向一块原始的、未设初值的内存
    * `size_t` 表示需要分配多少内存。
    * `operator` 可以被重载,加上额外的参数,但是第一参数必须是`size_t`。
- 作用
    * `operator new`和`malloc`一样。它唯一任务就是分配内存。它不知道什么是`constructr`,  `operator new`只负责内存分配。
  • new operator的流程
1
2
3
4
5
6
7
8
void *memory =								//取得原始内存(rawmemory)。
operator new(sizeof(string)); //用来放置一个string对象。

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

string *ps = //让ps指向新完成的对象。
static_cast<string*>(memory);

4️⃣ Placement new

  • 应用场景:你已拥有一些分配好的原始内存,只需要在上面构建对象。
1
2
3
4
5
6
7
8
9
10
class Widget {
public:
Widget(int widgetSize);
...
};

Widget * constructWidgetInBuffer(void *buffer, int widgetSize)
{
return new (buffer) Widget (widgetSize);
}
  • 解析
    • 函数返回指针,指向一个Widgetobject,它被构造于传递给此函数的一块内存缓冲区。
    • new (buffer) Widget (widgetSize);
      • 指定一个额外自变量(buffer)作为new operator隐式调用operator new时所用。
      • 被调用的operator new除了接受一定得有的size_t自变量之外,还接受了一个void*参数,指向一块内存,准备用来接受构造好的对象。
  • 作用
    • operator new的目的是要为对象找到一块内存,然后返回一个指针指向它。
    • placement new的情况下,调用者已经知道指向内存的指针了,因为调用者知道对象应该放在哪里。因此placement new唯一需要做的就是将它获得的指针再返回。

5️⃣ 三类new的使用场景

  • 如果你只是打算分配内存,请调用operator new,那就没有任何constructor会被调用。
  • 如果你打算在heap objects产生时自己决定内存分配方式,请写一个自己的operator new,并使用 new operator,它将会自动调用你所写的operator new
  • 如果你打算在已分配(并拥有指针)的内存中构造对象,请使用placement new

6️⃣ 删除(Deletion)与内存释放(Deallocation)

  • 问题的引入

    • 为了避免resourceleaks(资源泄漏),每一个动态分配行为都必须匹配一个相应但相反的释放动作
    • operator delete对于内建的delete operator,等价operator new对于new operator一样
  • 案例引入

    1
    2
    3
    string* ps;
    ...
    delete ps;
  • delete 的执行流程

    • 先调用ps->~string(),先调用对象的析构函数。
    • 再调用operator delete(ps),再释放对象所占用的内存。
  • 进一步理解

    • 如果使用placement new,在某内存块中产生对象,就避免对那块内存使用delete operator
    • 因为delete operator会调用operator delete来释放内存,但是该内存内含的对象最初并非是由operator new分配得来的。
    • 毕竟placement new只是返回它所接收的指针而已,谁知道那个指针从哪里来呢?所以为了抵消该对象的constructor的影响,你应该直接调用该对象的destructor