编辑: 从另一个问题,我提供了一个答案,有很多关于单例的问题/答案的链接:

所以我读了单身人士的帖子:好的设计还是拐杖? 争论仍在激烈进行。

我认为单例是一种设计模式(有好有坏)。 单例的问题不在于模式,而在于用户(对不起大家)。每个人和他们的父亲都认为他们可以正确地实施一个(从我所做的许多采访来看,大多数人都不能)。另外,因为每个人都认为他们可以实现正确的单例,所以他们滥用模式并在不合适的情况下使用它(用单例替换全局变量!)

所以需要回答的主要问题是:

什么时候应该使用单例 如何正确地实现单例

我对本文的希望是,我们可以在一个地方(而不是谷歌和搜索多个站点)收集何时(以及如何)正确使用Singleton的权威来源。同样合适的是列出反用法和常见的坏实现,解释为什么它们不能工作,以及对于好的实现来说它们的缺点。

所以开始行动吧: 我会举起我的手,说这是我用的,但可能有问题。 我喜欢“Scott Myers”在他的书《Effective c++》中对这个主题的处理。

使用单例的好情况(不多): 日志框架 线程回收池

/*
 * C++ Singleton
 * Limitation: Single Threaded Design
 * See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
 *      For problems associated with locking in multi threaded applications
 *
 * Limitation:
 * If you use this Singleton (A) within a destructor of another Singleton (B)
 * This Singleton (A) must be fully constructed before the constructor of (B)
 * is called.
 */
class MySingleton
{
    private:
        // Private Constructor
        MySingleton();
        // Stop the compiler generating methods of copy the object
        MySingleton(MySingleton const& copy);            // Not Implemented
        MySingleton& operator=(MySingleton const& copy); // Not Implemented

    public:
        static MySingleton& getInstance()
        {
            // The only instance
            // Guaranteed to be lazy initialized
            // Guaranteed that it will be destroyed correctly
            static MySingleton instance;
            return instance;
        }
};

好的。让我们把一些批评和其他实现放在一起。 : -)


当前回答

单例对象的问题不在于它们的实现。而是它们合并了两个不同的概念,这两个概念显然都不可取。

1)单例提供了对对象的全局访问机制。尽管在没有定义良好的初始化顺序的语言中,它们可能更线程安全或更可靠,但这种用法在道义上仍然相当于全局变量。它是一个用一些笨拙的语法修饰的全局变量(比如说,foo::get_instance()而不是g_foo),但它具有完全相同的目的(在整个程序中可访问的单个对象),并且具有完全相同的缺点。

2)单例防止一个类的多个实例化。据我所知,这种特性很少被添加到类中。这通常是一个更有语境的东西;很多被认为是独一无二的东西实际上只是碰巧是独一无二的。在我看来,更合适的解决方案是只创建一个实例——直到您意识到需要多个实例为止。

其他回答

第一个例子不是线程安全的——如果两个线程同时调用getInstance,这个静态将是一个PITA。某种形式的互斥会有所帮助。

单例基本上让你在语言中拥有复杂的全局状态,否则就很难或不可能拥有复杂的全局变量。

Java特别使用单例变量作为全局变量的替代品,因为所有内容都必须包含在类中。最接近全局变量的是公共静态变量,它们可以像导入静态的全局变量一样使用

c++确实有全局变量,但是调用全局类变量的构造函数的顺序未定义。因此,单例可以让您推迟全局变量的创建,直到第一次需要该变量。

Python和Ruby等语言很少使用单例,因为您可以在模块中使用全局变量。

那么什么时候使用单例对象是好的/坏的呢?差不多就是什么时候使用全局变量是好是坏。

另一个实现

class Singleton
{
public:
    static Singleton& Instance()
    {
        // lazy initialize
        if (instance_ == NULL) instance_ = new Singleton();

        return *instance_;
    }

private:
    Singleton() {};

    static Singleton *instance_;
};

关于模式有一点:不要一概而论。当他们有用的时候,当他们失败的时候,他们有所有的情况。

当您必须测试代码时,单例可能会令人讨厌。您通常只能使用类的一个实例,并且可以选择在构造函数中打开一扇门或使用一些方法来重置状态等等。

另一个问题是,Singleton实际上只不过是一个伪装的全局变量。当你的程序有太多的全局共享状态时,事情往往会倒退,我们都知道这一点。

这可能会使依赖追踪更加困难。当所有事情都依赖于你的单例时,就很难改变它,拆分为两个等等。你通常会被它困住。这也妨碍了灵活性。研究一些依赖注入框架来缓解这个问题。

如何正确地实现单例

有一个问题我从未见人提起过,是我在之前的工作中遇到的。我们有在dll之间共享的c++单例,而通常的确保类的单个实例的机制就不起作用了。问题是每个DLL都有自己的一组静态变量,还有EXE。如果你的get_instance函数是内联的或者是静态库的一部分,那么每个DLL都会有它自己的“单例”副本。

解决方案是确保单例代码只定义在一个DLL或EXE中,或者创建一个具有这些属性的单例管理器来分配实例。