我有一个测试情况下我有3子对象类(A,B和C),以及第二子对象B施工期间抛出异常.据我了解C++,编译器应该回退大类的构造并销毁第一个对象A,而不是第二个(B)或第三个(C)对象.
我看到的是,如果我使用第一个对象的"类内初始化" A,那么第三个对象将被A销毁,而不是第一个对象C被破坏.当然,破坏尚未构建的对象是非常糟糕的!例如,如果C是a std:unique_ptr<T>,它可能会在尝试释放垃圾指针时发出分段违规信号.
如果我使用旧学校"成员初始化",那么这个问题就不会发生.
用gcc 4.8我没看到这个
这是代码.该类D公开了这个bug.该类E应该具有相同的功能,但它不会暴露该错误.
#include <iostream>
using namespace std;
struct A {
A(const string& x) : x_(x) { cout << "A::A()" << (void*)this <<endl; }
~A() { cout << "A::~A() " << (void*)this<< endl;}
string x_;
};
struct B {
B(const A& a) { cout << "B::B()" << endl; throw "dead"; }
~B() { cout << "B::~B()" << endl;}
};
struct C {
C() { cout << "C::C()" << endl; }
~C() { cout << "C::~C()" << endl;}
};
struct D {
A a{"foo"}; // "new school In-class initialization"
B b{a};
C c;
D() { cout <<"D::D()" << endl; }
~D() { cout <<"D::~D()" << endl; }
};
struct E {
A a;
B b;
C c;
E()
:a{"foo"} // "old school member initialization"
,b(a)
{ cout <<"E::E()" << endl; }
~E() { cout <<"E::~E()" << endl; }
};
int main()
{
try {
D d;
}
catch(...)
{
cout << "got exception" << endl;
}
try {
E e;
}
catch(...)
{
cout << "got exception" << endl;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是输出.我希望看到A构造,B部分构造然后抛出,然后A被摧毁,但这不是我看到的D情况.
$ icpc -std=c++11 test.cpp
$ ./a.out
A::A()0x7fffe0a5ee90
B::B()
C::~C()
got exception
A::A()0x7fffe0a5eea0
B::B()
A::~A() 0x7fffe0a5eea0
got exception
Run Code Online (Sandbox Code Playgroud)
- 更新 -
描述应该发生什么的标准部分是15.2.3
对于任何存储持续时间的类类型的对象,其初始化或销毁由异常终止,对每个对象的完全构造的子对象调用析构函数,即对于主构造函数(12.6.2)具有的每个子对象.完成执行并且析构函数尚未开始执行,除了在破坏的情况下,类型联合类的变体成员不会被销毁.子对象以完成构造的相反顺序销毁.在进入构造函数或析构函数的函数try-block的处理程序(如果有的话)之前,对这种破坏进行排序.
这绝对是一个编译器错误,并且您已经使用标准中的正确参考回答了自己的问题:[ except.ctor] / 3,并强调:
\n\n\n\n\n对于任何存储持续时间的类类型的对象,其初始化或销毁因异常而终止,将为每个对象\xe2\x80\x99s完全构造的子对象调用析构函数,即为每个\n 子对象调用析构函数其中主构造函数 (12.6.2) 已完成执行,而析构函数尚未开始执行,但在销毁的情况下,类联合类的变体成员不会被销毁。子对象按照其构造完成的相反顺序被销毁。在进入构造函数或析构函数(如果有)的函数 try 块的处理程序之前,会对此类销毁进行排序。
\n
在哪里:
\n\n\n\n\n主构造函数是在对象构造过程中调用的第一个构造函数(即,不是该对象构造的目标构造函数)。
\n
C还没有完全构造 - 它的主要构造函数甚至还没有被调用 - 所以它的析构函数不应该被调用。