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

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

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

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


当前回答

我们最近在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何时消失?但对于简单的应用程序,这很好。"

其他回答

它确实可能是从堆中分配的,但没有源就无从得知。

典型的实现(取自我在emacs中已有的一些代码)是:

Singleton * Singleton::getInstance() {
    if (!instance) {
        instance = new Singleton();
    };
    return instance;
};

……然后依靠超出范围的程序进行清理。

如果您在必须手动进行清理的平台上工作,我可能会添加一个手动清理例程。

这样做的另一个问题是它不是线程安全的。在多线程环境中,两个线程可以通过“if”,然后任何一个线程都有机会分配新实例(所以两者都会)。如果你依靠程序终止来清理,这仍然不是什么大问题。

简单的单例类,这必须是你的头类文件

#ifndef SC_SINGLETON_CLASS_H
#define SC_SINGLETON_CLASS_H

class SingletonClass
{
    public:
        static SingletonClass* Instance()
        {
           static SingletonClass* instance = new SingletonClass();
           return instance;
        }

        void Relocate(int X, int Y, int Z);

    private:
        SingletonClass();
        ~SingletonClass();
};

#define sSingletonClass SingletonClass::Instance()

#endif

像这样访问单例:

sSingletonClass->Relocate(1, 2, 5);

C++11线程安全实现:

 #include <iostream>
 #include <thread>


 class Singleton
 {
     private:
         static Singleton * _instance;
         static std::mutex mutex_;

     protected:
         Singleton(const std::string value): value_(value)
         {
         }
         ~Singleton() {}
         std::string value_;

     public:
         /**
          * Singletons should not be cloneable.
          */
         Singleton(Singleton &other) = delete;
         /**
          * Singletons should not be assignable.
          */
         void operator=(const Singleton &) = delete;

         //static Singleton *GetInstance(const std::string& value);
         static Singleton *GetInstance(const std::string& value)
         {
             if (_instance == nullptr)
             {
                 std::lock_guard<std::mutex> lock(mutex_);
                 if (_instance == nullptr)
                 {
                     _instance = new Singleton(value);
                 }
             }
             return _instance;
         }

         std::string value() const{
             return value_;
         }
 };

 /**
  * Static methods should be defined outside the class.
  */
 Singleton* Singleton::_instance = nullptr;
 std::mutex Singleton::mutex_;


 void ThreadFoo(){
     std::this_thread::sleep_for(std::chrono::milliseconds(10));
     Singleton* singleton = Singleton::GetInstance("FOO");
     std::cout << singleton->value() << "\n";
 }

 void ThreadBar(){
     std::this_thread::sleep_for(std::chrono::milliseconds(1000));
     Singleton* singleton = Singleton::GetInstance("BAR");
     std::cout << singleton->value() << "\n";
 }

 int main()
 {
     std::cout <<"If you see the same value, then singleton was reused (yay!\n" <<
                 "If you see different values, then 2 singletons were created (booo!!)\n\n" <<
                 "RESULT:\n";
     std::thread t1(ThreadFoo);
     std::thread t2(ThreadBar);
     t1.join();
     t2.join();
     std::cout << "Complete!" << std::endl;

     return 0;
 }

我们最近在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何时消失?但对于简单的应用程序,这很好。"

另一个非分配的选择:根据需要创建一个单例,比如C类:

singleton<C>()

使用

template <class X>
X& singleton()
{
    static X x;
    return x;
}

在当前C++中,这一点和Cætælin的答案都不是自动线程安全的,而是在C++0x中。