2017-04-25 38 views
3

如何处理从本地静态对象的构造函数中抛出的异常?例如我已经以下代码:本地静态对象和异常

class A 
{ 
public: 
    A() {throw runtime_error("Ooops");} 
}; 

void foo() 
{ 
    static A a = A(); 
    cout << "Continue" << endl; 
} 

int main(void) 
{ 
    try 
    { 
     foo(); 
    } 
    catch(...) 
    { 
    } 
    foo(); // Prints continue 
    return 0; 
} 

据我所知,在第二呼叫foo方法的情况下,对象a被视为完全构造对象,构造不被调用。 (更多结束,它似乎是由于第一次异常抛出不被称为a的析构函数)

+3

不,您的理解不正确。你可以尝试一下[trivially](https://wandbox.org/permlink/0zqV3BglWpVZSgRM)。 –

+0

如果抛出异常并终止构造函数的执行(未完成构造对象),您如何期望'a'完全构造? – Rogus

+0

'静态A a = A();'你为什么这样做?为什么要复制初始化? –

回答

4

如果这是真的,那么这是一个编译器错误。

(然而,VTT claims that this code produces the expected result with Visual Studio 2015,所以我建议您仔细检查你的结果。)

下面是标准规定的行为:

[C++14: 6.7/4]:所有块的零初始化(8.5)在进行任何其他初始化之前执行具有静态存储持续时间(3.7.1)或线程存储持续时间(3.7.2)的范围变量。在静态存储持续时间(如果适用)下,块范围实体的常量初始化(3.6.2)在第一次输入块之前执行。允许实现在静态或线程存储持续时间在命名空间范围(3.6.2)中允许实现静态初始化变量的相同条件下,使用静态或线程存储持续时间对其他块范围变量进行早期初始化。否则,这种变量会在控件第一次通过声明时被初始化;这种变量在其初始化完成时被认为是初始化的。 如果通过抛出异常退出初始化,则初始化未完成,因此下次控制进入声明时将再次尝试初始化。如果控制器在变量初始化时同时进入声明,则并发执行应等待初始化完成。如果控制在初始化变量时递归地重新输入声明,则行为是未定义的。 [..]

GCC 6.3.0 correctly attempts to reconstruct the A(从而再次引发)。


更多了,这似乎是一个由于第一异常抛出的析构函数不叫

没有,也不会。你不能销毁一开始就没有成功创建的对象。

[C++14: 15.2/2]:任何存储持续时间,其初始化或破坏是由一个异常终止将已析构函数对于所有其完全构造的子对象(不包括联合状类的变体成员)的执行的一个目的,即,用于主构造函数(12.6.2)已经完成并且析构函数尚未开始执行的子对象。同样,如果对象的非委托构造函数已完成执行,并且该对象的委托构造函数以异常退出,则会调用该对象的析构函数。如果在新表达式中分配了对象,则会调用匹配释放函数(3.7.4.2,5.3.4,12.5)(如果有),以释放对象占用的存储空间。


顺便说一句,这并不解决问题,但你应该这样写:

static A a; 

的拷贝初始化从临时是没有意义的。

+1

6.7/4“如果通过抛出异常退出初始化,则初始化不完整,因此下一次控制进入声明时将再次尝试初始化。” – aschepler

+0

@aschepler:谢谢 –

+0

@BoundaryImposition,附加/分离调试器中程序的行为不同(我的意思是msvc 2015)。在分离的调试程序失败的情况下 – LmTinyToon

2

静态局部变量的每个实例还隐式地创建了一个全局布尔变量,在构造静态变量后,该变量将被设置为true。如果构造函数抛出的时间比下次调用方法时还多,则会有另一个构造静态变量的尝试。

+0

是的,但OP显示它不是。 –

+0

嗯,它看起来像他甚至没有运行他的代码(它实际上有一个未捕获的异常),请参见[工作示例](http://ideone.com/4HZSTO),它也可以在VS2015中正常工作 – VTT

+0

有趣!我没有VS2015确认。 –