成员初始化列表中临时对象的构造与析构顺序问题
C++成员初始化列表中临时对象的析构时机分析
问题背景与代码
以下是待分析的C++代码:
#include <iostream> class A { public: A() { std::cout << "Default Constructor of A\n"; } ~A() { std::cout << "Destructor of A\n"; } }; class B { public: B() { std::cout << "Default Constructor of B\n"; } ~B() { std::cout << "Destructor of B\n"; } }; class C { A a; B b; public: C() : a(A()), b(B()) { } }; int main() { C c; }
编译输出
使用Apple Clang以-std=c++11 -fno-elide-constructors编译后,程序输出如下:
Default Constructor of A Destructor of A Default Constructor of B Destructor of B Destructor of B Destructor of A
核心疑问
- 根据C++规则,临时对象在包含其创建的完整表达式结束时析构,但成员初始化列表是否属于表达式?此处的完整表达式是什么?
- 是否可能出现以下输出?
Default Constructor of A Default Constructor of B Destructor of B Destructor of A Destructor of B Destructor of A
规则分析与解答
成员初始化列表的完整表达式界定
C++标准明确规定:构造函数成员初始化列表中的每个成员初始化器都是独立的完整表达式。也就是说,a(A())这个初始化器本身就是一个完整表达式——其中A()创建的临时对象,会在这个初始化器执行完毕(即完成对成员a的拷贝构造,因关闭了拷贝省略)后立刻析构;同理,b(B())中的临时B对象,会在b的初始化完成后立刻析构。
输出可能性判断
第二个输出的顺序意味着两个临时对象都等到整个成员初始化列表执行完毕后才析构,这完全不符合C++标准对完整表达式的定义——每个初始化器的临时对象必须在自身初始化完成后就销毁,而非等到所有成员初始化完成。因此,不可能出现第二种输出。
第一个输出的逻辑完全符合规则:
- 执行
a(A()):创建临时A → 用临时A拷贝构造a→ 销毁临时A - 执行
b(B()):创建临时B → 用临时B拷贝构造b→ 销毁临时B - main函数结束,销毁
c对象:按成员声明的逆序,先销毁b→ 再销毁a
内容的提问来源于stack exchange,提问作者Y.T.
相关产品推荐
相关产品推荐

