为什么在使用 new 运算符声明对象时编译器不自动调用析构函数?

Why don't compilers call destructor automatically when an object is declared using new operator?

#include<iostream>

using namespace std;

class b {
    public:
        int *a;

    b (int val) {
        *a = val;
    }

    ~b() {
        cout << "destructor" << endl;
        delete a;
    }
};

int main() {
    b *obj = new b(1);
    cout << "end" << endl;
    return 0;
}

预期输出:

destructor
end

收到输出:

end

在上面的代码中,当我使用 new 运算符创建对象时,编译器不会调用析构函数,但在正常对象析构函数调用成功的情况下。 背后的原因是什么?

使用 new 创建对象至少有三个不同的原因;根据您这样做的原因,完成后删除它可能合适也可能不合适。

第一个是你想管理它的生命周期:

int *create(int i) {
    int *result = new int(i);
    return result;
}

在这里,您不想在函数结束时销毁 int 对象。使用 new 创建它意味着您必须在完成后注意清理它:

int main() {
    int *xp = create(3);
    std::cout << *xp << 'n';
    delete xp; // I'm done with it
    // more code can go here, but don't use xp
    return 0;
}

另一个原因是对象是数组,编译时不知道它的大小,所以不能创建为自动对象:

void f(int size) {
    int my_array[size]; // illegal, although some compilers allow it, sigh
    int *my_ptr = new int[size]; // okay, creates array with size elements
    // code that uses my_ptr goes here
    delete[] my_ptr;
}

但是那个真的应该用 std::vector<int> 来写,这样可以减轻你在完成后必须记住删除数组的负担:

void f(int size) {
    std::vector<int> my_array(size);
    // code that uses my_array
    // no delete, because no explicit new; my_array destructor handles the memory
}

最后,您可能正在处理一个大到您不想用它弄乱堆栈的对象:

struct my_large_type {
    long double filler[32768];
};

是的,您可以在堆栈上创建这样的对象:

void g() {
    my_large_type x;
}

但是拥有许多这样的东西会冒着占用 space 超出堆栈处理能力的风险。因此,您可以在免费商店中使用 new:

创建它们
void g() {
    my_large_type *xp = new my_large_type;
    // code that uses *xp goes here
    delete xp;
}

这里的危险是您可能会忘记删除内存,或者更糟的是,newdelete 之间的代码可能会抛出异常,因此删除不会没有发生 -- new 分配的内存刚刚泄漏。

这里正确的解决方案是使用智能指针,这样您就不必自己处理删除对象:

void g() {
    std::unique_ptr<my_large_type> xp = new my_large_type;
    // code that uses *xp goes here
    // no delete; destructor will do it for you
}

总而言之:如果您想在创建对象的块末尾清理 newed 对象,最好不要显式使用 new,而是将责任转化为 class ,其析构函数将处理清理。在这些示例中,这将是 std::vectorstd::unique_ptr。这留下了您想要显式管理对象生命周期的情况,因为您在创建它时不知道它应该停留多长时间。那样的话,你不清理,它就清理不干净;你告诉编译器你会处理它,编译器信任你。