当函数中静态变量的构造函数异常终止时会发生什么

What happen when the constructor of a static variable in a function terminates unusually?

本文关键字:什么 异常终止 构造函数 函数 静态 变量      更新时间:2023-10-16

我有一个函数可以简化为:

void f() {
    static MyObject o("hello");
    DoSomethingWith(o);
}

这个函数是跨 C API 边界调用的,所以像个好孩子一样,我使用 try 来捕获在它们越过边界之前抛出的任何异常并搞砸事情:

void f() {
    try {
        static MyObject o("hello");
        DoSomethingWith(o);
    } catch (const MyObjectException& e) {
        Message("Constructor of o failed");
    }
}

第一次调用此函数,我收到消息"Constructor of o failed".但是,稍后,再次调用该函数,我再次收到消息。我收到消息的次数与调用f一样多。我正在使用Visual C++所以这告诉我MSVC++做什么,而不是应该做什么。

我的问题是,当static函数变量的构造函数异常终止时会发生什么(通过throw ing,构造函数的longjmp,它所在的线程的终止等(?另外,在它之前和之后声明的任何其他static变量应该会发生什么?我也希望从标准中获得任何相关报价。

C++11标准第6.7([stmt.dcl](节

指出

在进行任何其他初始化之前,将执行具有静态存储持续时间 (3.7.1( 或线程存储持续时间 (3.7.2( 的所有块范围变量的零初始化 (8.5(。具有静态存储持续时间的块范围实体的常量初始化 (3.6.2((如果适用(在首次输入其块之前执行。允许实现使用静态或 线程存储持续时间与允许实现在命名空间范围 (3.6.2( 中使用静态或线程存储持续时间静态初始化变量的条件相同。 否则,此类变量在控件第一次通过其声明时初始化;此类变量在其初始化完成后被视为已初始化。 如果初始化通过引发异常退出,则初始化 不完整,因此下次控件进入声明时将再次尝试。 如果在初始化变量时控件并发进入声明,则并发执行应等待初始化完成。 如果控件在变量正在时递归地重新输入声明 已初始化,行为未定义。

问:当静态函数变量的构造函数异常终止时会发生什么 [...] ?

答:§6.7 [stmt.dcl] p4 [...]否则,此类变量在控件第一次通过其声明时初始化;此类变量在其初始化完成后被视为已初始化。如果初始化通过引发异常退出,则初始化未完成,因此下次控件进入声明时将再次尝试初始化。

因此,如果o通过引发异常退出,则将再次尝试初始化。我认为这同样适用于任何类型的异常退出初始化,尽管没有明确说明。Brb,寻找更多报价。

由于我找不到任何相关内容,因此我打开了一个后续问题。

问:另外,在它之前和之后声明的任何其他静态变量应该怎么做?

答:没有,只要线程或整个程序都不终止。

§3.6.3 [basic.start.term]

具有

静态存储持续时间的初始化对象(即生存期 (3.8( 已开始的对象(的析构函数 (12.4( 由于从main返回和调用std::exit (18.5( 而被调用。给定线程中具有线程存储持续时间的初始化对象的析构函数由于从该线程的初始函数返回以及该线程调用std::exit 的结果而被调用。在该线程中具有线程存储持续时间的所有初始化对象的析构函数的完成是在启动具有静态存储持续时间的任何对象的析构函数之前排序的。

§3.7.2 [basic.stc.thread]

具有线程存储持续时间的变量应在首次使用 (3.2( 之前初始化,如果构造,则应在线程退出时销毁。

重新设计

你的程序。将尝试初始化静态变量,直到初始化成功。如果这种模式不合适,你应该找到一种更好的方式来表达你的目标。也许是在受控环境中填充的静态unique_ptr?如果你有一个无法可靠地构造的资源,你必须将构造降级到可以处理错误的其他上下文中,或者让你的函数只选择性地依赖于资源(例如,通过空指针(。