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

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

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

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


当前回答

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

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

在这种情况下,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() {}
};

其他回答

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

singleton<C>()

使用

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

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

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

它将类的实例化限制为一个对象。当只需要一个对象来协调整个系统的操作时,这非常有用

class Singleton {
private:
    int data;
    static Singleton* instance;
    Singleton();
public:
    static Singleton* getInstance();
};
Singleton* Singleton::instance = 0;
Singleton::Singleton()
{
    this->data = 0;
    cout << "constructor called.." << endl;
}

 

Singleton* Singleton::getInstance() {
    if (!instance) {
        instance = new Singleton();
        return instance;
    }
}
int main() {
    Singleton *s = s->getInstance();
    Singleton *s1 =s1->getInstance();
    }

作为单身汉,你通常不希望它被破坏。

当程序终止时,它将被拆除并释放,这是单例的正常行为。如果您希望能够显式地清理它,那么向类中添加一个静态方法非常容易,该方法允许您将其恢复到干净状态,并在下次使用时重新分配,但这超出了“经典”单例的范围。

接受答案中的解决方案有一个明显的缺点——在控件离开main()函数后调用单例的析构函数。当一些依赖对象被分配到main中时,可能真的会有问题。

我在尝试在Qt应用程序中引入Singleton时遇到了这个问题。我决定,我的所有设置对话框都必须是Singleton,并采用了上面的模式。不幸的是,Qt的主类QApplication被分配在主函数的堆栈上,当没有应用程序对象可用时,Qt禁止创建/销毁对话框。

这就是为什么我更喜欢堆分配的单件。我为所有单例提供了显式的init()和term()方法,并在main内部调用它们。因此,我可以完全控制单体创建/销毁的顺序,而且我保证无论是否有人调用getInstance(),都会创建单体。