我通常以这种方式实现单例模式:
class Singleton
{
public:
virtual ~Singleton() {}
static Singleton& GetInstance()
{
static Singleton instance;
return instance;
}
private:
Singleton();
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
}
最近,我遇到了这个实现,略有不同:
class Singleton
{
public:
Singleton();
virtual ~Singleton() {}
static Singleton& GetInstance()
{
return instance;
}
private:
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
static Singleton instance;
}
Singleton Singleton::instance;
哪种实施更好?
不将构造函数设为私有(第二次实现)不是很危险吗?
感谢。
答案 0 :(得分:4)
我不需要重复关于在其他答案中制作单身人士的懒惰构造的好处。
让我补充一点:
public: Singleton(); virtual ~Singleton() {}
这个特殊班级的设计师觉得有必要允许:
Singleton
类,称派生类名为DerSingleton
DerSingleton
可以使用指向Singleton
的指针删除实例(因此DerSingleton
不是单身人士)根据定义,DerSingleton
的任何实例也都是Singleton
个实例,因此如果DerSingleton
实例化,则Singleton
不是单身。
所以这个设计断言两件事:
答案 1 :(得分:3)
有区别。在第一种情况下,instance
在函数的第一次调用时初始化。在第二种情况下,它在程序启动时初始化。
如果您制作public
构造函数 - 它不是singleton
,因为它可以由任何人创建
答案 2 :(得分:3)
如果在初始化另一个命名空间级别变量或类静态成员期间尝试使用单例,则会出现主要的行为差异。在第一种情况下,因为在第一次函数调用期间按需创建实际对象,所以将很好地定义构造期间的行为。在第二种情况下,所有投注都是关闭的,因为来自不同翻译单元的静态对象的初始化的相对顺序是不确定的。
另请注意,虽然第一个在构建期间是安全的,但它可能不是在销毁期间。也就是说,如果具有静态存储持续时间的对象在构造期间不使用单例,则可以在单例实例之前初始化它。破坏的顺序与构造的顺序相反,在这种特殊情况下,单体将在另一个对象之前被销毁。如果该对象在其析构函数中使用单例,则会导致未定义的行为。
答案 3 :(得分:1)
第二个实施是错误。默认构造函数应该是私有的。事实上,它本身不是单身人士。除此之外,在@Andrew和@Brady答案中提到了实现之间的差异。
答案 4 :(得分:0)
两者之间的一个重要区别是第二个示例中的实例创建是线程安全的。
你是完全正确的,构造函数应该是私有的。