有效的单身人士课程?

时间:2014-04-01 10:27:45

标签: c++ design-patterns singleton

class Singleton
{
 private:
     static Singleton s;
     Singleton(){}
 public:
    static Singleton *getInstance()
    {
        return &s;
    }
};

Singleton Singleton::s;

这是一个有效的单身人士课程吗?

class Singleton
{
 private:
     static Singleton *m_instance;
     Singleton(){}
 public:
    static Singleton *getInstance()
    {
        return m_instance;
    }

};
Singleton * Singleton::m_instance = new Singleton;

class Singleton
{
 private:
     static Singleton *m_instance;
     Singleton(){}
 public:
    static Singleton *getInstance()
    {
        if(m_instance == NULL)
        {
            lock();
            if(m_instance == NULL)
                m_instance = new Singleton;
            unlock();
        }
        return m_instance;
    }

};
Singleton * Singleton::m_instance = NULL;

上面的三个单例类都是线程安全的,但它们都容易出现“静态初始化顺序惨败”,我是对的吗?

3 个答案:

答案 0 :(得分:6)

  

这是一个有效的单身人士课程吗?

现在,在编辑后答案是肯定的,它是有效的&它也是线程安全的,因为所有非函数范围的静态变量都是在main()之前构造的,而只有一个活动线程。

C ++标准版n3337§3.6.2/1§3.6.2/ 2:非局部变量的初始化

  

有两大类命名的非局部变量:带有的变量   静态存储持续时间(3.7.1)和具有线程存储持续时间的那些   (3.7.2)。具有静态存储持续时间的非局部变量是   由于程序启动而初始化。非本地   具有线程存储持续时间的变量初始化为a   线程执行的结果。在每个启动阶段中,初始化发生如下。

     

具有静态存储持续时间(3.7.1)或线程存储的变量   持续时间(3.7.2)应在任何其他之前进行零初始化(8.5)   初始化发生。执行常量初始化:

     

- 如果每个完整表达式(包括隐式转换)那个   出现在带有静态或线程的引用的初始值设定项中   存储持续时间是一个常量表达式(5.19),参考是   绑定到左值,指定具有静态存储持续时间的对象   或临时(见12.2);

     

- 如果初始化具有静态或线程存储持续时间的对象   通过构造函数调用,如果构造函数是constexpr构造函数,   如果所有构造函数参数都是常量表达式(包括   转换),如果在函数调用替换后(7.1.5),   mem-initializers中的每个构造函数调用和完整表达式   在非静态数据成员的brace-or-equal-initializers中是一个   恒定表达;

     

- 如果具有静态或线程存储持续时间的对象不是   由构造函数调用初始化,如果每个完整表达式   在其初始化器中出现的是一个常量表达式。

     

一起调用零初始化和常量初始化   静态初始化;所有其他初始化都是动态的   初始化。静态初始化应在任何之前执行   动态初始化发生。 (...)

C ++标准版n3337§6.7/ 4:声明声明

  

使用static的所有块范围变量的零初始化(8.5)   存储持续时间(3.7.1)或线程存储持续时间(3.7.2)是   在任何其他初始化发生之前执行。不变   具有静态存储的块范围实体的初始化(3.6.2)   持续时间(如果适用)在其块首次执行之前执行   进入。允许实现尽早执行   使用static或thread初始化其他块作用域变量   在与实现相同的条件下的存储持续时间   允许使用static或thread静态初始化变量   命名空间范围内的存储持续否则这样的变量是   初始化第一次控制通过其声明;   这样的变量在完成后被认为是初始化的   初始化。如果通过抛出异常退出初始化,   初始化未完成,因此下次将再次尝试   时间控制进入声明。 如果控制进入声明   在初始化变量的同时,并发   执行应等待初始化完成*)。(...)

*):

  

实现不得在执行时引入任何死锁   初始化器。

但仍然容易static initialization order fiasco。写getInstance的常用方法是:

Singleton& getInstance()
{
    static Singleton instance;
    return instance;
}

这样可以避免这种初始化问题。

  

这是一个线程安全的单例类吗?

在上面的C ++ 11中,代码是线程安全的。在C ++ 03中,您可以使用

pthread_once


除此之外,你还应该防止复制和分配:

Singleton( Singleton const&);      // Don't Implement
void operator=( Singleton const&); // Don't implement

答案 1 :(得分:3)

据我所知,这是线程安全的。但它易受static initialization order fiasco的影响。

如果某个对象试图在其构造函数中访问Singleton并且该对象是在程序初始化期间构造的,并且此代码位于Singleton之外的另一个编译单元中,则它可能会崩溃,也可能不会崩溃,因为{{1可能已经或者可能尚未初始化(因为未定义编译单元中静态对象的初始化顺序)。这是一个例子:

Singleton::s

答案 2 :(得分:0)

这是懒惰的初始化Singleton,是的。它在C ++ 11下是线程安全的。