Singleton in java vs. Singleton in C++

Singleton in java vs. Singleton in C++

本文关键字:Singleton in C++ java vs      更新时间:2023-10-16

在Java中,我可以创建一个这样的Singleton(只要它不作为异常抛出):

private static Singleton m_this = new Singleton();

这非常方便,因为它本质上是线程安全的。

我可以在C++中做类似的事情吗?

一种使用线程安全初始化创建单例的方法,由 C++11 的标准保证。 是:

class SomeSingleton {
  public:
    static SomeSingleton& instance() {
      static SomeSingleton instance_;
      return instance_;
    }
  private:
  SomeSingleton() {
    ...
  }
};

这是线程安全的,因为局部静态变量初始化在 C++11 中是线程安全的。相关标准文档 N3485 在第 6.7.4 节中说:

此类变量在

控件第一次通过其声明时初始化;此类变量在其初始化完成后被视为已初始化。[...]如果在初始化变量时控件并发进入声明,则并发执行应等待初始化完成。

带脚注:

实现不得在初始值设定项的执行周围引入任何死锁。

您可以使用 CRTP 抽象成一个漂亮的模板基类:

//Singleton template definition
template <typename TDerived>
class Singleton {
  static_assert(is_base_of<Singleton, TDerived>::value, "Singleton: unexpected Derived template parameter");
  public:
    static TDerived& instance() {
      static TDerived instance_;
      return instance_;
    }
  protected:
  Singleton() {
  }
};
// SomeSingleton definition, using the Singleton template
class SomeSingleton : public Singleton<SomeSingleton> {
  ...
};

如果所说的单例是指真正的单例(我不确定你给出的例子是否相同,因为你仍然可以创建第二个Singleton实例并将其分配给不同的变量),答案是肯定的。您可以使用工厂模式创建单一实例对象。

class factory;
class singleton{
   singleton(){}; // private constructor
   friend class factory;
};
class factory{
   private:
      static std::shared_ptr<singleton> object;
   public:
      static singleton& getSingleton(){
         if(object)
            return *object;
         object = new singleton;
         return *object;
      }
};

您也可以getSingleton singleton本身的static成员函数,而不是创建专用的factory类。只要记住使构造函数private,这样用户就不能创建多个副本。

另外,除了 Paweł Stawarz 给出的答案之外,我还要补充一点,复制构造函数、移动构造函数 (C++11)、重载赋值运算符和析构函数也应该是私有的。

class singleton{
   singleton(){}; // private constructor
   ~singleton(){}; // private destructor
   singleton(const singleton &src){}; // private copy constructor
   singleton(const singleton&& src){}; // private move constructor for C++11
   singleton& operator = (const singleton& src){return *this;} // private = operator
   friend class factory;
};

作为私有的,复制构造函数、移动构造函数 (C++11)、重载赋值运算符和析构函数不能从外部调用来克隆现有副本以及销毁刚刚创建的副本。