当我查找有关C ++单例模式的信息时,我总能找到这样的例子:
class Singleton
{
public:
~Singleton() {
}
static Singleton* getInstance()
{
if(instance == NULL) {
instance = new Singleton();
}
return instance;
}
protected:
Singleton() {
}
private:
static Singleton* instance;
};
Singleton* Singleton::instance = NULL;
但是这种单身人士似乎也有效:
class Singleton
{
public:
~Singleton() {
}
static Singleton* getInstance()
{
return &instance;
}
protected:
Singleton() {
}
private:
static Singleton instance;
};
Singleton Singleton::instance;
我猜第二个单例是在程序开头实例化的,与第一个不同,但它是唯一的区别吗?
为什么我们主要找到第一个?
答案 0 :(得分:3)
http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14
静态初始化顺序fiasco是一个非常微妙和常见的 误解了C ++的一个方面。不幸的是,它很难被发现 - 错误经常发生在main()开始之前。
简而言之,假设你有两个静态对象x和y 单独的源文件,比如x.cpp和y.cpp。进一步假设 y对象的初始化(通常是y对象的构造函数) 在x对象上调用一些方法。
就是这样。就这么简单。
悲剧在于你有50%-50%的死亡几率。如果 x.cpp的编译单元首先被初始化,一切都是 好。但是如果y.cpp的编译单元首先被初始化, 然后y的初始化将在x的初始化之前运行,并且 你敬酒。例如,y的构造函数可以在x上调用方法 对象,但x对象尚未构建。
您列出的第一种方法可以完全避免此问题。它被称为“首次使用习语的构造”
这种方法的缺点是对象永远不会被破坏。 有另一种技术可以解决这个问题,但需要这样做 小心使用,因为它创造了另一个的可能性(同样 讨厌的问题。
注意:在某些情况下,静态初始化顺序fiasco也可以 适用于内置/内在类型。
答案 1 :(得分:2)
第一个允许您删除实例,而第二个则不允许。但请注意,您的第一个示例不是线程安全的
答案 2 :(得分:0)
通常称为static
initialization order fiasco。总之,文件范围内的静态实例不一定在显式函数调用之前初始化,如第一个示例中那样创建一个。
答案 3 :(得分:0)
单身模式通常被认为是不良实践,因此在这种情况下,经验证据(你“看得最多”)几乎没有价值。
第一个版本使用动态分配,而第二个版本使用静态分配。也就是说,第二个分配不会失败,而第一个分配可能会抛出异常。
这两个版本都有利有弊,但通常你应该尝试一种不需要单身的不同设计。
答案 4 :(得分:0)
第一个也是“懒惰” - 只有在需要时才会创建它。如果您的Singleton
价格昂贵,这可能就是您想要的。
如果您的Singleton
便宜和,您可以处理未定义的静态初始化顺序(并且您不会在main()之前使用它),那么您也可以选择第二个解决方案。