什么是设计模式

设计模式代表了最佳实践,是软件开发过程中面临一般问题的解决方案。

设计模式是一套被反复使用、经过分类、代码设计总结的经验。

单例模式

单例模式也叫单件模式。Singleton是一个非常常用的设计模式,几乎所有稍微大一些的程序都会使用到它,所以构建一个线程安全并且高效的Singleton很重要。

1. 单例类保证全局只有一个唯一实例对象。

2. 单例类提供获取这个唯一实例的接口。

怎样设计一个单例模式


实现一(不考虑线程安全)

classSingleton{public://获取唯一对象实例的接口函数staticSingleton*GetInstance(){if(_sInstance==NUL){if(_sInstance==NULL){_sInstance=newSingleton();}}return_sInstance;}//删除实例对象staticvoidDelInstance(){if(_sInstance){delete_sInstance;_sInstance=NULL;}}voidPrint(){cout<<_data<<endl;}private://构造函数定义为私有,限制只能在类内创建对象Singleton():_data(0){}Singleton(constSingleton&);Singleton&operator=(constSingleton&);//指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例staticSingleton*_sInstance;//单例类里面的数据int_data;};Singleton*Singleton::_sInstance=NULL;voidTestSingleton(){Singleton::GetInstance()->Print();Singleton::DelInstance();}

实现二

线程安全的单例 -- (懒汉模式-- lazy loading)

ps:下面部分的加锁使用了C++11库的互斥锁classSingleton{public://获取唯一对象实例的接口函数staticSingleton*GetInstance(){//使用双重检查,提高效率,避免高并发场景下每次获取实例对象都进行加锁if(_sInstance==NULL){std::lock_guard<std::mutex>lck(_mtx);if(_sInstance==NULL){//tmp=newSingleton()分为以下三个部分//1.分配空间2.调用构造函数3.赋值//编译器编译优化可能会把2和3进行指令重排,这样可能会导致//高并发场景下,其他线程获取到未调用构造函数初始化的对象//以下加入内存栅栏进行处理,防止编译器重排栅栏后面的赋值//到内存栅栏之前Singleton*tmp=newSingleton();MemoryBarrier();_sInstance=tmp;}}return_sInstance;}//删除实例对象staticvoidDelInstance(){std::lock_guard<std::mutex>lck(_mtx);if(_sInstance){delete_sInstance;_sInstance=NULL;}}voidPrint(){cout<<_data<<endl;}private://构造函数定义为私有,限制只能在类内创建对象Singleton():_data(0){}Singleton(constSingleton&);Singleton&operator=(constSingleton&);//指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例staticSingleton*_sInstance;//保证线程安全的互斥锁staticmutex_mtx;//单例类里面的数据int_data;};Singleton*Singleton::_sInstance=NULL;mutexSingleton::_mtx;voidTestSingleton(){Singleton::GetInstance()->Print();Singleton::DelInstance();}

实现三

线程安全的单例 -- (饿汉模式--简洁、高效、不用加锁、但是在某些场景下会有缺陷)

方法1

//方式一classSingleton{public://获取唯一对象实例的接口函数staticSingleton*GetInstance(){staticSingletonsInstance;return&sInstance;}voidPrint(){cout<<_data<<endl;}private://构造函数定义为私有,限制只能在类内创建对象Singleton():_data(0){}Singleton(constSingleton&);Singleton&operator=(constSingleton&);//单例类里面的数据int_data;};voidTestSingleton(){Singleton::GetInstance()->Print();}

方法2

//方式二classSingleton{public://获取唯一对象实例的接口函数staticSingleton*GetInstance(){assert(_sInstance);return_sInstance;}//删除实例对象staticvoidDelInstance(){if(_sInstance){delete_sInstance;_sInstance=NULL;}}voidPrint(){cout<<_data<<endl;}private://构造函数定义为私有,限制只能在类内创建对象Singleton():_data(0){}Singleton(constSingleton&);Singleton&operator=(constSingleton&);//指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例staticSingleton*_sInstance;//单例类里面的数据int_data;};Singleton*Singleton::_sInstance=newSingleton;voidTestSingleton(){Singleton::GetInstance()->Print();Singleton::DelInstance();}

带RAII GC 自动回收实例对象的方式

classSingleton{public://获取唯一对象实例的接口函数staticSingleton*GetInstance(){assert(_sInstance);return_sInstance;}//删除实例对象staticvoidDelInstance(){if(_sInstance){delete_sInstance;_sInstance=NULL;}}voidPrint(){cout<<_data<<endl;}classGC{public:~GC(){cout<<"DelInstance()"<<endl;DelInstance();}};private://构造函数定义为私有,限制只能在类内创建对象Singleton():_data(0){}//指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例staticSingleton*_sInstance;//单例类里面的数据int_data;};//静态对象在main函数之前初始化,这时只有主线程运行,所以是线程安全的。Singleton*Singleton::_sInstance=newSingleton;//使用RAII,定义全局的GC对象释放对象实例Singleton::GCgc;voidTestSingleton(){Singleton::GetInstance()->Print();}

由于程序在结束的时候,系统会自动析构所有的全局变量,实际上,系统也会析构所有类的静态成员变量,就像这些静态变量是全局变量一样。我们知道,静态变量和全局变量在内存中,都是存储在静态存储区的,所以在析构时,是同等对待的。