析构函数和'delete'之间的区别

The difference between a destructor and 'delete'

本文关键字:区别 之间 delete 析构函数      更新时间:2023-10-16

在C++中销毁对象和删除有什么区别?在 OOP 中,当对象超出范围时,将调用析构函数,而在动态分配内存时,我们必须在析构函数中使用delete关键字。那么,销毁或删除对象意味着什么?

析构函数是class的特殊方法。每当对象超出范围或有人显式调用它时,都会调用它。

调用对象析构函数但不

删除的示例:
struct MyObject
{
MyObject() { std::cout << "default constructor"; }
~MyObject() { std::cout << "destructor"; }
void doWork() {};
}
int main()
{
{
MyObject object; //constructor is called and memory allocated for the object on the stack
object.doWork();
} //at the end of scope, the destructor of MyObject is called, and the stack memory is "released"
// object is not longer available and stack memory is free
}

作用域对您没有帮助的示例:

int main()
{
{
MyObject* object_ptr = new MyObject; //constructor is called and memory allocated on the heap. The pointer_ptr object 
object_ptr->doWork();
} //at the end of scope the object_ptr is removed from the stack, but the destructor for the MyObject is not called!
// object_ptr is no longer available.
}

对于上面的例子,我们有一个动态分配的内存问题。我们通过调用 delete 来显式调用我们指向的对象的析构函数(并释放 MyObject 所在的内存!

int main()
{
{
MyObject* object_ptr = new MyObject; //constructor is called and memory allocated on the heap. The pointer_ptr object 
object_ptr->doWork();
delete object_ptr; // destructor of MyObject is called and memory freed.
// object_ptr is still usable here, but it is pointing at an destroyed object, so don't use it! (unless you redirect it)
} 
}

所以delete用于管理动态内存,但析构函数是类本身的方法,当对象从内存(堆栈或堆(中释放时,总是调用它。析构函数是在释放对象的内存时调用的,因此,如果对象本身处理内存,则有时间释放该内存。这就是为什么智能指针很好:

int main()
{
{
std::unique_ptr<MyObject> object_ptr = std::make_unique<MyObject>(); //constructor is called and memory allocated on the heap. The pointer_ptr object 
object_ptr->doWork();
}  //destructor for std::unique_ptr<MyObject> is called, which in turn calls delete on the object it is pointing at, which calls the destructor of that object!
}

有多种内存分配方法,其中一些是:

  1. 自动内存分配:
    在这种情况下,作用域内定义的变量(例如函数(是自动分配的,并且通常存储在堆栈上。您对此内存的生存期的控制有限。例如:函数中的自动变量仅在函数完成之前存在,然后当超出该范围时,会自动调用它们的析构函数,即您不得调用delete来释放为该对象保留的内存。
  2. 动态内存分配 :
    在这种情况下,作用域内定义的变量,例如函数,是动态分配的。您可以控制这些内存位置的大小和生存期。如果不释放保留的内存(删除它(,则分配的内存仍然有效且可访问,即使函数已终止(超出范围(,在这种情况下,您将出现内存泄漏,这可能会导致程序崩溃。因此,应显式删除该对象,这反过来将调用该对象的析构函数

就像构造函数只是创建对象过程的一部分一样,调用析构函数只是销毁对象过程的一部分。这是一个特殊清理代码可以去的地方,例如关闭文件句柄或销毁其他间接拥有的对象。

delete是触发动态分配对象的删除的方式。其他类型的对象在超出范围时会自动删除。此关键字将清理内存并更抽象地结束对象的生存期,以便编译器知道它在概念上不再"存在"。

通常不会手动调用析构函数,因为它通常不足以安全地结束对象的生存期。但是,当您对对象的存在进行超细粒度控制时,这样做很有用,例如,放置new:那么,它可能就足够了。但这是一个特例。

当一个对象被销毁时,它的析构函数被调用。可以删除指向动态分配的内存的指针,但不能删除对象本身(除非您将指针视为一种对象(。

这些操作中的任何一个都可能产生连锁效应,导致其他操作发生(或同一操作的多个实例(。例如,如果删除指向对象的指针,则将调用对象析构函数(如果有(,并且它的析构函数可能会导致删除其他指针并销毁其他对象。

基本上析构函数是关于对象的死亡,删除是关于释放动态分配的内存,但这两个操作通常是交织在一起的。