当构造函数抛出异常时,哪些析构函数会被运行?

92

在C++中,如果构造函数抛出异常,那么运行哪些析构函数?

特别地,在初始化列表或函数体中抛出异常是否有任何区别?

此外,继承和成员变量又如何处理?假设所有已完成的构造都会被析构,如果只构造了一些成员变量,则只有这些成员变量会被析构吗?如果是多重继承,所有已完成的构造函数都会被析构吗?虚拟继承是否有所不同?


2
你为什么不自己测试一下呢? - Steve Wellens
44
@Steve:如果你想要可移植性或可维护性的代码,那么依赖于特定编译器版本的行为是一个糟糕的想法。 - Ben Voigt
@BenVoigt - 理论上我同意。但是在现实世界中编程时,你必须面对现实。如果所有主要的编译器都没有做到这一点,我会非常惊讶。 - Steve Wellens
3个回答

82

如果构造函数抛出异常,哪些析构函数会运行?

在该作用域中完全创建的所有对象的析构函数。

如果异常发生在初始化列表或主体期间,是否有区别?

所有已完成的对象都将被销毁。
如果构造函数从未完全调用,则对象从未构建,因此无法销毁。

继承和成员变量呢?可以想象只有完成的构建才会被销毁。如果只有一些成员被构造,那么只有这些成员被销毁吗?如果有多重继承,所有已完成的构造函数是否都被销毁?虚拟继承是否会改变情况?

所有已完成的构建都会被销毁。是的,只有完全创建的对象才会被销毁。

好文章:

Constructor Failures by Herb Sutter

特别是他解释的部分:

从生物学角度来看,受孕发生了——构造函数开始了——但尽管做了最大的努力,却随之发生了流产——构造函数从未运行到结束(termination)。

顺便说一下,这就是为什么如果构造函数没有成功,析构函数永远不会被调用——因为没有东西可以销毁。“它不能死亡,因为它从未活过。”请注意,这使得“其构造函数抛出异常的对象”这个短语真正成为了一个自相矛盾的词。这样的事物甚至还不如一个前对象......它从来没有生存过,从未存在过,从未呼吸过第一口气。


“逻辑作用域”是什么意思? - Armen Tsirunyan
1
您可能还想阅读@Als在同一主题上的早期回答 - Jerry Coffin
一个注意点:不受限制的联合成员... - C.M.
1
这个“完全创建”的区分非常有趣:https://godbolt.org/z/815hPohTz 因为从一个构造函数调用另一个构造函数会使对象本身“完全创建”。 - Post Self

24
在C++中,如果构造函数抛出异常,会运行哪些析构函数?
所有已经完成构造的对象。
特别地,初始化列表或函数体中是否发生异常不影响结果吗?
不影响。在异常抛出之前已完全构造的成员将运行其析构函数。在构造过程中抛出异常的成员和所有未构造的成员将< strong>不会运行它们的析构函数。成员的构造顺序是明确定义的,因此您知道在异常抛出点时会发生什么。
那么继承和成员呢?
同样适用。
显然,所有已完成的构造都会被销毁。
是的
如果只构造了一些成员,只有这些成员被销毁吗?
是的
如果有多重继承,所有已完成的构造函数是否都被销毁?
是的
虚继承会改变什么吗?
不会。
但请注意:虚继承确实会影响调用构造函数的顺序。如果您不熟悉如何定义顺序,请查阅确切规则,否则可能不直观。

6

任何在局部作用域中因构造函数而创建的对象都将被销毁。运行时处理程序沿着调用栈向上回溯,调用析构函数,直到找到处理程序。

如果异常是从构造函数中抛出的,则将调用所有完全构造的子对象的析构函数。此外,如果构造函数是new表达式的一部分,则如果存在相应的放置删除运算符,将调用该运算符。


+1 是用于描述 new 表达式的重要性。 - Spacen Jasset

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