class Singleton {
public:
static Singleton *getInstance() {
return &singleton;
}
private:
Singleton();
~Singleton();
private:
static Singleton singleton;
};
有人说,这会导致一些讨厌的错误。但是这会导致什么错误?不使用指针可以很好地避免双重检查锁定模式。
这样的错误
别的什么.cpp
class SomethingSingleton {
public:
SomethingSingleton() {
auto* singleton = Singleton::getInstance();
singleton->whatever();
}
~SomethingSingleton() {
auto* singleton = Singleton::getInstance();
singleton->whatever();
}
};
static SomethingSingleton something;
C++ 没有指定两个翻译单元的构造顺序(在这种情况下,大致相当于不同的实现文件,一个与你的单例,一个与我的
(现在,如果运行时决定在我的之前销毁你的单例(即在我的之后构造它(,那么我的单例将尝试在其析构函数和构造函数中使用你的单例。 然后是未定义的行为。
现在,如果你没有在翻译单元中将其设置为静态全局,并且使用了函数范围的静态(可以使用 DCLP(,那么C++运行时将在我之前构造你的单例,在我的之后销毁你的单例,这是一个很好的执行🙂顺序
另请注意,在某些实现中,如果您不与 -pthread
链接,编译器可以自由地不为 DCLP 输入代码,因此您无论如何都可能无法获得您担心的成本。
另请注意,真正导致线程代码速度变慢的是争用,如果您没有争用,那么互斥体的大多数实现都使用所谓的 futex,它允许没有争用情况以尽可能避免系统调用。 因此,如果您对此有所思虑,那么这也应该不是什么大问题。
但是,如果您仍然担心静态局部是一个问题,那么无论您使用什么解决方案,都要小心,并确保它不会严重爆炸。 基本上我想说的是,相信编译器
但是,如果您仍然想避免函数范围的静态,请使用这样的代码
static Something& get() {
if (!something_instance) {
something_instance = std::unique_ptr<Something>(new Something{});
}
return *something_instance;
}
请注意,由于这个有趣的原因,我在这里没有使用std::make_unique
。
另请注意,如果您像这样全局构造unique_ptr
std::unique_ptr<Singleton> Singleton::singleton_instance{};
它是静态初始化的一部分,而不是动态初始化的一部分。 因此不容易发生 UB。 它保证在任何单例(使用 singleton_instance
ptr(被构造之前发生。
也看看静态初始化惨败