C++ C++;俏皮的反成语;为什么?
我最近遇到了一个问题。我的理解是,这用于在标准库(如cout、cerr等)中实现globals。由于专家选择了它,我认为这是一种非常强大的技术 我试图理解使用更像Meyer Singleton的东西有什么好处 例如,可以在头文件中包含:C++ C++;俏皮的反成语;为什么?,c++,c++11,singleton,static-initialization,C++,C++11,Singleton,Static Initialization,我最近遇到了一个问题。我的理解是,这用于在标准库(如cout、cerr等)中实现globals。由于专家选择了它,我认为这是一种非常强大的技术 我试图理解使用更像Meyer Singleton的东西有什么好处 例如,可以在头文件中包含: inline Stream& getStream() { static Stream s; return s; } static Stream& stream = getStream(); 优点是您不必担心引用计数、新的位置或有两个类,即代码更
inline Stream& getStream() { static Stream s; return s; }
static Stream& stream = getStream();
优点是您不必担心引用计数、新的位置或有两个类,即代码更简单。既然不是这样做的,我相信这是有原因的:
编辑:在阅读雅克的答案时,我被提示编写以下代码,我将其作为一个快速演示添加到原始问题中。这是一个非常简单的示例,说明了使用Meyer Singleton+全局引用如何在main:.之前进行初始化。静态本地/Meyer的Singleton+静态全局引用(您的解决方案)几乎等同于漂亮的计数器 区别如下:
静态蒸汽&
存在于每个编译单元中;所引用的对象不存在。因为在当前版本的C++中,没有办法检测到这一点,就好像它消失了一样。但有些实现可能会创建该引用,而不是忽略它静态流&
之前调用getStream()
;这将导致销毁顺序困难(流的销毁时间比预期晚)。这可以通过违反规则来避免内联getStream
线程中创建静态流
本地。检测到这种情况不会发生对编译器来说是一个挑战,因此解决方案中可能存在一些冗余的线程安全开销。nifty计数器不明确支持线程安全;这被认为是安全的,因为它在静态初始化时运行,在预期线程之前
getStream()
。只有当它被证明不能做任何事情时,它才能被优化,这是很困难的。俏皮的计数器也有类似的成本,但是操作可能更简单,也可能不更容易优化运行时成本。(确定这一点需要在各种编译器上检查生成的程序集输出)getStream()
,这(如上所述)通常应该被禁止避免一个以上的单例有时很重要。关于俏皮计数器(又称Schwartz计数器)的实用性/性能的所有问题基本上都由Maxim Egorushkin在回答中回答(但也请参见评论线程) 主要问题是,双方正在进行权衡。当您使用Nifty计数器时,您的程序启动时间会慢一些(在大型项目中),因为所有这些计数器都必须在发生任何事情之前运行。这在梅尔的单身汉身上是不会发生的 但是,在Meyer的单例中,每次您想要访问全局对象时,都必须检查它是否为null,或者,编译器发出代码,在尝试任何访问之前检查静态变量是否已构造。在这个漂亮的计数器中,您已经有了指针,您只需发射出去,因为您可以假设init发生在启动时
因此,Nifty Counter与Meyer的singleton基本上是程序启动时间和运行时间之间的折衷。使用这里的解决方案,全局
流
变量在静态初始化过程中的某个点被赋值,但在初始化时未指定。因此,在静态初始化期间使用来自其他编译单元的流
,可能不起作用。Nifty计数器是一种确保全局(例如std::cout)即使在静态初始化期间也可用的方法
#include <iostream>
struct use_std_out_in_ctor
{
use_std_out_in_ctor()
{
// std::cout guaranteed to be initialized even if this
// ctor runs during static initialization
std::cout << "Hello world" << std::endl;
}
};
use_std_out_in_ctor global; // causes ctor to run during static initialization
int main()
{
std::cout << "Did it print Hello world?" << std::endl;
}
#包括
结构使用\u标准\u输出\u输入\u
{
使用_std_out _in_ctor()
{
//std::即使是这样,也不能保证初始化
//ctor在静态初始化期间运行
std::cout总结答案和评论:
让我们比较一个库的3个不同选项,希望将全局单例表示为变量或通过getter函数:
选项1-允许使用全局变量,即:
- 确保一次创建
- 作为
// libA .h
struct A {
A();
};
A& getA();
// some other header
A global_a2 = getA();
// main
int main() {
std::cerr << "main\n";
}
// libA .cpp - need to be dynamically linked! (same as libstdc++ is...)
// thus the below shall be created only once in the process
A& getA() {
static A a;
return a;
}
A::A() { std::cerr << "construct A\n"; }