迈耶斯如何实施单身人士实际上是一个单身人士

lbr*_*anl 43 c++ singleton design-patterns c++11

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

这个实现是:

static Singleton& instance()
{
     static Singleton s;
     return s;
}
Run Code Online (Sandbox Code Playgroud)

我理解这对于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;
        }
}
Run Code Online (Sandbox Code Playgroud)

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

  1. 第一个实现如何强制执行单例模式?我假设它与static关键字有关,但我希望有人能够深入地向我解释发生在幕后的事情.
  2. 在这两种实现方式之间,是否优于另一种?优缺点都有什么?

谢谢你的帮助,

Sea*_*tch 45

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

在引擎盖下,这可以被大致认为等同于以下的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();
}
Run Code Online (Sandbox Code Playgroud)

线程安全位使它变得更复杂,但它基本上是相同的.

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

Singleton& instance() {
   static Singleton instance;
   return instance;
}
Run Code Online (Sandbox Code Playgroud)

在64位AMD64上使用instanceUbuntu的Clang 3.0 的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
Run Code Online (Sandbox Code Playgroud)

您可以看到它引用全局防护,以查看是否需要初始化,使用__cxa_guard_acquire,再次测试初始化​​,等等.除了使用AMD64程序集和Itanium ABI中指定的符号/布局之外,几乎所有方式都与您从维基百科发布的版本完全相同.

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


4pi*_*ie0 16

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

private:
    Singleton();
    ~Singleton();
};
Run Code Online (Sandbox Code Playgroud)

这种实现被称为Meyers的Singleton.Scott Meyers说:

"这种方法建立在C++的保证之上,当在调用该函数时首次遇到对象的定义时,会初始化本地静态对象." ......"作为奖励,如果你从不调用模拟非本地静态对象的函数,你就不会产生构造和破坏对象的成本."

Singleton& s=Singleton::Instance() 第一次调用 时,会创建对象,并且每次调用Singleton::Instance()返回相同对象的结果时.主要问题:


另一种实现称为可信泄漏的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;
Run Code Online (Sandbox Code Playgroud)

两个问题:

  • 泄漏,除非你实现一个Release并确保调用它(一次)
  • 不是线程安全的

  • 对于泄漏的单例,为什么不``static Singleton&Instance(){static Singleton*I = new Singleton(); 返回*我; }`. (7认同)