在实现单例类时,通常要把构造相关的几个函数访问权限设为private或protected(最好是private)。但假设一个大型系统中,有数十个单例类(这很正常,单例类其实是外观模式的一种最常用设计),每一个都这么写显得繁琐不堪。要把这些操作代表的代码复用,可以使用c++语言提供的利器---继承。

设计如下的基类:

 class NonCopyable {
public:
NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
void operator=(const NonCopyable& c) = delete;
};

当单例类继承此类后,客户就无法在复制或赋值此单例类的实例化对象了,这样整个内存只存在一个全局对象。这里的NonCopyable类实现参考了boost::noncopyable。

解决了构造函数访问权限问题后,还有个问题,就是如何实例化?单例类实现的经典做法是在类里声明一个私有的静态类对象的指针,使用get手法返回此对象实例引用,通过此引用再调用类的其它方法。很好,但每个单例类都要这么实现一遍仍然繁琐。此时可以使用C++提供的另一个代码复用的利器--template。先实现一个通用的singleton模板类,模板类型参数传入待实现的单例类名,这样在编译阶段就完成了单例类的构造(模板实例化)。

通用的singleton模板类实现如下:

 template <class T>
class Singleton : public NonCopyable {
private:
static T* inst_; public:
Singleton() {}
virtual ~Singleton() {} static T& inst()
{
if (!inst_) inst_ = new T;
return *inst_;
} static void uninst()
{
if (!inst_) return;
delete inst_;
inst_ = nullptr;
} };
//__declspec(selectany)声明使得我们可以在头文件中初始化一个全局变量
template <class T> __declspec(selectany) T * Singleton<T>::inst_ = nullptr;

自定义单例类实现:

 class singletontest :public Singleton<singletontest>
{
public:
singletontest(){ printf("singletontest constructor function called "); }
~singletontest(){ printf("class test object destroyed "); }
//成员方法
void print(){
printf("singletontest::print function called ");
}
};

测试代码:

 singletontest::inst().print(); //line1
singletontest a; //line 2 无法阻止默认构造,理想中,单例类只允许出现类似line1的调用
printf("singletontest obj a addr is %d\n", &a);
a.print();
singletontest b;
printf("singletontest obj b addr is %d\n", &b);
b.print();
//a(b); //singletontest继承了noncopyable,继承类初始化时先调用父类的构造函数,由于定义成私有,所以构造失败
//a = b; //同上,赋值运算操作符函数同样是私有的

测试结果:

line2行代码写完后编译期并不报错,意味着对象使用默认构造生成。想阻止这种行为,就要把默认构造函数设为私有,但inst()方法里的 new T就会失败,更加得不偿失。

测试 line 8、9代码的错误,编译器可以识别。

代码里存在三层的继承链(singletontest-> singleton<T>-> Noncopyable),但因为类中没有声明虚函数,调用方法上无周转之处,所以性能上没有任何损失。

singletontest:public sington<singletontest>这种写法成文CRTP(Curiously Recurring Template Pattern:奇异递归模板模式),常用于实现静多态,本文不作过多介绍。

最后,一般在多线程环境中讨论单例类创建时,常有饱汉式与饿汉式两种方式,但现代C++已经保证了静态成员变量创建的线程安全性,所以再无讨论这种方式的必要了

当noncopyable遇见singleton的更多相关文章

  1. item2,实现singleton模式

    单例模式? 只能实现一个实例的类成为单例. ============== muduo库中单例模式实现 #include<boost/noncopyable.hpp> //#include ...

  2. pthread_once重塑singleton模式

    单件模式是非线程安全的: // Single threaded version class Foo { private Helper helper = null; public Helper getH ...

  3. boost::singleton

    singleton即单件模式,实现这种模式的类在程序生命周期里只能有且仅有一个实例. 使用singleton,需要包括头文件: #include <boost/serialization/sin ...

  4. Be Better:遇见更好的自己-2016年记

    其实并不能找到好的词语来形容过去的一年,感觉就如此平淡的过了!没有了毕业的稚气,看事情淡了,少了一丝浮躁,多了一分认真.2016也许就是那句话-多读书,多看报,少吃零食多睡觉,而我更愿意说--Be B ...

  5. 23种设计模式--单例模式-Singleton

    一.单例模式的介绍 单例模式简单说就是掌握系统的至高点,在程序中只实例化一次,这样就是单例模式,在系统比如说你是该系统的登录的第多少人,还有数据库的连接池等地方会使用,单例模式是最简单,最常用的模式之 ...

  6. 设计模式之单例模式(Singleton)

    设计模式之单例模式(Singleton) 设计模式是前辈的一些经验总结之后的精髓,学习设计模式可以针对不同的问题给出更加优雅的解答 单例模式可分为俩种:懒汉模式和饿汉模式.俩种模式分别有不同的优势和缺 ...

  7. PHP设计模式(四)单例模式(Singleton For PHP)

    今天讲单例设计模式,这种设计模式和工厂模式一样,用的非常非常多,同时单例模式比较容易的一种设计模式. 一.什么是单例设计模式 单例模式,也叫单子模式,是一种常用的软件设计模式.在应用这个模式时,单例对 ...

  8. 自建git node pm2 (不赘述,就说遇见的问题)

    //======================[git]部分 主题部分还是按照网上的办法进行安装. 安装的话  分为两个办法(一个是yum (contos办法)  或者sudo(ubuntu办法) ...

  9. The Java Enum: A Singleton Pattern [reproduced]

    The singleton pattern restricts the instantiation of a class to one object. In Java, to enforce this ...

随机推荐

  1. sql server driver ODBC驱动超时

  2. ES6语法的数组查询

    setProductId(param){ console.log(param); let prod = this.products.find(item =>{ return item.prodC ...

  3. <转>ajax 同步异步问题

    原文:https://blog.csdn.net/wxr15732623310/article/details/76387787 看似简单明白的问题说起来却迷迷糊糊,完全没有逻辑,说不到重点上,再来整 ...

  4. Swagger注解

    swagger注解说明  1.与模型相关的注解,用在bean上面 @ApiModel:用在bean上,对模型类做注释: @ApiModelProperty:用在属性上,对属性做注释 2.与接口相关的注 ...

  5. Java 运行时常量池

    运行时常量池是方法区的一部分.class中除了有类的版本,字段,方法,接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放在方法区的运行时常量池 ...

  6. 配置MQTT服务器

    第一步:下载一个Xshell 链接:https://pan.baidu.com/s/16oDa5aPw3G6RIQSwaV8vqw 提取码:zsb4 打开Xshell 前往MQTT服务器软件下载地址: ...

  7. canvas(一) 基本线条绘制

    var dom = document.getElementById('canvasItem'), ctx = dom.getContext('2d'); //坐标位置默认基于 浏览器窗口(0,0),此 ...

  8. HDU 3667.Transportation 最小费用流

    Transportation Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  9. 学生管理系统(Java Swing JDBC MySQL)

    该系统使用 Java Swing.JDBC.MySQL 开发 开发环境 Eclipse.WindowBuilder JDK版本:1.8 代码在百度网盘中(176***5088) 目录结构如下 Data ...

  10. leveldb 学习记录(八) compact

    随着运行时间的增加,memtable会慢慢 转化成 sstable. sstable会越来越多 我们就需要进行整合 compact 代码会在写入查询key值 db写入时等多出位置调用MaybeSche ...