如果在一个函数中,我新建一个对象,我应该在函数退出之前调用指针上的delete,还是析构函数,这会在函数退出后自动调用,执行删除工作?
例如,
void f()
{
A * a = new A(); // assume A has destructor defined, which frees any dynamically assigned resources
}
OR
void f()
{
A * a = new A();
delete a;
}
删除和自动调用析构函数是否等效?
答案 0 :(得分:9)
如果在一个函数中,我新建一个对象,我应该在函数退出之前调用指针上的delete,还是析构函数,这会在函数退出后自动调用,执行删除工作?
从函数返回时,所有具有自动存储持续时间的本地对象都将被销毁。如果它们是类类型,则在它们占用的存储被声明回来之前调用它们的析构函数。如果它们是非类类型(如int
),则不会调用析构函数。
这里,唯一具有自动存储持续时间的本地对象是指针a
(注意:不是a
指向的对象!),指针不是类类型。这意味着a
将被销毁,就是这样 - 特别是,对象a
指向的对象不会被销毁。
因此,您必须在离开函数之前调用delete
(无论您是通过执行return
还是抛出异常而离开它)。一般情况下,您始终需要将每次通话与new
的来电匹配为delete
,并且每次通过拨打new[]
拨打delete[]
。
因为在用delete
创建对象之后很容易忘记调用new
(因为它很容易调用它!),所以在Modern C ++中使用它是一种很好的做法所谓的RAII包装器(例如智能指针),它们是本地对象,其析构函数用于清理构造期间获取的资源。
例如:
void foo()
{
auto p = std::make_unique<A>(); // OK, make_unique() will only be available
// in C++14. Meanwhile, in C++11 you can do:
//
// std::unique_ptr<A> p(new A());
// Work with p...
// No memory leak here!
}
如果你不被允许使用C ++ 11,例如因为你的老板说出于兼容性原因,SW必须在旧版本的编译器上编译,你总是可以杀死你的老板使用Boost的智能指针类,例如boost::shared_ptr
和boost::scoped_ptr
。
无论如何,请注意,除非您需要,否则不应执行动态分配。如果您不需要它(例如,如果您不必与任何其他函数共享该对象的所有权),您可以简单地为您的A
对象提供自动存储持续时间,从而确保在以下时调用其析构函数它超出了范围:
void foo()
{
A a;
// Work with a...
// a will be destroyed when returning from foo()
}
答案 1 :(得分:3)
只有自动对象超出范围时才会被销毁:
void f() {
A a;
} // destroyed here
如果您使用new
动态分配对象,则您有责任使用delete
解除分配对象;否则,它将永远分配,你会泄漏内存。这可能非常难以正确管理,特别是如果异常可能导致您离开指针的范围。
出于这个原因,除非你真的需要,否则不要使用new
;并始终使用共享指针或其他RAII对象来管理动态对象。
答案 2 :(得分:1)
唯一可以释放的是指针变量本身(因为它还需要内存来存储它指向的地址,并且其范围仅在函数体中)。指针指向的对象完全由您负责,因此您应该在显式分配时明确删除它。
答案 3 :(得分:1)
当您动态分配内存时,如果变量超出范围,则不会释放内存。你必须明确delete
那个记忆。
答案 4 :(得分:0)
我将从此开始,
什么时候是一个名为?
的构造函数创建(构建)对象时。
MyClass c,d(1);
等。
构造函数用于在使用之前正确初始化对象。 分配必要的动态内存等。
什么时候是析构函数?
当一个对象超出范围时(当你遇到声明了该对象的}
大括号时)。
当对象超出范围时,会自动调用析构函数。
动态分配内存(new
)时,
它一直持续到你明确释放(delete
)它。什么比构造函数更好,以确保你想要发生的*事情*。
因为,在对象被销毁之前,保证要析构析构函数,你可以在其上调用delete
。
默认情况下,析构函数不会清除您可能在代码中创建的副作用。它不会为你释放记忆。
你知道你的管家会跟着你走出房间。并且只会做你要求他做的事情。如果你指示他,他会在你离开后清理你的桌子。如果你不指示他这样做,你必须确保你自己在某个地方做到这一点或者只是留下一团糟