引用可能被破坏的静态对象

时间:2017-01-05 13:45:58

标签: c++ c++11 static-variables

假设我有以下代码

Something.hpp

#pragma once

class Something {
public:
    static Something& get();
private: 
    Something();
};

Something.cpp

#include "Something.hpp"
#include <iostream>
using namespace std;

Something& Something::get() {
    static Something something;
    return something;
}
Something::Something() {
    cout << "Something()" << endl;
}

的main.cpp

#include <iostream>
using namespace std;

struct SomethingElse {
    ~SomethingElse() {
        Something::get();
        cout << "~SomethingElse" << endl; 
    }
};

void func() {
    static SomethingElse something_else;
    // do something with something_else
}

int main() {
    func();
    return 0;
}

是否可以创建Something个对象的多个实例?标准是否说明了序列化静态对象的破坏?

注意我知道跨越不同的翻译单元时文件级静态变量的破坏是未定义的,我想知道在函数作用域静态变量的情况下会发生什么(which have the double-checked locking pattern built into the C++ runtime )对于具有文件级静态变量的相同转换单元情况,编译器很容易根据变量在代码中的布局(静态)来确保构造和销毁的序列化,但是当变量动态延迟创建时会发生什么什么时候调用函数?

注意原始变量怎么样?我们可以期望它们包含它们的值直到程序结束吗?因为它们不需要被销毁。

修改

在cppreference.com(http://en.cppreference.com/w/cpp/utility/program/exit

上找到了这个
  

如果构造函数的完成或线程局部或静态对象A的动态初始化在线程局部或静态对象B之前被排序,则在破坏A开始之前对B的销毁完成进行排序。

如果这是真的,那么序列化每个静态对象的销毁?但我也发现了这个

https://isocpp.org/wiki/faq/ctors#construct-on-first-use-v2与标准相矛盾

2 个答案:

答案 0 :(得分:4)

静态(和全局非静态)对象的构造和销毁顺序在C ++规范中明确定义,单个translation unit

如果您有多个翻译单元(多个源文件),则未定义TU之间的构造/销毁顺序。

因此,您显示的代码可能具有未定义的行为

答案 1 :(得分:4)

  

[stmt.dcl]¶4

     

具有静态存储持续时间或线程存储持续时间的块范围变量的动态初始化是在控件第一次通过其声明时执行的。

     

[basic.start.term]¶1

     

如果具有静态存储持续时间的对象的构造函数或动态初始化的完成先于另一个对象的顺序排序,则在第一个析构函数的启动之前对第二个析构函数的析构函数的完成进行排序。

     

<强>¶2

     

如果函数包含已销毁的静态或线程存储持续时间的块范围对象,并且在销毁具有静态或线程存储持续时间的对象期间调用该函数,则该程序具有未定义的行为如果控制流通过先前销毁的块范围对象的定义。

SomethingElse的析构函数中,我们冒险调用此未定义的行为:

SomethingElse::~SomethingElse() {
    Something::get();
}

如果存在具有静态存储持续时间的SomethingElse实例,则有四种可能性:

  1. Something的单个实例是在SomethingElse之前构建的。它的破坏将在SomethingElse之后发生,因此行为定义明确。

  2. Something的单个实例是在SomethingElse之后构建的。它的破坏将发生在SomethingElse之前,因此行为未定义如上所述。

  3. Something的单个实例是在不同的线程中构建的,没有与SomethingElse的构造同步。破坏可能同时发生,因此行为未定义。

  4. 尚未构建Something的实例(即这是对Something::get的第一次调用)。在这种情况下,程序要求在Something之后构建SomethingElse,这意味着Something的销毁必须在SomethingElse之前发生,但是自销毁以来SomethingElse已经开始,这是一个矛盾,行为是不明确的。 (从技术上讲,“之前排序”关系中有一个循环。)