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

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

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

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

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

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

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


当前回答

如果你是创建单例对象并使用它的人,不要将它设置为单例对象(这没有意义,因为你可以控制对象的奇点而不将其设置为单例对象),但如果你是一个库的开发人员,并且你只想向你的用户提供一个对象,这是有意义的(在这种情况下,你是创建单例对象的人,但你不是用户)。

单例对象所以使用它们作为对象,很多人访问单例直接通过调用方法返回它,但这是有害的因为你使你的代码知道对象是单身,我喜欢用单例对象,我通过构造函数传递它们,使用它们作为普通对象,通过这种方式,你的代码不知道是否这些对象是单例对象,这让依赖关系更加明确,它有助于重构……

其他回答

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

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

我认为这是c#最健壮的版本:

using System;
using System.Collections;
using System.Threading;

namespace DoFactory.GangOfFour.Singleton.RealWorld
{

  // MainApp test application

  class MainApp
  {
    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Same instance?
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 server requests
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // "Singleton"

  class LoadBalancer
  {
    private static LoadBalancer instance;
    private ArrayList servers = new ArrayList();

    private Random random = new Random();

    // Lock synchronization object
    private static object syncLock = new object();

    // Constructor (protected)
    protected LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      // Support multithreaded applications through
      // 'Double checked locking' pattern which (once
      // the instance exists) avoids locking each
      // time the method is invoked
      if (instance == null)
      {
        lock (syncLock)
        {
          if (instance == null)
          {
            instance = new LoadBalancer();
          }
        }
      }

      return instance;
    }

    // Simple, but effective random load balancer

    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

下面是. net优化版:

using System;
using System.Collections;

namespace DoFactory.GangOfFour.Singleton.NETOptimized
{

  // MainApp test application

  class MainApp
  {

    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Confirm these are the same instance
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 requests for a server
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // Singleton

  sealed class LoadBalancer
  {
    // Static members are lazily initialized.
    // .NET guarantees thread safety for static initialization
    private static readonly LoadBalancer instance =
      new LoadBalancer();

    private ArrayList servers = new ArrayList();
    private Random random = new Random();

    // Note: constructor is private.
    private LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      return instance;
    }

    // Simple, but effective load balancer
    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

你可以在dotfactory.com上找到这个模式。

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

大多数人在试图让自己对使用全局变量感觉良好时使用单例变量。有合法的使用,但大多数时候,当人们使用它们时,只能有一个实例的事实,与它是全局可访问的事实相比,只是一个微不足道的事实。

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

#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;
}

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