什么是设计模式设计模式
设计模式表明了最佳实践,是软件开发过程当中面临通常问题的解决方案。安全
设计模式是一套被反复使用、通过分类、代码设计总结的经验。并发
单例模式ide
单例模式也叫单件模式。Singleton是一个很是经常使用的设计模式,几乎全部稍微大一些的程序都会使用到它,因此构建一个线程安全而且高效的Singleton很重要。函数
1. 单例类保证全局只有一个惟一实例对象。高并发
2. 单例类提供获取这个惟一实例的接口。优化
怎样设计一个单例模式spa
实现一(不考虑线程安全)线程
class Singleton { public: // 获取惟一对象实例的接口函数 static Singleton* GetInstance() { if(_sInstance == NUL) { if (_sInstance == NULL) { _sInstance = new Singleton(); } } return _sInstance; } // 删除实例对象 static void DelInstance() { if (_sInstance) { delete _sInstance; _sInstance = NULL; } } void Print() { cout<<_data<<endl; } private: // 构造函数定义为私有,限制只能在类内建立对象 Singleton() :_data(0) {} Singleton(const Singleton&); Singleton& operator=(const Singleton&); // 指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例 static Singleton* _sInstance; // 单例类里面的数据 int _data; }; Singleton* Singleton::_sInstance = NULL; void TestSingleton() { Singleton::GetInstance()->Print(); Singleton::DelInstance(); }
实现二设计
线程安全的单例 -- (懒汉模式-- lazy loading)
ps: 下面部分的加锁使用了C++11库的互斥锁 class Singleton { public: // 获取惟一对象实例的接口函数 static Singleton* GetInstance() { // 使用双重检查,提升效率,避免高并发场景下每次获取实例对象都进行加锁 if (_sInstance == NULL) { std::lock_guard<std::mutex> lck(_mtx); if (_sInstance == NULL) { // tmp = new Singleton()分为如下三个部分 // 1.分配空间 2.调用构造函数 3.赋值 // 编译器编译优化可能会把2和3进行指令重排,这样可能会致使 // 高并发场景下,其余线程获取到未调用构造函数初始化的对象 // 如下加入内存栅栏进行处理,防止编译器重排栅栏后面的赋值 // 到内存栅栏以前 Singleton* tmp = new Singleton(); MemoryBarrier(); _sInstance = tmp; } } return _sInstance; } // 删除实例对象 static void DelInstance() { std::lock_guard<std::mutex> lck(_mtx); if (_sInstance) { delete _sInstance; _sInstance = NULL; } } void Print() { cout<<_data<<endl; } private: // 构造函数定义为私有,限制只能在类内建立对象 Singleton() :_data(0) {} Singleton(const Singleton&); Singleton& operator=(const Singleton&); // 指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例 static Singleton* _sInstance; // 保证线程安全的互斥锁 static mutex _mtx; // 单例类里面的数据 int _data; }; Singleton* Singleton::_sInstance = NULL; mutex Singleton::_mtx; void TestSingleton() { Singleton::GetInstance()->Print(); Singleton::DelInstance(); }
实现三
线程安全的单例 -- (饿汉模式--简洁、高效、不用加锁、可是在某些场景下会有缺陷)
方法1
// 方式一 class Singleton { public: // 获取惟一对象实例的接口函数 static Singleton* GetInstance() { static Singleton sInstance; return &sInstance; } void Print() { cout<<_data<<endl; } private: // 构造函数定义为私有,限制只能在类内建立对象 Singleton() :_data(0) {} Singleton(const Singleton&); Singleton& operator=(const Singleton&); // 单例类里面的数据 int _data; }; void TestSingleton() { Singleton::GetInstance()->Print(); }
方法2
// 方式二 class Singleton { public: // 获取惟一对象实例的接口函数 static Singleton* GetInstance() { assert(_sInstance); return _sInstance; } // 删除实例对象 static void DelInstance() { if (_sInstance) { delete _sInstance; _sInstance = NULL; } } void Print() { cout << _data << endl; } private: // 构造函数定义为私有,限制只能在类内建立对象 Singleton() :_data(0) {} Singleton(const Singleton&); Singleton& operator=(const Singleton&); // 指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例 static Singleton* _sInstance; // 单例类里面的数据 int _data; }; Singleton* Singleton::_sInstance = new Singleton; void TestSingleton() { Singleton::GetInstance()->Print(); Singleton::DelInstance(); }
带RAII GC 自动回收实例对象的方式
class Singleton { public: // 获取惟一对象实例的接口函数 static Singleton* GetInstance() { assert(_sInstance); return _sInstance; } // 删除实例对象 static void DelInstance() { if (_sInstance) { delete _sInstance; _sInstance = NULL; } } void Print() { cout << _data << endl; } class GC { public: ~GC() { cout << "DelInstance()"<<endl; DelInstance(); } }; private: // 构造函数定义为私有,限制只能在类内建立对象 Singleton() :_data(0) {} // 指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例 static Singleton* _sInstance; // 单例类里面的数据 int _data; }; // 静态对象在main函数以前初始化,这时只有主线程运行,因此是线程安全的。 Singleton* Singleton::_sInstance = new Singleton; // 使用RAII,定义全局的GC对象释放对象实例 Singleton::GC gc; void TestSingleton() { Singleton::GetInstance()->Print(); }
因为程序在结束的时候,系统会自动析构全部的全局变量,实际上,系统也会析构全部类的静态成员变量,就像这些静态变量是全局变量同样。咱们知道,静态变量和全局变量在内存中,都是存储在静态存储区的,因此在析构时,是同等对待的。