破坏具有静态存储持续时间的对象

ava*_*kar 15 c++

考虑以下程序.

struct s { ~s(); };
void f()
{
    static s a;
}

struct t { ~t() { f(); } };
int main()
{
    static s b;
    static t c;
}
Run Code Online (Sandbox Code Playgroud)

我试图找出关于静态对象破坏的标准保证是什么,但我发现C++ 03 [basic.start.term]的文本相当不足.

是否定义了程序的行为?如果是这样,静态对象的破坏顺序是什么a,b并且c?如果会发生什么s::~s引发了异常?请解释您的推理,最好使用标准中的引号.

Mar*_*ork 8

以下Objects是指静态存储持续时间的对象.

全局命名空间中的对象在main之前创建.

根据定义创建同一编译单元中的对象.
订单在不同的编译单元中未定义.

命名空间中的对象是在访问该命名空间中的任何函数/变量之前创建的.这可能是也可能不是在主要之前.

函数中的对象在首次使用时创建.

对象以创建的相反顺序销毁.请注意,创建顺序由其CONSTRUCTOR的完成定义(而不是在调用时).因此,在其构造函数中创建另一个'y'的一个对象'x'将导致首先构造'y'.

如果它们没有被创建,那么它们将不会被销毁.

是否定义了程序的行为?

所以是的,订单定义明确

b:   Created
c:   Created
c:   Destroyed Start
a:   Created (during destruction of C)
c:   Destroyed End
a:   Destroyed  (a was created after b -> destroyed before b)
b:   Destroyed
Run Code Online (Sandbox Code Playgroud)

修改代码以查看:

#include <iostream>

struct s
{
    int mx;
    s(int x): mx(x) {std::cout <<  "S(" << mx << ")\n";}
    ~s()            {std::cout << "~S(" << mx << ")\n";}
};
void f()
{
    static s a(3);
}

struct t
{
    int mx;
    t(int x): mx(x) { std::cout << "T(" << mx << ")\n";}
    ~t()
    {                 std::cout << "START ~T(" << mx << ")\n";
                      f();
                      std::cout << "END   ~T(" << mx << ")\n";
    }
};
int main()
{
    static s b(1);
    static t c(2);
}
Run Code Online (Sandbox Code Playgroud)

输出是:

$ ./a.exe
S(1)
T(2)
Start ~T(2)
S(3)
END   ~T(2)
~S(3)
~S(1)
Run Code Online (Sandbox Code Playgroud)


Joh*_*itb 5

如前所述,析构函数调用的顺序与构造函数(3.6.3/1)的完成顺序完全相反.换句话说,(3.8/1),静态存储持续时间的对象的生命周期的停止与静态存储持续时间的对象的生命周期的开始相反.所以这一切都归结为他们的构造函数被调用.假设这Printer是一个在以下示例中在其构造函数中输出内容的类型.

命名空间范围

在任何情况下,在第一次使用在(3.6.2/3)中定义对象的相同转换单元中定义的任何函数或变量之前,创建命名空间作用域(全局和用户定义)的对象.这个延迟初始化(在调用main之后)必须遵守该对象定义的相同转换单元中关于其他对象定义的定义顺序.(3.6.2/1).

翻译单位1:

void f() { }

extern Printer a;
Printer b("b");
Printer a("a");
extern Printer c;
Run Code Online (Sandbox Code Playgroud)

翻译单位2:

Printer c("c");
void f();
Run Code Online (Sandbox Code Playgroud)

如果我们使用f,这不一定会强制创建c,因为f未在定义的转换单元中c定义.a之后创建.b因为它是后来定义的.

阻止范围

当控件首先通过其定义或首次为POD(6.7/4)输入块时,会创建块作用域(局部静态)的对象.如果创建无法成功(如果发生异常)(6.7/4),控制权将在下一次传递时再次尝试启动生命周期.

void g() { static Print p("P"); }
struct A {
  A() { 
    static int n; 
    if(n++ == 0) throw "X"; 
    cout << "A"; 
    g(); 
  } 
};
void f() {
  try { static A a; } catch(char const *x) { cout << x; }
}
Run Code Online (Sandbox Code Playgroud)

此代码段输出"XAP".

班级范围

对于静态数据成员,相同的规则适用于初始化顺序,根据它们在同一翻译单元(3.6.2/1)中的定义顺序.这是因为它的规则被公式化为"在命名空间范围中定义的对象......"而不是"命名空间范围的对象......".在C++ 03中,延迟初始化(延迟构造直到从其转换单元使用变量/函数)仅允许用于命名空间范围的对象,这不是预期的.C++ 0x也允许静态数据成员("具有静态存储持续时间的非本地变量").


因此,通过采用上述规则并考虑到销毁订单实际上是由构造函数的完成而不是通过它们的开始来确定的,我们将得到订单~c ~a ~b.

如果s::~s抛出异常,C++ 0x表示terminate()会调用它,如果它抛出异常,你最终会c破坏并终止生命周期a而不完成它的析构函数.我在C++ 03标准中找不到任何指定的内容.它似乎只指定对于非局部静态而不是像C++ 0x那样的块范围静态.