Kad*_*mir 1 c++ singleton c++11
我最近读过Andrei Alexandrescu的Modern C++ Design.在阅读了6.章后,我开始担心我们公司的单身人士.由于我们经验丰富的团队负责人编写核心助手库,如单身人士等.我问他处理单身人员的方式是否能解决死亡参考问题?如果他使用了由C核心语言给出的at_exit函数调用?
他告诉我C++ 11具有单例支持,并将连续执行CTOR和DTOR,它们不会是任何死引用问题.用户不必处理同步.
即使它听起来很棒我找不到任何在互联网上证实他的信息.那么请告诉我C++ 11是否为单身人士处理死亡参考问题,如果是这样,请解释一下后面的黑暗魔法是什么?
据推测,您的团队负责人正在谈论实施如下的单身人士:
T &get_value() {
static T val;
return val;
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,标准给出了两个保证.第一个是val对象将被构造一次,这是程序执行流第一次通过本地静态变量的声明,即使它在多个线程上同时发生6.7/4:
允许实现在静态或线程存储持续时间内执行其他块范围变量的早期初始化,条件是允许实现在命名空间范围内静态初始化具有静态或线程存储持续时间的变量(3.6.2).否则,在第一次控制通过其声明时初始化这样的变量; 这样的变量在初始化完成后被认为是初始化的.如果通过抛出异常退出初始化,则初始化未完成,因此下次控制进入声明时将再次尝试初始化.如果控制在初始化变量时同时进入声明,则并发执行应等待初始化完成.
静态初始化仅在常量的情况下允许,因此只要T没有constexpr构造函数,您就不必担心(但如果有一些与您的相关的边缘情况,请阅读3.6.2的完整规则)码).
第二个保证是所有具有静态存储持续时间的变量将按其构造的相反顺序进行破坏3.6.3/1:
具有静态存储持续时间的初始化对象(即,其生命周期(3.8)已开始的对象)的析构函数(12.4)被调用为从main返回并且由于调用std :: exit(18.5)而返回的结果.具有给定线程内的线程存储持续时间的初始化对象的析构函数被调用作为从该线程的初始函数返回的结果,并且作为该线程调用std :: exit的结果.在具有静态存储持续时间的任何对象的析构函数的启动之前,对具有该线程内的线程存储持续时间的所有初始化对象的析构函数的完成进行排序.如果构造函数的完成或具有线程存储持续时间的对象的动态初始化在另一个之前按顺序排序,则在第一个的析构函数的启动之前对第二个的析构函数的完成进行排序.如果具有静态存储持续时间的对象的构造函数或动态初始化的完成先于另一个对象的顺序排序,则在第一个的析构函数的启动之前对第二个的析构函数的完成进行排序.[注意:此定义允许同时销毁. - 结束注释]如果对象是静态初始化的,则对象的破坏顺序与对象动态初始化的顺序相同.对于数组或类类型的对象,在销毁子对象构造期间初始化静态存储持续时间的任何块范围对象之前,将销毁该对象的所有子对象.如果具有静态或线程存储持续时间的对象的销毁通过异常退出,则调用std :: terminate(15.5.1).
虽然这个段落在构造是并发的时候为静态对象的并发销毁提供了很多空间,但是从中取出它的主要原因是破坏以与构造相反的顺序发生.
这些意思一起意味着如果T val依赖于U val这些单例函数中的另一个,U val则总是会在之前构造T val并在之后被破坏T val,所以总体而言,这是实现单例的安全方式(除非你做的事情非常疯狂).