C++不同的单例实现



我通常通过以下方式实现单例模式:

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

最新更新