最近,我遇到了C++的Singleton设计模式的实现/实现。它看起来是这样的(我采用了现实生活中的例子):

// a lot of methods are omitted here
class Singleton
{
   public:
       static Singleton* getInstance( );
       ~Singleton( );
   private:
       Singleton( );
       static Singleton* instance;
};

从这个声明中,我可以推断实例字段是在堆上启动的。这意味着存在内存分配。对我来说完全不清楚的是,内存将在何时被释放?或者是否存在错误和内存泄漏?似乎在实现方面存在问题。

我的主要问题是,如何以正确的方式实现它?


当前回答

这是关于对象生命周期管理的。假设您的软件中有多个单体。它们依赖于Logger单例。在应用程序销毁期间,假设另一个单例对象使用Logger记录其销毁步骤。您必须保证Logger应该最后清理。因此,请同时查看本文:http://www.cs.wustl.edu/~schmidt/PDF/ObjMan.PDF

其他回答

以下是我对如何实现正确的单线图(以及其他非平凡静态对象)的看法:https://github.com/alex4747-pub/proper_singleton

摘要:

使用静态初始化列表在正确的时间实例化单线程:在进入main之后和启用多线程之前添加一些小的改进,使其对单元测试友好。

这里是一个使用CRTP的可模拟单例。它依赖于一个小助手在任何时候(最多)强制执行一个对象。要在程序执行过程中强制执行单个对象,请删除重置(我们发现这对测试很有用)。

ConcreteSinleton可以这样实现:

class ConcreteSingleton : public Singleton<ConcreteSingleton>
{
public:
  ConcreteSingleton(const Singleton<ConcreteSingleton>::PrivatePass&)
      : Singleton<StandardPaths>::Singleton{pass}
  {}
  
  // ... concrete interface
  int f() const {return 42;}

};

然后与一起使用

ConcreteSingleton::instance().f();

这是关于对象生命周期管理的。假设您的软件中有多个单体。它们依赖于Logger单例。在应用程序销毁期间,假设另一个单例对象使用Logger记录其销毁步骤。您必须保证Logger应该最后清理。因此,请同时查看本文:http://www.cs.wustl.edu/~schmidt/PDF/ObjMan.PDF

@洛基·阿斯塔里的回答很好。

然而,有时使用多个静态对象时,您需要能够保证在所有使用单例的静态对象不再需要它之前,单例不会被破坏。

在这种情况下,std::shared_ptr可用于保持所有用户的单例有效,即使在程序结束时调用静态析构函数:

class Singleton
{
public:
    Singleton(Singleton const&) = delete;
    Singleton& operator=(Singleton const&) = delete;

    static std::shared_ptr<Singleton> instance()
    {
        static std::shared_ptr<Singleton> s{new Singleton};
        return s;
    }

private:
    Singleton() {}
};

我们最近在EECS课上讨论了这个话题。如果您想详细查看课堂讲稿,请访问http://umich.edu/~eecs381/讲座/习语DesPattsCreational.pdf。这些笔记(以及我在这个答案中给出的引文)由我的教授大卫·基拉斯创建。

我知道有两种方法可以正确创建Singleton类。

第一种方式:

按照示例中的方式实现它。至于破坏,“单线程通常会持续程序运行的时间;当程序终止时,大多数操作系统都会恢复内存和大多数其他资源,因此有理由不用担心这一点。”

然而,在程序终止时进行清理是一种很好的做法。因此,您可以使用辅助静态SingletonDestructor类来完成此操作,并将其声明为Singleton中的朋友。

class Singleton {
public:
  static Singleton* get_instance();
  
  // disable copy/move -- this is a Singleton
  Singleton(const Singleton&) = delete;
  Singleton(Singleton&&) = delete;
  Singleton& operator=(const Singleton&) = delete;
  Singleton& operator=(Singleton&&) = delete;

  friend class Singleton_destroyer;

private:
  Singleton();  // no one else can create one
  ~Singleton(); // prevent accidental deletion

  static Singleton* ptr;
};

// auxiliary static object for destroying the memory of Singleton
class Singleton_destroyer {
public:
  ~Singleton_destroyer { delete Singleton::ptr; }
};

// somewhere in code (Singleton.cpp is probably the best place) 
// create a global static Singleton_destroyer object
Singleton_destoyer the_destroyer;

Singleton_destroyer将在程序启动时创建,“当程序终止时,所有全局/静态对象都会被运行库关闭代码(由链接器插入)破坏,因此_destroyer将被破坏;其析构函数将删除Singleton,运行其析构器。”

第二条路

这叫做Meyers Singleton,由C++向导Scott Meyers创建。只需以不同的方式定义get_instance()。现在还可以去掉指针成员变量。

// public member function
static Singleton& Singleton::get_instance()
{
  static Singleton s;
  return s;
}

这很简单,因为返回的值是通过引用的,您可以使用。语法而不是->来访问成员变量。

“编译器通过声明,而不是之后,然后在程序中删除静态对象结束"

还需要注意的是,使用Meyers Singleton时,如果对象在终止-相对于其他对象,Singleton何时消失?但对于简单的应用程序,这很好。"