C++设计模式--单例模式

打印 上一主题 下一主题

主题 1027|帖子 1027|积分 3081

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
参考C++设计模式 - 单例模式_单例模式大秦坑王-CSDN博客
单例模式简介

单例模式指的是,无论怎么获取,永远只能得到该类类型的唯逐一个实例对象,那么设计一个单例就必须要满足下面三个条件:

  • 构造函数私有化,这样用户就不能任意定义该类型的对象了
  • 定义该类型唯一的对象
  • 通过一个static静态成员方法返回唯一的对象实例
饿汉单例模式

饿汉式单例模式,顾名思义,就是步伐启动时就实例化了该对象,并没有推迟到第一次使用该对象时再进行实例化;如果运行过程中没有使用到,该实例对象就被浪费掉了。
  1. class CSingleton{
  2. public:
  3.         static CSingleton* getInstance()
  4.         {
  5.                 return &single;
  6.         }
  7. private:
  8.         static CSingleton single;
  9.         CSingleton() { cout << "CSingleton()" << endl; }
  10.         ~CSingleton() { cout << "~CSingleton()" << endl; }
  11.         CSingleton(const CSingleton&);
  12.     // 防止外部使用拷贝构造产生新的对象,如下面CSingleton s = *p1;
  13. };
  14. CSingleton CSingleton::single;
  15. int main(){
  16.         CSingleton *p1 = CSingleton::getInstance();
  17.         CSingleton *p2 = CSingleton::getInstance();
  18.         CSingleton *p3 = CSingleton::getInstance();
  19.         cout<<p1<<" "<<p2<<" "<<p3<<endl;
  20.         return 0;
  21. }
复制代码
是否线程安全?

饿汉单例模式中,单例对象定义成了一个static静态对象,它是在步伐启动时,main函数运行之前就初始化好的,因此不存在线程安全问题,可以放心的在多线程环境中使用
懒汉单例模式

步伐启动时,只对single指针初始化了空值,等第一次调用getInstance函数时,由于single指针为nullptr,才进行对象的实例化,所以是一个懒汉式单例模式(对象的实例化,耽误到第一次使用它的时候)。
  1. class CSingleton{
  2. public:
  3.         static CSingleton* getInstance(){
  4.                 if (nullptr == single){
  5.                         single = new CSingleton();
  6.                 }
  7.                 return single;
  8.         }
  9. private:
  10.         static CSingleton *single;
  11.         CSingleton() { cout << "CSingleton()" << endl; }
  12.         ~CSingleton() { cout << "~CSingleton()" << endl; }
  13.         CSingleton(const CSingleton&);
  14. };
  15. CSingleton* CSingleton::single = nullptr;
  16. int main(){
  17.         CSingleton *p1 = CSingleton::getInstance();
  18.         CSingleton *p2 = CSingleton::getInstance();
  19.         CSingleton *p3 = CSingleton::getInstance();
  20.         cout << p1 << " " << p2 << " " << p3 << endl;
  21.         return 0;
  22. }
复制代码
上面new出来的对象,没见过delete,这样不好吧,固然了,有new没有delete,不配对啊!尚有人说,管它呢,当进步程结束的时候,体系反正会回收分配给它的全部资源,包括未回收的内存,但是作为C++开发者,资源的分配和回收,我们必须要考虑清晰,不能糊涂,那么下面的修改感觉如何:
  1. int main(){
  2.         CSingleton *p1 = CSingleton::getInstance();
  3.         CSingleton *p2 = CSingleton::getInstance();
  4.         CSingleton *p3 = CSingleton::getInstance();
  5.         cout << p1 << " " << p2 << " " << p3 << endl;
  6.         delete p1;    // 这里delete之前new过的对象,析构对象并且释放堆上的内存
  7.         return 0;
  8. }
