【发布时间】:2011-03-31 05:50:18
【问题描述】:
当我使用虚函数时,有人可以帮忙看看破坏的顺序是什么。是从基类开始,然后是派生类吗?
【问题讨论】:
标签: c++ destructor virtual-destructor
当我使用虚函数时,有人可以帮忙看看破坏的顺序是什么。是从基类开始,然后是派生类吗?
【问题讨论】:
标签: c++ destructor virtual-destructor
第 12.6.2/5 节:
初始化应按以下顺序进行:
- 首先,并且仅适用于最派生类的构造函数 如下所述,虚拟基类应在 它们出现在深度优先从左到右的遍历中的顺序 基类的有向无环图,其中“从左到右”是 派生类中基类名称的出现顺序 基本说明符列表。
- 然后,应初始化直接基类 按照它们出现在基本说明符列表中的声明顺序 (不管 mem-initializers 的顺序如何)。
- 然后,非静态 数据成员应按照它们被声明的顺序进行初始化 类定义(同样不管 内存初始化器)。 — 最后,构造函数的主体被执行。
[注意:声明顺序的强制要求是确保基础和 成员子对象以相反的顺序被销毁 初始化。 ]
【讨论】:
由于我看不到虚函数如何改变任何对象的销毁顺序,我假设您指的是虚拟继承中基类和数据成员的销毁顺序 场景。
子对象是构造的
Destruction与construction正好相反,所以你只需要记住以上内容即可。
但是,上面的四个规则是按这个顺序排列的,因为这很有意义,如果你理解这个顺序为什么有意义,你甚至不必记住这四个规则,但可以根据你的理解推断它们(就像我刚刚做过)。所以让我们检查一下这个顺序:
【讨论】:
virtual析构函数。
:)(感谢您修正语法!)
首先是派生的,然后是基础。非虚拟案例没有区别。
补充说明。当你有继承和虚方法时,你必须将析构函数声明为虚函数,否则在删除时你可能会有未定义的行为。
例如,假设 Derived 是从 Base 派生的,并且您使用以下行分配 Derived:
Base *o = new Derived();
delete(o);
如果这种情况发生在您的代码中,并且 Base 没有虚拟析构函数,则结果行为是未定义的。通常,只会调用 Base 的析构函数。不会调用 Derived 的析构函数,因为您正在对 Base 指针调用 delete。但是,程序可能会崩溃。一旦你处于未定义行为的领域,所有的赌注都没有了,你正在运行的代码注定要失败。为了防止混乱,Base 析构函数必须是虚拟的。
【讨论】:
Base 没有虚拟析构函数,则行为未定义。可能会或可能不会调用 Base 或 Derived 析构函数,否则程序可能会崩溃。
销毁顺序是倒过来的构造顺序。我最近制作了一个小工具来显示任何层次结构的构造顺序。看这里:
在图中,编号较小的节点首先被构造,最后被破坏。
【讨论】:
假设您已正确地将析构函数声明为虚拟。
然后破坏以完全相反的构造顺序进行。
A) 从最派生的类开始。
B) 递归地重复以下操作。
1) 执行析构代码。
2)执行每个成员的析构函数(按创建的相反顺序)
3) 执行父类的析构函数。 (如果有多个以相反的创建顺序)
如果您使用虚拟继承,那么情况会略有不同,因为基类构造的顺序与正常情况不同。 但是破坏的顺序是总是构造顺序的倒序。
【讨论】:
虚拟函数对销毁的顺序没有影响,而虚拟基类则相反。
没有虚拟基类,派生类总是在其基类之前被销毁;这是它们构造的相反顺序。
对于最派生类,首先构造虚拟基类,在其他基类之前和最派生类本身之前。破坏以相反的顺序发生。这意味着一个虚拟基可能在一个虚拟派生自它的类之后被销毁,如果该类不是被销毁的最派生类。直接基类永远不会发生这种情况。
【讨论】:
自下而上的破坏顺序。 (从派生到基础)
简答:正好相反 构造函数顺序。
长答案:假设“最 派生”类是 D,意思是 最初的实际对象 created 属于 D 类,而那个 D 继承乘法(非虚拟地) 从 B1 和 B2。子对象 对应于最派生类 D 首先运行,然后是 dtors 它的非虚拟基类在 反向声明顺序。就这样 析构函数顺序为 D、B2、B1。 该规则是递归应用的;为了 例如,如果 B1 继承自 B1a 并且 B1b 和 B2 继承自 B2a 和 B2b, 最终顺序为 D、B2、B2b、B2a、 B1,B1b,B1a。
【讨论】:
与构造函数相反。所以先派生出来。
【讨论】: