new 和 delete

new

用 new 申请的内存最少占用 1 个字节——尽管我们申请的可能是 0 个字节。

delete/delete[]

delete 和 delete[] 都会归还空间,但是 delete[] 会询问元素数量,并析构数组中的每个元素,而 delete 只会析构一个元素。

尽管虚析构函数允许我们正常 delete 掉指向子类对象的基类指针,但是在基类指针上使用 delete[] 可能是有错的:

#include <cstdio>

struct A {
    double a;
    virtual ~A() {
        puts("~A()");
        fflush(stdout);
    }
};

struct B : A {
    double b;
    ~B() override {
        puts("~B()");
        fflush(stdout);
    }
};

static_assert(sizeof(A) == 16);
static_assert(sizeof(B) == 24);

int main() {
    A *array_of_A = new B[2];
    delete [] array_of_A; // <-- 大问题!
}

上述代码在 Compiler Explorer 上编译后返回 139。这里应用 delete[],程序会将数组当成 A 数组来析构,把 A 的大小作为步长分割申请的空间,然后在每个”对象“上调用析构函数。这显然是不正确的,因为真正的步长是 B 的大小。

如果注释掉 B 类中的 double b; 声明,让 A 和 B 的大小一致,那么析构过程不会引发程序错误。但是非常不建议这么做!这对程序修改的容错不友好。

Placement new

一般不用于在已有对象上重新构造,毕竟那样可以直接赋值。可以用于在 arena(一块足够大的、连续的空间)上初始化对象,但是需要记得自行析构