C++实现单例模式

C++实现单例模式

1.什么是单例模式?

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。 比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

2.单例模式的两种实现模式

2.1 饿汉模式

饿汉模式是指不管你将来用不用程序启动时就创建一个唯一的实例对象,而且是在main入口前就创建完成

//饿汉模式
//优点:简单
//缺点:可能会导致启动慢
#include<iostream>
using namespace std;
class Singleton
{
public:
  static Singleton* Getinstance()
  {
    return &_sInstance; 
  }
 //这里也可以直接返回值或者引用 
 // static Singleton& Getinstance()
 // {
 //   return _sInstance;
 // }
  void Print()
  {
    cout << "饿汉模式" << endl;
  }
private:
  Singleton()//构造器私有化,保证其他地方不能直接创建对象
  {}
  //防止拷贝构造,C++98只声明不定义
  Singleton(Singleton const& sl);
  //C++11已删除函数
  Singleton& operator=(Singleton const& sl) = delete;
  static Singleton _sInstance;
};
Singleton Singleton::_sInstance; //在main函数入口之前就已经创建好对象
int main()
{
  //Singleton s1; 在定义静态变量_sIntance时就已经创建了对象,在次创建对象则会失败
  //Singleton s(*Singleton::Getinstance()); //用拷贝构造创建对象也会失败,因为拷贝函数被显示的声明为私有的
  //Singleton s1 = *Singleton::Getinstance();//运算符重载也会创建失败,因为被显示的声明为已删除函数并且是私有的
  Singleton::Getinstance()->Print();
  return 0;
}

用户访问唯一实例的方法只有Getinstance()成员函数。如果不通过这个函数,任何创建实例的尝试都将失败,因为类的构造函数是私有的Getinstance()使用懒惰初始化,也就是说静态成员变量_sInstance一旦被定义对象就会被创建。因为成员方法和变量只能通过对象来访问,所以我们必须把它们声明为static,让它们属于类而不属于对象,这样我们就可以直接通过类名来访问了。但是在定义类的时候,一定要记得把累的拷贝构造函数和赋值运算符重载也声明为私有的,否则还可以通过拷贝构造去拷贝其他的对象。 如果这个单例对象在多线程高并发环境下频繁使用性能要求较高,那么显然使用饿汉模式来避免资源竞争提高响应速度更好。

2.2 懒汉模式

如果单例对象构造十分耗时或者占用很多资源,比如加载插件, 初始化网络连接,读取文件等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式,该对象在我们首次访问公用函数时创建,而不是在函数一运行起来就要创建

#include<iostream>
using namespace std;

class Singleton
{
public:
  static Singleton* Getinstance()
  {
    if(_spInstance == NULL)
    {
      _spInstance = new Singleton();
    }
    return _spInstance;
  }
  void Print()
  {
    cout << "懒汉模式" << endl;
  }
private:
  Singleton()//构造器私有化,保证其他地方不能直接创建对象
  {}
   //防拷贝C++98(只声明不定义)
  Singleton1(const Singleton1& sl);
  //C++11(使用delete)
  Singleton1& operator=(const Singleton1& sl) = delete;
 

  static Singleton* _spInstance;
};
Singleton* Singleton::_spInstance = NULL;
int main()
{
  Singleton::Getinstance()->Print();//调用Getinstance函数时对象才会被创建
  return 0;
}

但是如果在类的析构行为中有必须的操作,例如要关闭文件,释放外部资源,那么上面的代码无法实现这个要求因为这个指向对象的指针是一个静态成员,无法调用本类的析构。我们可以有种方法,正常的删除该实例:

  • 可以在程序结束时调用Getinstance(),并对返回的指针掉用delete操作。但是这样做容易出错。因为很难保证在delete之后,没有代码再调用GetInstance函数。
  • 让这个类把删除自己的操作挂在操作系统中的某个合适的点上,使其在恰当的时候被自动执行。我们知道程序在结束的时候,系统会自动析构所有的全局变量和所有的类的静态成员变量。利用这个特征,我们可以在单例类中定义一个静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例
#include<iostream>
using namespace std;
class Singleton
{
public:
  static Singleton* Getinstance()
  {
    if(_spInstance == NULL)
    {
      _spInstance = new Singleton();
    }
    return _spInstance;
  }
  void Print()
  {
    cout << "懒汉模式" << endl;
  }
  //定义一个内部垃圾回收类,它是外部类的友元类,可以访问外部类的成员
  class CGarbo 
  {
  public:
    ~ CGarbo()
    {
      if(Singleton::_spInstance)
        delete Singleton::_spInstance;
    }   
    void Print1()
    {
      cout << "析构函数" << endl;
    }
    //程序结束时系统自动调用析构释放单例对象
    static CGarbo Garbo;
  };
private:
  Singleton()//构造器私有化,保证其他地方不能直接创建对象
  {}
   //防拷贝C++98(只声明不定义)
 Singleton1(const Singleton1& sl);
 //C++11(使用delete)
 Singleton1& operator=(const Singleton1& sl) = delete;
 
  static Singleton* _spInstance;
};
//静态变量在类外定义,类内声明
Singleton* Singleton::_spInstance = NULL;
Singleton::CGarbo Garbo;
int main()
{
  Singleton::Getinstance()->Print();
  return 0;
}

上述的类可以很好的解决释放的问题。但是依然存在很多问题,例如线程安全的问题,如果两个线程同时走到new的时候,指针_spInstance只有一份,而两个线程都会new一份空间,那么这两块空间应该怎么分配给这个指针呢?这就导致了线程安全的问题,我们可以用互斥锁和双检查来解决这个问题

//懒汉模式
//优点:第一次使用实例对象创建对象
//缺点:复杂,不是线程安全的(两个线程并行处理可能会new出来两个对象,可以加锁解决
#include<iostream>
using namespace std;
#include <mutex> //互斥量
class Singleton1
{
public:
 static Singleton1* GetInstance()
 {
  //double check(可以提高效率,如果没有双检查,原本并行的现场到这里都变成串行的)
  if (_spInstance == nullptr)
  {
   //_mutex.lock();相当于二元信号量,只允许一个人进去
   if (_spInstance == nullptr)
    _spInstance = new Singleton1;
   //_mutex.unlock();
  }
  return _spInstance;
 }
 void Print()
 {
  cout << "懒汉模式" << endl;
 }
 //定义一个垃圾回收内部类
 class CGarbo
 {
 public:
  ~CGarbo()
  {
   //close,delete
   cout << "delete" << endl;
  }
  static CGarbo Garbo;
 };
private:
 Singleton1()//构造函数私有化
 {}
 //防拷贝C++98(只声明不定义)
 Singleton1(const Singleton1& sl);
 //C++11(使用delete)
 Singleton1& operator=(const Singleton1& sl) = delete;
 
 static Singleton1* _spInstance;
 static mutex _mutex; //互斥锁
};
//静态变量类外定义
mutex Singleton1::_mutex;
Singleton1::CGarbo Garbo;
Singleton1* Singleton1::_spInstance = nullptr;
int main()
{
 Singleton1::GetInstance()->Print();
 return 0;
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值