我通常通过以下方式实现单例模式:
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;
哪种实施方式更好?
不将构造函数私有化(第二次实现)不是很危险吗?
谢谢。
这是有区别的。在第一种情况下,instance
在函数的第一次调用时被初始化。在第二种情况下,它是在程序启动时初始化的。
如果你制作了一个public
构造函数-它不是singleton
,因为它可以由任何创建
我不需要重复其他答案中关于singleton的惰性构造的优点。
让我补充一下:
public: Singleton(); virtual ~Singleton() {}
这个特殊类的设计者觉得有必要允许:
- 从这个
Singleton
类派生,比如派生的类称为DerSingleton
DerSingleton
可以具有可以通过指向Singleton
的指针删除的实例(因此DerSingleton
不是单例)
根据定义,DerSingleton
的任何实例也是Singleton
实例,因此,如果DerSingleton
被实例化,则Singleton
不是单例。
因此,这个设计断言了两件事:
- 这个类是一个单例
- 这个类是而不是单例
如果在初始化另一个命名空间级变量或类静态成员期间尝试使用singleton,那么行为上的主要差异就会出现。在第一种情况下,由于实际对象是在第一次函数调用期间按需创建的,因此构造期间的行为将得到很好的定义。在第二种情况下,由于来自不同转换单元的静态对象的初始化的相对顺序是未定义的,因此所有赌注都是无效的。
还要注意的是,虽然第一个在构建期间是安全的,但在销毁期间可能不是安全的。也就是说,如果具有静态存储持续时间的对象在构造过程中不使用singleton,则可以在singleton实例之前对其进行初始化。破坏的顺序与构造的顺序相反,在这种特殊情况下,singleton将在其他对象之前被破坏。如果该对象在其析构函数中使用singleton,则会导致未定义的行为。
第二个实现是错误的。默认构造函数应该是私有的。事实上,它本身并不是一个单例。除此之外,@Andrew和@Brady的回答中提到了实现之间的差异。
两者之间的一个重要区别是,在第二个示例中创建实例是线程安全的。
不过,你绝对是对的,构造函数应该是私有的。
这里有一个相关的问题:https://stackoverflow.com/a/10479084/1158895