复制代码
这种方式怎么看,怎么不舒服,起首资源的开释如果交给用户来操作,难免会忘记写delete,又大概多次delete,成开释野指针了,所以上面开释单例对象资源的方式不够好,我们利用static静态对象在步伐结束时自动析构这么一个特征,给出如下开释资源的代码,肯定比上面的方式要好,代码如下:
  1. class CSingleton{
  2. public:
  3.         static CSingleton* getInstance(){
  4.                 if (nullptr == single){
  5.                         single = new CSingleton();
  6.                 }
  7.                 return single;
  8.         }
  9. private:
  10.         static CSingleton *single;
  11.         CSingleton() { cout << "CSingleton()" << endl; }
  12.         ~CSingleton() { cout << "~CSingleton()" << endl; }
  13.         CSingleton(const CSingleton&);
  14.         // 定义一个嵌套类,在该类的析构函数中,自动释放外层类的资源
  15.         class CRelease{
  16.         public:
  17.                 ~CRelease() { delete single; }
  18.         };
  19.         // 通过该静态对象在程序结束时自动析构的特点,来释放外层类的对象资源
  20.         static CRelease release;
  21. };
  22. CSingleton* CSingleton::single = nullptr;
  23. CSingleton::CRelease CSingleton::release;
  24. int main(){
  25.         CSingleton *p1 = CSingleton::getInstance();
  26.         CSingleton *p2 = CSingleton::getInstance();
  27.         CSingleton *p3 = CSingleton::getInstance();
  28.         cout << p1 << " " << p2 << " " << p3 << endl;
  29.         return 0;
  30. }
复制代码
是否线程安全?

1.
  1. static CSingleton* getInstance(){
  2.         if (nullptr == single){
  3.                 single = new CSingleton();
  4.         }
  5.         return single;
  6. }
复制代码
很显着,这个getInstance是个不可重入函数,也就它在多线程环境中执行,会出现竞态条件问题,起首搞清晰这句代码,single = new CSingleton()它会做三件事变,开发内存,调用构造函数,给single指针赋值,那么在多线程环境下,就有大概出现如下问题:

  • 线程A先调用getInstance函数,由于single为nullptr,进入if语句
  • new操作先开发内存,此时A线程的CPU时间片到了,切换到B线程
  • B线程由于single为nullptr,也进入if语句了,开始new操作
很显着,上面两个线程都进入了if语句,都试图new一个新的对象,不符合单例模式的设计,那该如何处理呢?对了,应该为getInstance函数内部加锁,在线程间进行互斥操作。此处使用锁+双重判断,也叫双重查验锁,代码如下:
  1. static CSingleton* getInstance(){
  2.         if (nullptr == single){
  3.                 // 获取互斥锁
  4.                 pthread_mutex_lock(&mutex);
  5.                 // 这里需要再添加一个if判断,否则当两个线程都进入这里,又会多次new对象
  6.                 if(nullptr == single){
  7.                         single = new CSingleton();
  8.                 }
  9.                 // 释放互斥锁
  10.                 pthread_mutex_unlock(&mutex);
  11.         }
  12.         return single;
  13. }
复制代码
2.
  1. #include <iostream>
  2. using namespace std;
  3. class CSingleton{
  4. public:
  5.         static CSingleton* getInstance(){
  6.                 static CSingleton single; // 懒汉式单例模式,定义唯一的对象实例
  7.                 return &single;
  8.         }
  9. private:
  10.         static CSingleton *single;
  11.         CSingleton() { cout << "CSingleton()" << endl; }
  12.         ~CSingleton() { cout << "~CSingleton()" << endl;}
  13.         CSingleton(const CSingleton&);
  14. };
  15. int main(){
  16.         CSingleton *p1 = CSingleton::getInstance();
  17.         CSingleton *p2 = CSingleton::getInstance();
  18.         CSingleton *p3 = CSingleton::getInstance();
  19.         return 0;
  20. }
复制代码
对于static静态局部变量的初始化,编译器会自动对它的初始化进行加锁息争锁控制,使静态局部变量的初始化成为线程安全的操作,不用担心多个线程都会初始化静态局部变量,因此上面的懒汉单例模式是线程安全的单例模式!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

南飓风

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表