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

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

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

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

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

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

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


当前回答

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.

其他回答

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

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

另一个实现

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

        return *instance_;
    }

private:
    Singleton() {};

    static Singleton *instance_;
};

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

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

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

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

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

下面是实现线程安全的单例模式的更好方法,在析构函数本身释放内存。但我认为析构函数应该是可选的,因为单例实例将在程序终止时自动销毁:

#include<iostream>
#include<mutex>

using namespace std;
std::mutex mtx;

class MySingleton{
private:
    static MySingleton * singletonInstance;
    MySingleton();
    ~MySingleton();
public:
    static MySingleton* GetInstance();
    MySingleton(const MySingleton&) = delete;
    const MySingleton& operator=(const MySingleton&) = delete;
    MySingleton(MySingleton&& other) noexcept = delete;
    MySingleton& operator=(MySingleton&& other) noexcept = delete;
};

MySingleton* MySingleton::singletonInstance = nullptr;
MySingleton::MySingleton(){ };
MySingleton::~MySingleton(){
    delete singletonInstance;
};

MySingleton* MySingleton::GetInstance(){
    if (singletonInstance == NULL){
        std::lock_guard<std::mutex> lock(mtx);
        if (singletonInstance == NULL)
            singletonInstance = new MySingleton();
    }
    return singletonInstance;
}

关于我们需要使用单例类的情况可以是- 如果我们想在整个程序执行过程中维护实例的状态 如果我们参与写入应用程序的执行日志,其中只需要使用文件的一个实例....等等。 这将是值得赞赏的,如果有人可以建议在我上面的代码优化。