本地静态对象和异常

时间:2017-04-25 11:15:26

标签: c++ visual-studio-2015

我应该如何处理从本地静态对象的构造函数抛出的异常?例如,我有以下代码:

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的析构函数,因为第一次异常投掷没有被调用)

2 个答案:

答案 0 :(得分:5)

如果这是真的,那就是编译错误。

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

这是标准规定的行为:

  

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

GCC 6.3.0 correctly attempts to reconstruct the A(因此再次抛出)。

  

更重要的是,似乎没有调用第一个异常抛出的析构函数

不,它不会。您无法破坏从未成功创建过的对象。

  

[C++14: 15.2/2]:任何存储持续时间的对象,其初始化或销毁由异常终止,将为其所有完全构造的子对象(不包括类似联合的类的变体成员)执行析构函数,即对于主要构造函数(12.6.2)已完成执行且析构函数尚未开始执行的子对象。类似地,如果对象的非委托构造函数已完成执行并且该对象的委托构造函数以异常退出,则将调用该对象的析构函数。如果该对象是在中分配的new-expression ,调用匹配的释放函数(3.7.4.2,5.3.4,12.5),以释放对象占用的存储空间。

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

static A a;

临时复制初始化毫无意义。

答案 1 :(得分:2)

静态局部变量的每个实例也隐式创建一个全局布尔变量,在构造静态变量后将设置为true。如果构造函数抛出而不是下次调用方法,那么将会有另一种构造静态变量的尝试。