C++中的析构函数

12

析构函数是用来在编译器释放对象之前执行一些最后时刻的清理工作,它是否会释放分配给对象的内存取决于对象的实现。


2
析构函数应删除析构函数对象持有的任何动态分配的对象,但析构函数不负责释放其所在对象所占用的内存。 - JoeG
6个回答

32
'编译器' 不会删除任何东西,它创建的代码在运行时才能实现。当您编写 delete somePointer; 时,本质上是让编译器编写:
  if ( has_virtual_destructor( * somePointer  ) ) {
       // virtual dispatch to a compiler-generated function
      dynamic_cast< true_dynamic_type * >(somePointer)->destroy_dynamic_type();
       /* contents of true_dynamic_type::destroy_dynamic_type() {
              this->~true_dynamic_type();
              operator delete( this); // executed within class context
       } */
  } else {
      somePointer->~ClassName();
      operator delete(somePointer);
  }
换句话说,析构函数被调用,然后operator delete被调用以释放存储空间。
如果析构函数是虚拟的,则使用虚拟分派在其最派生形式中对对象执行整个操作。实现这一点的常见方法是向每个虚拟析构函数添加隐藏参数。
请注意,顶层if语句并不是生成的代码的一部分;编译器在编译时做出该决定。

1
这是很多赞啊 ;v) ……也许在虚析构函数的情况下提到将cast转换为真正的动态类型。(而且,由于operator delete查找的工作方式,实际上是从“负责”的虚拟析构函数中调用了delete。如果不提及这一点,在分析跟踪时可能会令人困惑。) - Potatoswatter
@potatoswatter:随意编辑。我无法解释这场山体滑坡,我只是将显而易见的东西打了出来。 - bmargulies
一个小问题:这个 if (has_virtual_destructor… 在运行时是 不会 被执行的。编译器 可以为每种类型(因此仅取决于 静态 类型)决定它是否具有虚拟或非虚拟析构函数,并为两种情况发出适当的代码。不确定在尝试 delete 一个 void* 时会发生什么,但我猜测这将是未定义行为。 - Konrad Rudolph
编译器永远不会在运行时进行动态转换或检查类是否具有虚析构函数!最糟糕的情况是,如果使用虚继承,则会获取到相对于基类的地址。然后它只需通过虚表进行偏移量加上地址调用即可。如果没有虚析构函数,则它无法执行任何操作或仅调用析构函数(或内联它)。但是你的代码与其所做的事情相去甚远... - user405725

2

析构函数被调用以让对象执行清理操作,以及销毁对象本身创建的任何其他对象。

在析构函数完成后,操作系统将处理释放对象本身的工作。


0

1) 析构函数不属于对象,而属于类。

2) 它会调用其类中的所有自定义类型(类对象)的析构函数。

3) 清理是可选的活动,只有在真正需要时才进行。


0

在析构函数退出后,内存将被释放,并在执行返回到“delete”调用或对象实例超出范围的点之前进行。理论上可以设置不同的内存管理器来处理new和delete,但这将是显式更改默认行为。


0
更具体地说,在C++中,只有程序员才能释放内存。如果对象在堆栈上,则驻留在程序的内存空间中,并在程序的生命周期内占用空间。如果它在堆上,则创建对象的人负责释放它。这就是delete 的作用。这就带我们来到了析构函数 - 如果您在类中创建对象,则析构函数允许您在类离开作用域时删除它们。它让您“在离开时关闭灯”。

没有所谓的“程序员”。如果我作为一个程序员编写了一个具有std::vector类型成员变量的类,我不需要也不能编写删除它的代码。编译器将自动生成代码来调用某个其他程序员在某个时间编写并在头文件<vector>中提供的std::vector析构函数。 - Jive Dadson
即使不是你,也总有一个“程序员”——电脑不会杀人,人才会杀人。或者泄漏内存,无论哪种情况。如果你创建了std::vector的成员变量,那么它就在堆栈上。没有分配,没有责任。如果你的std::vector在内部分配堆内存,那么在这种情况下,“程序员”就是std::vector的程序员,而不是你,所以你不必担心它。 - Matt

0
析构函数会自动调用正在被销毁的对象的成员变量的析构函数。这些析构函数可能会或可能不会释放内存。然而,指针没有析构函数,或者说指针的析构函数什么也不做。它不会释放它所指向的内存。如果一个对象包含从 "new" 或 "malloc" 获取的对象的指针,则由该对象的程序员来决定析构函数是否执行正确的操作。如果内存在概念上是正在被销毁的对象的一部分,则应编写析构函数以 "delete" 或 "free" 内存。例如,"vector" 对象通常从堆中获取内存,因为所需内存的数量通常在编译时不知道。那段内存在概念上是 vector 对象的一部分,因此 vector 类的程序员必须在析构函数中调用 "delete" 来释放它。像 std::vector 这样的标准模板库类可以正确地执行此操作。
另一方面,有些对象包含对其他对象的引用。字典或索引将包含对不是它们概念上的一部分的对象的引用(指针)。析构函数不能释放该内存。(如果您从电话簿中删除您的电话号码,您不希望您的电话自动消失。)

对于初学者而言,有一些例外是不必首先关注的。其中之一是当对象及其容器被编程为使用引用计数时,所引用的对象直到最后一个引用它的对象释放它之前并不会真正释放。另一个例外是在“放置 new”情况下。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接