C++ 迈耶斯对单例的实现如何实际上是单例

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17712001/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:22:36  来源:igfitidea点击:

How is Meyers' implementation of a Singleton actually a Singleton

c++design-patternsc++11singleton

提问by lbrendanl

I have been reading a lot about Singletons, when they should and shouldn't be used, and how to implement them safely. I am writing in C++11, and have come across the Meyer's lazy initialized implementation of a singleton, as seen in this question.

我已经阅读了很多关于单身人士,何时应该和不应该使用它们,以及如何安全地实现它们。我正在用 C++11 编写,并且遇到了 Meyer 对单例的惰性初始化实现,如本问题所示。

This implementation is:

这个实现是:

static Singleton& instance()
{
     static Singleton s;
     return s;
}

I understand how this is thread safe from other questions here on SO, but what I don't understand is how this is actually a singleton pattern. I have implemented singletons in other languages, and these always end up something like this example from Wikipedia:

我从 SO 上的其他问题中了解这如何是线程安全的,但我不明白这实际上是一个单例模式。我已经用其他语言实现了单例,这些总是像维基百科的这个例子一样结束:

public class SingletonDemo {
        private static volatile SingletonDemo instance = null;

        private SingletonDemo() {       }

        public static SingletonDemo getInstance() {
                if (instance == null) {
                        synchronized (SingletonDemo .class){
                                if (instance == null) {
                                        instance = new SingletonDemo ();
                                }
                      }
                }
                return instance;
        }
}

When I look at this second example, it is very intuitive how this is a singleton, since the class holds a reference to one instance of itself, and only ever returns that instance. However, in the first example, I don't understand how this prevents there ever existing two instances of the object. So my questions are:

当我查看第二个示例时,非常直观地看出这是一个单例,因为该类持有对自身的一个实例的引用,并且只返回该实例。但是,在第一个示例中,我不明白这如何防止对象的两个实例存在。所以我的问题是:

  1. How does the first implementation enforce a singleton pattern? I assume it has to do with the static keyword, but I am hoping that someone can explain to me in depth what is happening under the hood.
  2. Between these two implementation styles, is one preferable over the other? What are the pros and cons?
  1. 第一个实现如何强制执行单例模式?我认为它与 static 关键字有关,但我希望有人可以向我深入解释幕后发生的事情。
  2. 在这两种实现风格之间,一种比另一种更可取吗?优缺点都有什么?

Thanks for any help,

谢谢你的帮助,

回答by Sean Middleditch

This is a singleton because staticstorage duration for a function local means that only one instance of that local exists in the program.

这是一个单例,因为static函数 local 的存储持续时间意味着程序中只存在该 local 的一个实例。

Under the hood, this can very roughly be considered to be equivalent to the following C++98 (and might even be implemented vaguely like this by a compiler):

在幕后,这可以粗略地被认为等同于以下 C++98(甚至可能被编译器像这样模糊地实现):

static bool __guard = false;
static char __storage[sizeof(Singleton)]; // also align it

Singleton& Instance() {
  if (!__guard ) {
    __guard = true;
    new (__storage) Singleton();
  }
  return *reinterpret_cast<Singleton*>(__storage);
}

// called automatically when the process exits
void __destruct() {
  if (__guard)
    reinterpret_cast<Singleton*>(__storage)->~Singleton();
}

The thread safety bits make it get a bit more complicated, but it's essentially the same thing.

线程安全位使它变得有点复杂,但本质上是一样的。

Looking at an actual implementation for C++11, there is a guard variablefor each static (like the boolean above), which is also used for barriers and threads. Look at Clang's AMD64 output for:

查看 C++11 的实际实现,每个静态(如上面的布尔值)都有一个保护变量,它也用于屏障和线程。查看 Clang 的 AMD64 输出:

Singleton& instance() {
   static Singleton instance;
   return instance;
}

The AMD64 assembly for instancefrom Ubuntu's Clang 3.0 on AMD64 at -O1 (courtesy of http://gcc.godbolt.org/is:

instance来自 Ubuntu's Clang 3.0 on AMD64 at -O1的 AMD64 程序集(由http://gcc.godbolt.org/ 提供)是:

instance():                           # @instance()
  pushq %rbp
  movq  %rsp, %rbp
  movb  guard variable for instance()::instance(%rip), %al
  testb %al, %al
  jne   .LBB0_3
  movl  guard variable for instance()::instance, %edi
  callq __cxa_guard_acquire
  testl %eax, %eax
  je    .LBB0_3
  movl  instance()::instance, %edi
  callq Singleton::Singleton()
  movl  guard variable for instance()::instance, %edi
  callq __cxa_guard_release
.LBB0_3:
  movl  instance()::instance, %eax
  popq  %rbp
  ret

You can see that it references a global guard to see if initialization is required, uses __cxa_guard_acquire, tests the initialization again, and so on. Exactly in almost every way like version you posted from Wikipedia, except using AMD64 assembly and the symbols/layout specified in the Itanium ABI.

您可以看到它引用了一个全局守卫来查看是否需要初始化、使用__cxa_guard_acquire、再次测试初始化​​等等。除了使用 AMD64 程序集和Itanium ABI 中指定的符号/布局之外,几乎在所有方面都与您从 Wikipedia 发布的版本类似。

Note that if you run that test you should give Singletona non-trivial constructor so it's not a POD, otherwise the optimizer will realize that there's no point to doing all that guard/locking work.

请注意,如果您运行该测试,您应该提供Singleton一个非平凡的构造函数,因此它不是 POD,否则优化器将意识到执行所有这些保护/锁定工作毫无意义。

回答by 4pie0

// Singleton.hpp
class Singleton {
public:
    static Singleton& Instance() {
        static Singleton S;
        return S;
    }

private:
    Singleton();
    ~Singleton();
};

This implementation is known as Meyers' Singleton. Scott Meyers says:

这种实现被称为 Meyers 的 Singleton。斯科特·迈耶斯 说:

"This approach is founded on C++'s guarantee that local static objects are initialized when the object's definition is first encountered during a call to that function." ... "As a bonus, if you never call a function emulating a non-local static object, you never incur the cost of constructing and destructing the object."

“这种方法建立在 C++ 的保证之上,即在调用该函数期间首次遇到对象的定义时初始化本地静态对象。” ... “作为奖励,如果您从不调用模拟非局部静态对象的函数,则永远不会产生构造和销毁对象的成本。”

When you call Singleton& s=Singleton::Instance()first time the object is created and every next call to Singleton::Instance()results with the same object being returned. Main issue:

当您Singleton& s=Singleton::Instance()第一次调用 该对象时,每次调用Singleton::Instance()结果都会返回相同的对象。主要问题:



Another implementation is called the trusty leaky Singleton.

另一种实现称为可信泄漏单例。

class Singleton {
public:
    static Singleton& Instance() {
        if (I == nullptr) { I = new Singleton(); }
        return *I;
    }

private:
    Singleton();
    ~Singleton();

    static Singleton* I;
};

// Singleton.cpp
Singleton* Singleton::I = 0;

Two issues:

两个问题:

  • leaks, unless you implement a Releaseand make sure to call it (once)
  • not thread safe
  • 泄漏,除非你实现了一个Release并确保调用它(一次)
  • 不是线程安全的