使用虚拟的破坏顺序
Posted
技术标签:
【中文标题】使用虚拟的破坏顺序【英文标题】:order of destruction using virtual 【发布时间】:2011-03-31 05:50:18 【问题描述】:当我使用虚函数时,有人可以帮忙看看破坏的顺序是什么。是从基类开始,然后是派生类吗?
【问题讨论】:
【参考方案1】:第 12.6.2/5 节:
初始化应按以下顺序进行:
首先,并且仅适用于最派生类的构造函数 如下所述,虚拟基类应在 它们出现在深度优先从左到右的遍历中的顺序 基类的有向无环图,其中“从左到右”是 派生类中基类名称的出现顺序 基本说明符列表。 然后,应初始化直接基类 按照它们出现在基本说明符列表中的声明顺序 (不管 mem-initializers 的顺序如何)。 然后,非静态 数据成员应按照它们被声明的顺序进行初始化 类定义(同样不管 内存初始化器)。 — 最后,构造函数的主体被执行。[注意:声明顺序的强制要求是确保基础和 成员子对象以相反的顺序被销毁 初始化。 ]
【讨论】:
【参考方案2】:由于我看不到虚函数如何改变任何对象的销毁顺序,我假设您指的是虚拟继承中基类和数据成员的销毁顺序 场景。
子对象是构造的
-
基类构造从最基到最派生;
多个基类按作为基类的声明顺序构造;
虚拟基类是在所有其他之前构建的,它们之间遵守上述两条规则;
数据成员是在执行封闭对象的构造函数主体之前构造的,按照它们的声明顺序。
Destruction与construction正好相反,所以你只需要记住以上内容即可。
但是,上面的四个规则是按这个顺序排列的,因为这很有意义,如果你理解这个顺序为什么有意义,你甚至不必记住这四个规则,但可以根据你的理解推断它们(就像我刚刚做过)。所以让我们检查一下这个顺序:
您可能希望使用基类从派生类的构造函数中提供的任何服务。当然,在实际构造之前,您不能使用(基)类对象。因此,在构造派生类时,需要已经构造了基类。 (顺便说一句,这也解释了为什么虚函数调度不能完全在构造函数中工作:当一个子对象被构造时,只有基类的子对象已经被构造;派生类的子对象还没有被构造构造。因此,对虚函数的调用不能分派给派生类。与往常一样,析构函数是相同的,只是向后。) 由于多个基类是相同的同级,因此必须任意选择某些顺序。最终,声明的顺序是最简单的一种。数据成员也是相同的兄弟,遵循相同的(或多或少任意的)声明顺序规则。 虚拟基类是奇怪的野兽。因为总是只有一个虚拟基类的子对象,所以有一条特殊的规则,它总是需要首先构造它,从最派生类的构造函数开始。 (这就是为什么虚拟基类最适合作为没有数据且只有默认构造函数的抽象基类。)【讨论】:
其实我觉得他说的是virtual
析构函数。
@Matthieu:看来我是对的。 :)
(感谢您修正语法!)【参考方案3】:
首先是派生的,然后是基础。非虚拟案例没有区别。
补充说明。当你有继承和虚方法时,你必须将析构函数声明为虚函数,否则在删除时你可能会有未定义的行为。
例如,假设 Derived 是从 Base 派生的,并且您使用以下行分配 Derived:
Base *o = new Derived();
delete(o);
如果这种情况发生在您的代码中,并且 Base 没有虚拟析构函数,则结果行为是未定义的。通常,只会调用 Base 的析构函数。不会调用 Derived 的析构函数,因为您正在对 Base 指针调用 delete。但是,程序可能会崩溃。一旦你处于未定义行为的领域,所有的赌注都没有了,你正在运行的代码注定要失败。为了防止混乱,Base 析构函数必须是虚拟的。
【讨论】:
不完全准确。如果Base
没有虚拟析构函数,则行为未定义。可能会或可能不会调用 Base
或 Derived
析构函数,否则程序可能会崩溃。【参考方案4】:
销毁顺序是倒过来的构造顺序。我最近制作了一个小工具来显示任何层次结构的构造顺序。看这里:
Where is the "virtual" keyword necessary in a complex multiple inheritance hierarchy?在图中,编号较小的节点首先被构造,最后被破坏。
【讨论】:
【参考方案5】:假设您已正确地将析构函数声明为虚拟。
然后破坏以完全相反的构造顺序进行。
一般来说是:
A) 从最派生的类开始。 B) 递归地重复以下操作。
1) 执行析构代码。 2)执行每个成员的析构函数(按创建的相反顺序) 3) 执行父类的析构函数。 (如果有多个以相反的创建顺序)
如果您使用虚拟继承,那么情况会略有不同,因为基类构造的顺序与正常情况不同。 但是破坏的顺序是总是构造顺序的倒序。
【讨论】:
【参考方案6】:虚拟函数对销毁的顺序没有影响,而虚拟基类则相反。
没有虚拟基类,派生类总是在其基类之前被销毁;这是它们构造的相反顺序。
对于最派生类,首先构造虚拟基类,在其他基类之前和最派生类本身之前。破坏以相反的顺序发生。这意味着一个虚拟基可能在一个虚拟派生自它的类之后被销毁,如果该类不是被销毁的最派生类。直接基类永远不会发生这种情况。
【讨论】:
那么在健全的环境中避免不纯的 MI?明白了:) @Merlyn Morgan-Graham:“不纯的 MI”是什么意思? 我所说的不纯,是指任何需要虚拟继承的东西。任何非纯粹抽象的基类的多重继承。我认为必须在基类上使用“virtual”关键字预先计划多重继承已经够讨厌了。找出析构函数/构造函数的顺序会让事情变得更糟:)【参考方案7】:自下而上的破坏顺序。 (从派生到基础)
简答:正好相反 构造函数顺序。
长答案:假设“最 派生”类是 D,意思是 最初的实际对象 created 属于 D 类,而那个 D 继承乘法(非虚拟地) 从 B1 和 B2。子对象 对应于最派生类 D 首先运行,然后是 dtors 它的非虚拟基类在 反向声明顺序。就这样 析构函数顺序为 D、B2、B1。 该规则是递归应用的;为了 例如,如果 B1 继承自 B1a 并且 B1b 和 B2 继承自 B2a 和 B2b, 最终顺序为 D、B2、B2b、B2a、 B1,B1b,B1a。
见c++ faq section 25
【讨论】:
【参考方案8】:与构造函数相反。所以先派生出来。
【讨论】:
以上是关于使用虚拟的破坏顺序的主要内容,如果未能解决你的问题,请参考以下文章