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

单例模式(Singleton)[分类:创建型] [目的:为程序的某个类的访问,提供唯一对象实例]

这估计是设计模式里面最简单的一个类了,下面我们一起看看,单例它是什么?以及如何实现一个单例

  • 基本定义

    • 保证一个类仅有一个实例,并提供一个访问它的全局访问点.

  • 个人理解

    • 就是一个类在整个程序里面,有且仅有一个实例,这个实例由该类自己负责创建和保存,这样保证它不会在任何其他地方被创建,必须提供一个访问这个实例的全局访问接口.

    • 那么有些人可能有疑问,为什么不直接用一个全局变量来控制外部对它的访问呢?在这里如果用全局变量来控制外部的访问它实际上还是不能避免你实例化多个对象,这样还是达不到单例有且仅有一个实例的目的。

  • 单例的特征

    1. 有且仅有一个实例

    2. 实例必须由自己创建和保存

    3. 要提供和给外部访问实例的接口

  • UML结构图

  • 从UML结构图中我们可以一步步实现出以下代码

  • 简单版本的单例模式

1 class Singleton 2 { 3 public: 4     static Singleton& GetInstance()
 5     { 6         static Singleton instance; 7         return  instance; 8     } 9 private:10     Singleton();11 12 };
  • 这里我们可以看到,这个版本构造函数被私有化了,因此外部无法直接调用单例的构造器,而且使用静态的局部变量做返回,这样不需要考虑对内存的管理,是不是非常方便!但是我们在对函数执行以下操作时

1 Singleton instance1=Singleton::GetInstance();
2 Singleton instance2=Singleton::GetInstance();3 qDebug()<<"instance1 address:"<<&instance1;4 qDebug()<<"instance2 address:"<<&instance2;
  • 我们运行程序看结果

  • 在这里instance1和instance2实例他们的地址是完全不同的.这样的做法是不可取的,不是真正的单例,那么如何避免这样的问题的?跟开发者说你们直接调用GetInstance接口就行。

  • NONONO....

  • 下面我们看看修改版本的单例看看

