gpt4 book ai didi

c++ - Meyers 的单例实现实际上是如何实现单例的

转载 作者:IT老高 更新时间:2023-10-28 14:01:18 24 4
gpt4 key购买 nike

我已经阅读了很多关于单例的文章,它们应该在什么时候使用,什么时候不应该使用,以及如何安全地实现它们。我正在用 C++11 编写,并且遇到了 Meyer 的单例延迟初始化实现,如 this question. 中所示。

这个实现是:

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

我了解这对于 SO 上的其他问题来说是线程安全的,但我不明白这实际上是一个单例模式。我已经用其他语言实现了单例,这些总是像 Wikipedia 中的这个例子一样。 :

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;
}
}

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

  1. 第一个实现如何强制执行单例模式?我认为它与 static 关键字有关,但我希望有人可以深入向我解释幕后发生的事情。
  2. 在这两种实现方式中,哪一种更可取?有什么优缺点?

感谢您的帮助,

最佳答案

这是一个单例,因为 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();
}

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

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

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

instance 的 AMD64 程序集来自 Ubuntu 的 Clang 3.0 on AMD64 at -O1(由 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

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

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

关于c++ - Meyers 的单例实现实际上是如何实现单例的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17712001/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com