实现一个标准的单例


发布于 2020-07-29


在C++11之前,实现一个标准的单例模式类还是需要一些技巧的,要注意创建对象时的线程安全。

C++11之后,保证了静态变量的初始化是线程安全的。这样创建一个单例非常简单,代码如下:

class Singleton {
public:
  static Singleton *GetInstance() {
    static Singleton intance;
    return &intance;
  }

private:
  // 禁止在类外创建实例
  Singleton() = default;

  // 禁止在类外销毁
  ~Singleton() = default;

  // 禁止拷贝和赋值
  Singleton(const Singleton &) = delete;
  Singleton &operator=(const Singleton &) = delete;
};

只需要在类的静态函数里定义一个类的静态变量,返回其指针就行了。

因为我们是单例模式,所以还需要禁用构造函数、拷贝构造函数、赋值构造函数,以阻止其他地方代码不经意的创建了新的类实例。禁止析构函数防止其他地方代码不经意的销毁了实例。

因为定义的是static变量,所以类会在程序退出时,在main函数之外自动调用析构函数销毁。但是有时候,我们不希望单例类销毁,有以下几个原因:

  1. 类销毁成本很大,很耗时,会拖慢进程的退出
  2. 单例类可能跟其他类有依赖关系,退出时自动调用析构函数销毁,可能无法保证销毁的顺序,会引起崩溃

因此有一种泄漏的单例类,它在程序退出时也不会调用单例类的析构函数,实现如下:

class LeakySingleton {
public:
  static LeakySingleton *GetInstance() {
    static std::once_flag s_flag;
    std::call_once(s_flag, [&]() { intance_ = new LeakySingleton; });

    return intance_;
  }

private:
  // 禁止在类外创建实例
  LeakySingleton() = default;

  // 禁止在类外销毁
  ~LeakySingleton() = default;

  // 禁止拷贝和赋值
  LeakySingleton(const LeakySingleton &) = delete;
  LeakySingleton &operator=(const LeakySingleton &) = delete;

  static LeakySingleton *intance_;
};

LeakySingleton *LeakySingleton::intance_ = nullptr;

它的实现是定义了一个类的静态成员变量的指针,在GetInstance的时候,通过std::call_once只调用一次构造出来。因为它是new出来的,所以程序退出时不会析构到。

如果觉得定义一个类的静态成员变量,还需要在类外初始化,不够内聚。可以借助NoDestructor模板,生成一个不会调用析构函数的变量。关于NoDestructor,可以参考我的这篇博客 no destructor

template <typename T> class NoDestructor {
public:
  template <typename... Args> explicit NoDestructor(Args &&... args) {
    new (storage_) T(std::forward<Args>(args)...);
  }

  explicit NoDestructor(const T &x) { new (storage_) T(x); }
  explicit NoDestructor(T &&x) { new (storage_) T(std::move(x)); }

  NoDestructor(const NoDestructor &) = delete;
  NoDestructor &operator=(const NoDestructor &) = delete;

  ~NoDestructor() = default;

  const T &operator*() const { return *get(); }
  T &operator*() { return *get(); }

  const T *operator->() const { return get(); }
  T *operator->() { return get(); }

  const T *get() const { return reinterpret_cast<const T *>(storage_); }
  T *get() { return reinterpret_cast<T *>(storage_); }

private:
  alignas(T) char storage_[sizeof(T)];
};

class LeakySingleton {
public:
  static LeakySingleton *GetInstance() {
    static NoDestructor<LeakySingleton> intance;
    return intance.get();
  }

private:
  friend NoDestructor<LeakySingleton>;

  // 禁止在类外创建实例
  LeakySingleton() = default;

  // 禁止在类外销毁
  ~LeakySingleton() = default;

  // 禁止拷贝和赋值
  LeakySingleton(const LeakySingleton &) = delete;
  LeakySingleton &operator=(const LeakySingleton &) = delete;
};