1 class SingletonModify 2 { 3 public: 4     static SingletonModify& GetInstance()
 5     { 6         static SingletonModify instance; 7         return  instance; 8     } 9     void sayHello();10 private:11     SingletonModify();12     SingletonModify(SingletonModify const &);//to use this modify wrong demo13     SingletonModify& operator =(SingletonModify const &);//to use this modify wrong demo14 };
  • 从上面我们可以看出,我们重新定义了构造器以及=操作,这里不需要实现。然后我们再用相同的方式测试看看,这下编译器编译不通过了,那我们想调用实例的方法或者操作实例怎么办呢?

SingletonModify::GetInstance().sayHello();//after modify code,
  • 那我们只能通过上述方法访问实例了,保证了实例的唯一性了。

  • 简单版本的单例模式技术总结

  1. 将构造器私有化(保证外部无法创建实例)

  2. 使用静态的局部变量作为返回(无需考虑内存管理的问题)

  3. 重新定义赋值运算符以及构造器.(避免=发生拷贝操作)

  • 是不是觉得到这里单例就已经差不多啦?并不是,我们使用的是C++,但是并没有使用到C++的一个重要特征:指针

  • 首先我们针对上述简单版本的单例模式再次进行修正,我们修改单例的函数返回,用指针接收,看看下面代码

1 class SingletonSimplify 2 { 3 public: 4     SingletonSimplify(); 5     static SingletonSimplify* GetInstanceptr()//simplify method 6     { 7         static SingletonSimplify instance; 8         return  &instance; 9     }10 };
  • 测试:

1 SingletonSimplify* instanceSmp1=SingletonSimplify::GetInstanceptr();//another simplify singleton method2 SingletonSimplify* instanceSmp2=SingletonSimplify::GetInstanceptr();3 qDebug()<<"instanceSmp1 address:"<<instanceSmp1;4 qDebug()<<"instanceSmp2 address:"<<instanceSmp2;
  • 我们可以发现,用指针接收之后2个指针都是指向同一个地址,很好,这也是一种单例的实现方式,但是这个也属于访问静态局部对象,还是没有真正意义上使用到指针.再看看下面两种方式

  1. 懒汉模式

    • 1 SingletonLazyMode *SingletonLazyMode::mSingletonInstance=NULL; 2 SingletonLazyMode *SingletonLazyMode::GetInstance() 3 { 4     if(mSingletonInstance==NULL) 5     { 6         mSingletonInstance=new SingletonLazyMode(); 7         mSingletonInstance->num=20; 8     } 9     return  mSingletonInstance;10 }11 void SingletonLazyMode::InstanceDispose()12 {13     if(mSingletonInstance!=NULL)14     {15         delete mSingletonInstance;16         mSingletonInstance=NULL;17     }18 }19 void SingletonLazyMode::sayHi()20 {21     qDebug()<<"hi lazy man! Number:"<<num;22 }23 SingletonLazyMode::SingletonLazyMode()24 {25 }
    • 1 class SingletonLazyMode 2 { 3 public: 4     static SingletonLazyMode* GetInstance(); 5     static void InstanceDispose(); 6     void sayHi(); 7 private: 8     SingletonLazyMode(); 9     static SingletonLazyMode* mSingletonInstance;10     int num=10;11 };
    • 先看头文件.h代码

    • .cpp实现

  2. 饿汉模式

    • 1 SingletonEagerMode *SingletonEagerMode::mEagerInstance=new SingletonEagerMode(); 2 SingletonEagerMode *SingletonEagerMode::GetInstance() 3 { 4     return  mEagerInstance; 5 } 6 void SingletonEagerMode::InstanceDispose() 7 { 8     if(mEagerInstance!=NULL) 9     {10         delete mEagerInstance;11         mEagerInstance=NULL;12     }13 }14 SingletonEagerMode::SingletonEagerMode()15 {1617 }
    • 1 class SingletonEagerMode2 {3 public:4     static SingletonEagerMode* GetInstance();5     static void InstanceDispose();6 private:7     SingletonEagerMode();8     static SingletonEagerMode* mEagerInstance;9 };
    • 先看头文件.h代码

    • .cpp实现

  • 测试代码

    • 1 SingletonLazyMode* lazyinstance1=SingletonLazyMode::GetInstance();//lazy mode 懒汉模式 2     SingletonLazyMode* lazyinstance2=SingletonLazyMode::GetInstance(); 3     lazyinstance1->sayHi(); 4     lazyinstance2->sayHi(); 5     qDebug()<<"lazyinstance1 address:"<<lazyinstance1; 6     qDebug()<<"lazyinstance2 address:"<<lazyinstance2; 7     SingletonLazyMode::InstanceDispose(); 8     qDebug()<<"lazyinstance1 address:"<<lazyinstance1; 9     qDebug()<<"lazyinstance2 address:"<<lazyinstance2;10     lazyinstance1->sayHi();11     lazyinstance2->sayHi();12     SingletonEagerMode* eagerinstance1=SingletonEagerMode::GetInstance();//eager mode 饿汉模式13     SingletonEagerMode* eagerinstance2=SingletonEagerMode::GetInstance();14     qDebug()<<"eagerinstance1 address:"<<eagerinstance1;15     qDebug()<<"eagerinstance2 address:"<<eagerinstance2;
  • 运行效果:

    • 这里我们可以看到不管是懒汉还是饿汉模式两个实例的地址均是相同的。说明我们的单例是OK的

  • 技术总结:

  • 下面对懒汉模式和饿汉模式经行对比分析其异同点:

    • 懒汉模式初始化对象是在程序调用的时候,非线程安全,由于最终实现要加Qmutex进行枷锁处理,执行效率会相对而言要低

    • 饿汉模式是程序启动的时候就已经创建好了,浪费内存,但属于线程安全,执行效率相对懒汉而言要高

    • 懒汉/饿汉模式实现结构基本类似

    • 相同点:

    • 不同点:

  • 问题点:

  • 细心的同学可能发现了,在上面测试过程中我调用了自己定义的Dispose接口,但是还是能再次调用lazyinstance1,lazyinstance2实例中的函数和变量???这是在MinGW编译器下执行的结果,当我将编译器换成MSVC时,显示内存已经被释放掉了

  • 下图MSVC下的执行结果

  • 参考了一篇博客也没看出什么问题:https://www.cnblogs.com/chengjundu/p/11283123.html

  • 如果有朋友知道望不吝赐教!!!!感谢。

  • 最后看看我们线程安全的懒汉实现方式

  • .h文件

    1 class SingletonThreadSafety 2 { 3 public: 4     static SingletonThreadSafety* GetInstance(); 5     static void InstanceDispose(); 6 private: 7     SingletonThreadSafety(); 8     static SingletonThreadSafety* mSaftyInstance; 9     static QMutex mMutex;10 };
  • .cpp代码

  • 1 SingletonThreadSafety *SingletonThreadSafety::mSaftyInstance=NULL; 2 QMutex SingletonThreadSafety::mMutex; 3 SingletonThreadSafety *SingletonThreadSafety::GetInstance() 4 { 5     if(mSaftyInstance==NULL) 6     { 7         QMutexLocker locker(&mMutex); 8         if(mSaftyInstance==NULL) 9         {10             mSaftyInstance=new SingletonThreadSafety();11         }12     }13     return  mSaftyInstance;14 }15 void SingletonThreadSafety::InstanceDispose()16 {17     if(mSaftyInstance!=NULL)18     {19         delete mSaftyInstance;20         mSaftyInstance=NULL;21     }22 }23 SingletonThreadSafety::SingletonThreadSafety()24 {25 26 }

    这就完美解决掉了线程安全问题,但是在获取实例对象的时候需要对Qmutex进行判断,这会损失一点点性能。

  • 以上就是对单例模式的完整概述

  • 下面进行全面的技术总结:

  • 在写单例模式的时候我们要考虑到以下几个方面:

  1. 要封闭默认的构造函数,以防止多地方创建对象

  2. 类提供一个静态的对象,用来保存该实例

  3. 提供一个公共的访问实例的接口GetInstance

  4. 考虑线程安全问题

以上单例所有内容,如有错误请指出!!!

参考<<大话设计模式>>一书

附源代码:

https://gitee.com/xiaochunlu/designer-pattern/tree/master/Singleton_Pattern

(0)

相关推荐

  • 设计模式之单例模式(Singleton Pattern),太简单了

    基本定义 单例模式就是确保某一个类只有一个实例,并且提供一个全局访问点.单例模式有如下几个特点: 它只有一个实例. 它必须要自行实例化. 它必须自行向整个系统提供访问点. 代码实现 饿汉式 直接初始化 ...

  • 嵌入式系统架构浅谈:编程设计模式 (一)---访问硬件的设计模式

    这系列开始谈软件上面的设计,对设计模式在面向对象里面应该各位都知道,或许你在实际开发当中用到,也或许你见过别人的代码中用到.当你程序的代码足够庞大的时候,你会发现维护寸步难行,牵一发而动全身,这个时候 ...

  • 浅谈微服务架构的设计模式

    微服务架构模式(MicroserviceArchitectPattern).近两年在服务的疯狂增长与云计算技术的进步,让微服务架构受到重点关注 微型服务体系结构是一种体系结构模式,它主张把单个应用分成 ...

  • Java“老兵”浅谈源码的七大设计模式

    https://www.shengchulai.com/blog-9sFmIzthaZ.htm 一个专业的程序员,总是把代码的清晰性,兼容性,可移植性放在很重要的位置.他们总是通过定义大量的宏,来增强 ...

  • 浅谈乡村治理模式发生了哪些变化?

    随着乡村现代化的发展,越来越多的乡村走上了建设数字乡村的道路,从传统的乡村治理到使用互联网数字化治理的模式,乡村的风貌和农民的生活也发生了巨大的变化,接下来就让我们一起来了解一下乡村治理模式到底发生了 ...

  • 汽车是怎么开发出来的?浅谈汽车开发流程

    许良  汽车话题下的优秀答主你知道汽车是怎么开发出来的吗?你的脑海中很可能浮现出来这样一个画面:一个非常有艺术气息的设计师,在草图上帅气的描绘着看起来非常犀利的线条.对,但不全对.对于汽车工程师的我而 ...

  • 浅谈办公室装修的发展前景和趋势

    未来办公室装修的发展趋势会是怎样的?这是这个行业未来前景的重要话题.在这样一个新时代里,所有的事物都会以最新颖的方式出发.科技的发展也让每个行业都转遍了方向,同时对行业的要求和品质也有了更高的要求. ...

  • 颧骨浅谈

    ​骨过高 颧骨过于发达的人,单从脸部看上去就给人以高傲的感觉.而他们也得确有这种个性,常固执已见,虚张声势或显得自负是他们的特色.在工作上,也不愿意接受他人的忠告,总认为自己的就是最好的,这一点不利于 ...

  • 浅谈地龙在治疗咳喘中的运用

    浅谈地龙在治疗咳喘中的运用 笔者从事中医临床工作十多年来,在运用地龙治疗痰湿壅肺型.肺络瘀阻型.心肺两虚型.肾不纳气型的咳喘上有了一些心得,现介绍如下.   咳喘是现代医学中呼吸系统.心血管系统疾病的 ...

  • [鉴史释疑]曹操是英雄还是汉贼 浅谈曹操的英雄之处

    时间:2021-04-16 08:30:03    来源:本站(吾爱诗经网)整理       作者:魁哥说历史 在不同人眼里,曹操的形象和品格都是不一样的.有人觉得曹操是汉贼,但也有人说曹操是英雄,其 ...