迈耶斯对单例的实现实际上是单例
How is Meyers' implementation of a Singleton actually a Singleton
我读了很多关于Singleton的文章,什么时候应该使用和不应该使用它们,以及如何安全地实现它们。我是用C++11编写的,遇到了Meyer对singleton的惰性初始化实现,如本问题所示。
这种实现是:
static Singleton& instance()
{
static Singleton s;
return s;
}
我理解这是如何从SO上的其他问题中获得线程安全的,但我不明白的是,这实际上是一个单例模式。我已经在其他语言中实现了singleton,这些语言最终总是像维基百科上的例子一样:
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;
}
}
当我看第二个例子时,这是一个非常直观的单例,因为类持有对其自身一个实例的引用,并且只返回该实例。然而,在第一个例子中,我不明白这是如何防止对象存在两个实例的。所以我的问题是:
- 第一个实现如何强制执行单例模式?我认为这与static关键字有关,但我希望有人能向我深入解释引擎盖下发生的事情
- 在这两种实现风格之间,一种实现风格比另一种更可取吗?利弊是什么
感谢您的帮助,
这是一个单例,因为函数local的static
存储持续时间意味着程序中只存在该local的一个实例。
在引擎盖下,这可以被粗略地认为相当于下面的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();
}
线程安全位使它变得有点复杂,但本质上是一样的。
看看C++11的实际实现,每个静态(如上面的布尔值)都有一个保护变量,它也用于屏障和线程。查看Clang的AMD64输出:
Singleton& instance() {
static Singleton instance;
return instance;
}
用于instance
的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
你可以看到,它引用了一个全局保护来查看是否需要初始化,使用__cxa_guard_acquire
,再次测试初始化,等等。除了使用AMD64程序集和安腾ABI中指定的符号/布局外,几乎所有方面都与你从维基百科发布的版本完全一样。
请注意,如果您运行该测试,您应该为Singleton
提供一个非平凡的构造函数,这样它就不是POD,否则优化器将意识到做所有的保护/锁定工作是没有意义的。
// Singleton.hpp
class Singleton {
public:
static Singleton& Instance() {
static Singleton S;
return S;
}
private:
Singleton();
~Singleton();
};
这种实现被称为Meyers的Singleton。Scott Meyers说:
"这种方法建立在C++保证本地静态对象在第一次遇到对象的定义时初始化在调用该函数期间。"…"作为奖励,如果你从不打电话给函数模拟非本地静态对象,您永远不会产生成本构造和破坏物体。"
当你打电话Singleton& s=Singleton::Instance()
第一次创建对象时,每次对Singleton::Instance()
的调用都会返回相同的对象。主要问题:
- 受Fiasco销毁令(相当于Fiasco初始化令)约束
另一种实现被称为可靠泄漏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;
两个问题:
- 泄漏,除非您实现Release并确保调用它(一次)
- 线程不安全
- C++ 实现模板单例类时出现链接错误
- C++中的单例实现在调用 getInstance 函数时不会产生相同的类实例
- 使用 std::call_once 实现类似单例的功能
- 使用 CRTP 实现单例
- 这是MSVC 2013中具有共享PTR的单例的正确实现吗?
- C++ 单例实现 迈耶与call_once
- 函数内具有静态变量的单例类(迈耶实现)
- 为什么此单例实现使用私人类(C )
- 为什么我的单例实现两次启动?(一个进程,多个线程)
- 使用指针和使用静态对象实现单例实现之间的区别
- C++不同的单例实现
- 为什么这个单例实现无法编译?
- 单例实现和对 ::GetInstance 的未定义引用
- 这个单例实现有问题吗
- 以下是单例实现线程安全的
- 为什么在单例实现中清除boost::scopedptr
- 有人能确认一下这是否是一个线程安全的单例实现吗?
- 单例实现有什么问题?
- C++单例实现
- 单例实现问题