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

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

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

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

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

我对本文的希望是,我们可以在一个地方(而不是谷歌和搜索多个站点)收集何时(以及如何)正确使用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;
        }
};

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


当前回答

Alexandrescu的现代c++设计有一个线程安全的、可继承的泛型单例。

对于我的2p价值,我认为为你的单例对象定义生命周期是很重要的(当绝对有必要使用它们时)。我通常不让静态的get()函数实例化任何东西,把设置和销毁留给主应用程序的某个专用部分。这有助于突出单例程序之间的依赖关系——但是,正如上面强调的,如果可能的话最好避免它们。

其他回答

正如其他人所注意到的,单例的主要缺点包括无法扩展它们,以及失去实例化多个实例的能力,例如用于测试目的。

单例对象的一些有用的方面:

延迟或预先实例化 适用于需要设置和/或状态的对象

但是,您不必使用单例来获得这些好处。您可以编写一个普通的对象来完成这项工作,然后让人们通过工厂(一个单独的对象)访问它。如果需要,工厂可以只考虑实例化一个,并重用它等等。同样,如果你对一个接口而不是一个具体的类编程,工厂可以使用策略,也就是说,你可以切换到和退出接口的各种实现。

最后,工厂可以使用依赖注入技术,如Spring等。

当初始化和object时有很多代码正在运行时,单例非常方便。例如,当你使用iBatis设置一个持久化对象时,它必须读取所有的配置,解析映射,确保它是正确的,等等。在开始编写代码之前。

如果每次都这样做,性能会大大降低。在单例中使用它,你只执行一次命中,然后所有后续调用都不必执行它。

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

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

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

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

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

单身让你有能力在一个职业中结合两种坏特性。这在任何方面都是错误的。

单例可以给你:

对对象的全局访问,以及 这种类型只能创建一个对象的保证

第一点很简单。全局变量通常不好。除非真的需要,否则永远不要让对象具有全局可访问性。

第二点可能听起来很有道理,但让我们想想。上次你**不小心*创建了一个新对象而不是引用一个现有对象是什么时候?因为它被标记为c++,所以让我们使用该语言中的一个示例。你经常不小心写东西吗

std::ostream os;
os << "hello world\n";

当你打算写信的时候

std::cout << "hello world\n";

当然不是。我们不需要防范这种错误,因为这种错误根本不会发生。如果确实如此,正确的反应是回家睡12-20个小时,希望你能感觉好点。

如果只需要一个对象,只需创建一个实例。如果一个对象应该是全局可访问的,则将其设置为全局。但这并不意味着不可能创建它的其他实例。

“只有一个实例是可能的”约束并不能真正保护我们免受可能的错误。但这确实使我们的代码很难重构和维护。因为我们经常会发现我们需要不止一个实例。我们确实有不止一个数据库,不止一个配置对象,我们确实需要几个记录器。举一个常见的例子,我们的单元测试可能希望能够在每次测试中创建和重新创建这些对象。

So a singleton should be used if and only if, we need both the traits it offers: If we need global access (which is rare, because globals are generally discouraged) and we need to prevent anyone from ever creating more than one instance of a class (which sounds to me like a design issue). The only reason I can see for this is if creating two instances would corrupt our application state - probably because the class contains a number of static members or similar silliness. In which case the obvious answer is to fix that class. It shouldn't depend on being the only instance.

如果你需要全局访问一个对象,把它设置为全局的,比如std::cout。但是不要限制可以创建的实例的数量。

如果您确实需要将一个类的实例数量限制为一个,并且无法安全地处理创建第二个实例,那么就强制执行。但不要让它在全球范围内都可以使用。

如果你确实需要这两个特性,那么1)让它成为单例,2)让我知道你需要它做什么,因为我很难想象这样的情况。

独生子女的真正缺点是他们打破了继承。你不能派生一个新的类来提供扩展的功能,除非你能访问引用单例的代码。因此,除了Singleton将使您的代码紧密耦合之外(可通过策略模式修复…)又名依赖注入),它也会阻止你关闭部分代码的修订(共享库)。

因此,即使日志记录器或线程池的示例也是无效的,应该由策略替换。