定义
只提供唯一一个类的实例, 具有全局变量的特点, 在任何位置都可以通过接口获取到那个唯一实例
具体运用场景
1. 设备管理器, 系统中可能有多个设备, 但是只有一个设备管理器, 用于管理设备驱动;
2. 数据池, 用来缓存数据的数据结构, 需要在一处写, 多处读取或者多处写, 多处读取;
要点
1. 全局只有一个实例: static 特性, 同时禁止用户自己声明并定义实例 (把构造函数设为 private)
2. 禁止赋值和拷贝
3. 用户通过接口获取实例: 使用 static 类成员函数
4. 线程安全
实现
示例一, 没有考虑线程安全的方式
- class Singleton{
- private:
- Singleton(){
- //......
- }
- Singleton(Singleton&);
- Singleton& operator=(const Singleton&);
- static Singleton* instance_ptr;
- public:
- ~Singleton(){
- //.....
- }
- static Singleton* get_instance(){
- if(instance_ptr==nullptr){
- instance_ptr = new Singleton;
- }
- return instance_ptr;
- }
- };
- Singleton* Singleton::instance_ptr = nullptr;
- int main(){
- Singleton* instance = Singleton::get_instance();
- return 0;
- }
线程安全: 上述中, 可能会造成多个线程对共享内存数据访问的竞争条件的形成: 即, 第一个线程在访问 if 语句时判断 instance_ptr 为空, 开始实例化单例. 同时, 第二个线程访问单例, 判断到 if 还是为空, 也开始实例化单例.
C++ 标准中对数据竞争的定义是: 多个线程并发的去修改一个独立对象, 数据竞争是未定义行为的起因.
内存泄漏: 这个类没有负责 delete 对象, 需要使用者自己 delete, 因此存在内存泄漏的风险.
示例二, 线程安全的方式
- //include<mutex>
- class Singleton{
- private:
- Singleton(){
- //......
- }
- Singleton(Singleton&);
- Singleton& operator=(const Singleton&);
- static Singleton* instance_ptr;
- static std::mutex m_mutex;
- public:
- ~Singleton(){
- //.....
- }
- static Singleton* get_instance(){
- if(instance_ptr==nullptr){
- std::lock_guard<std::mutex> lk(m_mutex);
- if(instance_ptr==nullptr) {
- instance_ptr = new Singleton;
- }
- }
- return instance_ptr;
- }
- };
- // init static member
- Singleton* Singleton::instance_ptr = nullptr;
- int main(){
- Singleton* instance = Singleton::get_instance();
- return 0;
- }
加锁, 这里使用了两个 if 判断语句的技术称为双检锁; 好处是, 只有判断指针为空的时候才加锁, 避免每次调用 get_instance 都加锁.
互斥: 是指散布在不同进程之间的若干程序片断, 当某个进程运行其中一个程序片段时, 其它进程就不能运行它们之中的任一程序片段, 只能等到该进程运行完这个程序片段后才可以运行. 如果用对资源的访问来定义的话, 互斥某一资源同时只允许一个访问者对其进行访问, 具有唯一性和排它性. 但互斥无法限制访问者对资源的访问顺序, 即访问是无序的.
看起来很美好, 但这样是有问题的, 在某些平台, 双重检查锁定模式会失效! 具体可以看这篇文章
示例三, c++11 中的静态局部变量的线程安全
- class Singleton{
- private:
- Singleton(){
- //......
- }
- Singleton(Singleton&);
- Singleton& operator=(const Singleton&);
- public:
- ~Singleton(){
- //.....
- }
- static Singleton& get_instance(){
- static Singleton instance;
- return instance;
- }
- };
这种方法叫做 Meyers' Singleton , 是 Meyers 提出的. 所用到的特性是在 C++11 标准中的 Magic Static 特性:
如果当变量在初始化的时候, 并发同时进入声明语句, 并发线程将会阻塞等待初始化结束.
来源: http://www.bubuko.com/infodetail-3395516.html