14 March 2022

C++ 核心指南目录

I.3: Avoid singletons

理由

单例( Singleton )其实只不过是伪装了的复杂的全局对象:

例子

class Singleton {
    // ... lots of stuff to ensure that only one Singleton object is created,
    // that it is initialized properly, etc.
};

另外一个问题是,单例的形式也有很多种……

注意

如果希望全局对象的值不可变,可以把它声明为 constconstexpr

例外

可以用最简单的单例模式。第一次使用的时候,把它初始化。

typedef int X;
X& myX()
{
    static X my_x {3};
    return my_x;
}

int main()
{
    cout << myX() << endl;
    cout << myX();
    return 0;
}
3
3

以上是解决初始化顺序问题的最有效方法之一。在多线程环境下,初始化静态对象不会导致竞争条件。除非是在构造器中访问了共享对象。

局部静态变量的初始会不会导致竞争条件。但是对象的解构如果需要同步进行的话,就要用简单一些的解决办法。比如:

X& myX()
{
    static auto p = new X {3};
    return *p;  // potential leak
}

以上代码返回的是一个指针,就需要在某个地方以恰当的线程安全的方式删除其存储。这样就容易出错,所以尽量避免,除非:

  • myX 是一个多线程代码,
  • X 对象需要释放,
  • X 的解构器代码同步进行。

如果定义了单实例类,该类只创建了一个对象,那么 myX 函数就不是单实例。此种情况,也非本规则的例外。即也要遵循本规则。

强化

  • 找到名字中有 singleton 的类
  • 找到只创建一个对象的类:数对象数,检查构造器
  • 如类有一公开静态函数,函数里本地静态构造了此类的一对象,并作为指针或引用返回此对象。禁之。