引用可能已损坏的静态对象



假设我有以下代码

有点事。pp

#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对象的多个实例?该标准是否说明了序列化静态对象的销毁?

注意我知道在不同的翻译单元中,文件级静态变量的销毁是未定义的,我想知道在函数范围的静态变量(在C++运行时中内置了双重检查锁定模式)的情况下会发生什么,编译器很容易根据变量在代码中的布局(静态)通过构造和销毁来确保序列化,但当调用函数时动态延迟创建变量时会发生什么?

注意基元变量怎么办?我们能指望它们在程序结束前包含它们的值吗?因为它们不需要被摧毁。


编辑

在cppreference.com上找到此(http://en.cppreference.com/w/cpp/utility/program/exit)

如果线程本地或静态对象A的构造函数或动态初始化的完成在线程本地或静止对象B之前排序,则B的销毁完成在的销毁开始之前排序

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

https://isocpp.org/wiki/faq/ctors#construct-与标准相矛盾的首次使用-v2

静态(和全局非静态)对象的构造和销毁顺序在C++规范中定义良好,对于单个翻译单元

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

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

[stmt.dcl]¶4

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

[基本开始术语]¶1

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

¶2

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

正是在SomethingElse的析构函数中,我们才有可能调用这种未定义的行为:

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

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

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

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

  3. CCD_ 10的单个实例是在不同的线程中构造的,而没有相对于CCD_ 11的构造同步。破坏可能同时发生,因此行为是未定义的。

  4. 尚未构造Something的实例(即,这是对Something::get的第一次调用)。在这种情况下,程序要求在SomethingElse之后构造Something,这意味着Something的破坏必须在SomethingElse之前发生,但由于SomethingElse的破坏已经开始,这是一个矛盾,并且行为是未定义的。(从技术上讲,在"先序后序"关系中存在一个循环。)

最新更新