big*_*iao 0 c++ language-lawyer
在C++中,当对象的析构函数被调用时,它首先调用子类的析构函数,然后是父类的析构函数,这与构造过程相反。但为什么?这似乎是一个简单的问题,但我还没有在互联网上找到令人满意的答案。有人可以解释以这种顺序进行破坏的必要性吗?
由于这被标记为 [language-lawyer],这是说明析构函数做什么的规则:
[class.dtor]
在执行析构函数的主体并销毁主体内分配的自动存储持续时间的任何对象后,类 X 的析构函数调用 X 的直接非变体非静态数据成员的析构函数,调用 X 的非虚拟直接基类的析构函数并且,如果 X 是派生程度最高的类 ([class.base.init]),则其析构函数调用 X 的虚拟基类的析构函数。所有析构函数都被调用,就好像它们是用限定名引用的一样,也就是说,忽略更多派生类中任何可能的虚拟覆盖析构函数。基类和成员的销毁顺序与其构造函数完成的顺序相反(参见 [class.base.init])。
为什么?...这很有意义。以一种顺序构建的事物通常以相反的顺序销毁。该约定适用于类的子对象、数组元素、函数中的局部变量以及在程序结束时销毁静态存储持续时间的对象。我想不出任何不是这种情况的事情。
虽然对象生命周期内的依赖可以是双向的,但在构造和销毁过程中,依赖只能是一个方向。之前创建的对象不能在其构造函数中依赖于稍后创建的对象(因为该对象尚不存在)。同样适用于销毁,但反过来:稍后销毁的对象不能在其析构函数内依赖于之前销毁的对象(因为该对象不再存在)。
如果您可以依赖构建中的某些东西,那么您通常会期望能够依赖于破坏中的某些东西。换句话说,您希望依赖的方向保持在同一方向。逆破坏顺序满足这个期望。
回到c/d-tors,构造函数体是在构造函数中最后执行的,所以析构函数体应该是析构函数中首先执行的。在某些情况下,能够在析构函数体内使用基础子对象会很有用,如果它已经被销毁,这是不可能的。
为什么基对象需要在销毁过程中处于有效状态?
要理解这一点,首先应该考虑为什么析构函数体存在。通常,它是改变类的内部状态,为结束生命周期做准备。通常熟悉的操作是删除作为成员存储的拥有指针(这只是一个示例:改用智能指针)。如果对象不再以有效状态存在,那么再接触现在无效的状态就太迟了。
但是任何基类函数都可以是虚函数,它也可以在析构函数过程中访问子成员。
虚函数调用在销毁期间解析为当前类(就像在构造期间一样)。