为什么在使用 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;
}
这里的危险是您可能会忘记删除内存,或者更糟的是,new
和 delete
之间的代码可能会抛出异常,因此删除不会没有发生 -- 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
}
总而言之:如果您想在创建对象的块末尾清理 new
ed 对象,最好不要显式使用 new
,而是将责任转化为 class ,其析构函数将处理清理。在这些示例中,这将是 std::vector
和 std::unique_ptr
。这留下了您想要显式管理对象生命周期的情况,因为您在创建它时不知道它应该停留多长时间。那样的话,你不清理,它就清理不干净;你告诉编译器你会处理它,编译器信任你。
#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;
}
这里的危险是您可能会忘记删除内存,或者更糟的是,new
和 delete
之间的代码可能会抛出异常,因此删除不会没有发生 -- 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
}
总而言之:如果您想在创建对象的块末尾清理 new
ed 对象,最好不要显式使用 new
,而是将责任转化为 class ,其析构函数将处理清理。在这些示例中,这将是 std::vector
和 std::unique_ptr
。这留下了您想要显式管理对象生命周期的情况,因为您在创建它时不知道它应该停留多长时间。那样的话,你不清理,它就清理不干净;你告诉编译器你会处理它,编译器信任你。