关于C ++中的两种单例模式

时间:2011-09-06 22:44:17

标签: c++ singleton

当我查找有关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;

我猜第二个单例是在程序开头实例化的,与第一个不同,但它是唯一的区别吗?

为什么我们主要找到第一个?

5 个答案:

答案 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()之前使用它),那么您也可以选择第二个解决方案。