假设我有以下代码
#pragma once
class Something {
public:
static Something& get();
private:
Something();
};
#include "Something.hpp"
#include <iostream>
using namespace std;
Something& Something::get() {
static Something something;
return something;
}
Something::Something() {
cout << "Something()" << endl;
}
#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与标准相矛盾
答案 0 :(得分:4)
静态(和全局非静态)对象的构造和销毁顺序在C ++规范中明确定义,单个translation unit !
如果您有多个翻译单元(多个源文件),则未定义TU之间的构造/销毁顺序。
因此,您显示的代码可能具有未定义的行为。
答案 1 :(得分:4)
[stmt.dcl]¶4
具有静态存储持续时间或线程存储持续时间的块范围变量的动态初始化是在控件第一次通过其声明时执行的。
[basic.start.term]¶1
如果具有静态存储持续时间的对象的构造函数或动态初始化的完成先于另一个对象的顺序排序,则在第一个析构函数的启动之前对第二个析构函数的析构函数的完成进行排序。
<强>¶2强>
如果函数包含已销毁的静态或线程存储持续时间的块范围对象,并且在销毁具有静态或线程存储持续时间的对象期间调用该函数,则该程序具有未定义的行为如果控制流通过先前销毁的块范围对象的定义。
在SomethingElse
的析构函数中,我们冒险调用此未定义的行为:
SomethingElse::~SomethingElse() {
Something::get();
}
如果存在具有静态存储持续时间的SomethingElse
实例,则有四种可能性:
Something
的单个实例是在SomethingElse
之前构建的。它的破坏将在SomethingElse
之后发生,因此行为定义明确。
Something
的单个实例是在SomethingElse
之后构建的。它的破坏将发生在SomethingElse
之前,因此行为未定义如上所述。
Something
的单个实例是在不同的线程中构建的,没有与SomethingElse
的构造同步。破坏可能同时发生,因此行为未定义。
尚未构建Something
的实例(即这是对Something::get
的第一次调用)。在这种情况下,程序要求在Something
之后构建SomethingElse
,这意味着Something
的销毁必须在SomethingElse
之前发生,但是自销毁以来SomethingElse
已经开始,这是一个矛盾,行为是不明确的。 (从技术上讲,“之前排序”关系中有一个循环。)