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

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

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

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

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

我对本文的希望是,我们可以在一个地方(而不是谷歌和搜索多个站点)收集何时(以及如何)正确使用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()函数实例化任何东西,把设置和销毁留给主应用程序的某个专用部分。这有助于突出单例程序之间的依赖关系——但是,正如上面强调的,如果可能的话最好避免它们。

其他回答

当我有一个封装大量内存的类时,我发现它们很有用。例如,在我最近开发的一款游戏中,我有一个影响映射类,它包含一个非常大的连续内存数组集合。我想在启动时全部分配,在关闭时全部释放,我肯定只想要它的一个副本。我还必须从很多地方访问它。我发现单例模式在这种情况下非常有用。

我相信还有其他的解决方案,但我觉得这个非常有用,而且很容易实现。

Because a singleton only allows one instance to be created it effectively controls instance replication. for example you'd not need multiple instances of a lookup - a morse lookup map for example, thus wrapping it in a singleton class is apt. And just because you have a single instance of the class does not mean you are also limited on the number of references to that instance. You can queue calls(to avoid threading issues) to the instance and effect changes necessary. Yes, the general form of a singleton is a globally public one, you can certainly modify the design to create a more access restricted singleton. I haven't tired this before but I sure know it is possible. And to all those who commented saying the singleton pattern is utterly evil you should know this: yes it is evil if you do not use it properly or within it confines of effective functionality and predictable behavior: do not GENERALIZE.

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

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

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

在桌面应用程序中(我知道,只有我们这些恐龙才会写这些!),它们对于获得相对不变的全局应用程序设置是必不可少的——用户语言,帮助文件的路径,用户首选项等等,否则就必须传播到每个类和每个对话框中。

编辑-当然这些应该是只读的!

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

单例可以给你:

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

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

第二点可能听起来很有道理,但让我们想想。上次你**不小心*创建了一个新对象而不是引用一个现有对象是什么时候?因为它被标记为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)让我知道你需要它做什么,因为我很难想象这样的情况。