C++单例模式/饱汉式/饿汉式/返回指针/引用

223 阅读2分钟

单例模式就是某个类只可以有一个对象/实例,所以我们需要将它的构造函数设置为私有,通过一个函数来返回对象。但我们知道,调用成员函数必须通过对象调用,这就陷入了一个先有鸡还是先有蛋的问题。故我们可以通过静态函数返回一个实例,同时需要将拷贝构造函数和拷贝赋值运算符设置为删除

返回引用

//懒汉式
class Singleton
{
public:
  static Singleton& getInstance(int i_)
  {
    static Singleton instance(i_);
    return instance;
  }

  ~Singleton() = default;
  
  //重载了输出运算符,必须设置为友元函数
  friend ostream& operator<<(ostream& out, const Singleton& rhs) {
    out << rhs.i;
  }
private:
  Singleton(int i_):i(i_) {};
  Singleton(const Singleton&) = delete;
  Singleton& operator=(const Singleton&) = delete;
  int i;
};


int main() {
  Singleton &t = Singleton::getInstance(10);
  Singleton &t1 = Singleton::getInstance(11);
  std::cout << t << std::endl; //10
  std::cout << t1 << std::endl; //10
}

以上是懒汉式单例模式,只有在需要的时候才会调用getInstance生成对象,同时构造对象的语句用static修饰,确保只会执行一次。由于类的静态变量和函数内的局部静态变量的初始化是线程安全的,故这个单例模式也是线程安全的,不需要额外加锁,这也是《effective c++》最为推荐的单例模式实现形式。 下面的饿汉式的实现:

class Singleton
{
public:
  static Singleton& getInstance()
  {
    return instance;
  }

  ~Singleton() = default;


private:
  Singleton() = default;
  Singleton(const Singleton&) = delete;
  Singleton& operator=(const Singleton&) = delete;

 //静态变量
  static Singleton instance;
};

饿汉式的实现比较简单, 只要定义一个静态变量即可, 但是它的缺点是不管你用不用得到这个单例, 它都会在程序初始化时被创建出来, 有可能会造成资源的浪费。

返回指针

返回指针的实现思路和以上的类似,下面是饿汉版的实现,在main函数执行之前就完成了静态对象的初始化。

class Singleton
{
public:
  static Singleton* instance;
  static Singleton* getInstance()
  {
    return instance;
  }

  ~Singleton() = default;

private:
  Singleton() = default;
  Singleton(const Singleton&) = delete;
  Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::instance = new Singleton();

接下来是懒汉版本的实现,为了确保单例模式正常执行,需要加锁。

class Singleton
{
public:
  static Singleton* p;
  static std::mutex mtx;
  static Singleton* getInstance()
  {
    //减少线程对互斥锁的竞争
    if(p == nullptr) {
      lock_guard<mutex> lock(mtx);
      //确保只有一个对象
      if(p == nullptr) {
        p = new Singleton();
      }
    }
    return p;
  }

  ~Singleton() = default;

private:
  Singleton() = default;
  Singleton(const Singleton&) = delete;
  Singleton& operator=(const Singleton&) = delete;
};