分类
懒汉式:实例对象在第一次被使用时才进行初始化。
饿汉式:实例在定义时就被初始化。
特点
1、构造函数和析构函数私有化,不允许外部创建实例对象。
2、拷贝构造函数和复制运算符重载被delete,不允许产生新的实例。
3、内部定义一个私有的静态数据成员,该成员为本类的实例化对象。
4、提供公有静态成员方法获取该静态对象。
懒汉式
单线程懒汉式实现
1 class Singleton 2 { 3 public: 4 static Singleton* Ins() 5 { 6 if (_ins == nullptr) 7 _ins = new Singleton(); 8 return _ins; 9 } 10 11 protected: 12 Singleton() {} 13 ~Singleton() {} 14 Singleton(const Singleton&) = delete; 15 Singleton& operator=(const Singleton&) = delete; 16 17 private: 18 static Singleton* _ins; 19 };
缺陷:
- 非线程安全:假设目前有2个线程同时访问Singleton::Ins方法,线程A在if条件判断为真后,失去时间片,此时_ins还未初始化。线程B访问Singleton::Ins,由于在A线程中还未初始化_ins,导致B线程创建对象并将_ins初始化完成。此时,时间片再次回到A线程,还原现场,上一个时间片中已经判断过if条件,_ins将调用new创建新对象实例,导致对象被创建两次,内存泄漏。
- 内存泄漏:在类中,我们只负责new了一个对象,并未对其delete,导致内存泄漏。
下面将用双重锁+智能指针解决上面缺陷。
双重锁+智能指针实现
1 class Singleton 2 { 3 public: 4 ~Singleton() { std::cout << "destructor" << std::endl; } //必须声明为public 5 6 static std::shared_ptr<Singleton> Ins() 7 { 8 if (_ins == nullptr) 9 { 10 std::lock_guard<std::mutex> lock(_mt); 11 if (_ins == nullptr) 12 { 13 _ins = std::shared_ptr<Singleton>(new Singleton()); 14 } 15 } 16 return _ins; 17 } 18 19 Singleton(const Singleton&) = delete; 20 Singleton& operator=(const Singleton&) = delete; 21 22 protected: 23 Singleton() { std::cout << "constructor" << std::endl; } 24 25 private: 26 static std::shared_ptr<Singleton> _ins; 27 static std::mutex _mt; 28 };
缺点:
- 双重锁在特定环境下依旧是非线程安全的。
- 强制用户使用智能指针,并且要求将析构函数定义为piblic,可能在实际使用中忽略了该问题导致程序编译出错。
局部静态变量法(推荐)
1 class Singleton 2 { 3 public: 4 static Singleton& Ins() 5 { 6 static Singleton _ins; 7 return _ins; 8 } 9 10 Singleton(const Singleton&) = delete; 11 Singleton& operator=(const Singleton&) = delete; 12 13 protected: 14 Singleton() { std::cout << "constructor" << std::endl; } 15 ~Singleton() { std::cout << "destructor" << std::endl; } 16 };
C++11标准中定义了一个Magic Static特性:如果变量当前处于初始化状态,当发生并发访问时,并发线程将会阻塞,等待初始化结束。
std::call_once + 内部类方法
1 class Singleton 2 { 3 public: 4 static Singleton* Ins() 5 { 6 std::call_once(_flag, []() { 7 _ins = new Singleton; 8 }); 9 return _ins; 10 } 11 12 Singleton(const Singleton&) = delete; 13 Singleton& operator=(const Singleton&) = delete; 14 15 protected: 16 Singleton() { std::cout << "constructor" << std::endl; } 17 ~Singleton() { std::cout << "destructor" << std::endl; } //必须声明为私有,否则返回指针将可析构 18 19 private: 20 struct Deleter 21 { 22 ~Deleter() { 23 delete _ins; 24 _ins = nullptr; 25 } 26 }; 27 static Deleter _deleter; 28 static Singleton* _ins; 29 static std::once_flag _flag; 30 }; 31 32 Singleton::Deleter Singleton::_deleter; 33 Singleton* Singleton::_ins = nullptr; 34 std::once_flag Singleton::_flag;
缺点:不美观,在类内部定义内部类,头文件臃肿,适用于.h和.cpp分离的情况下使用。
饿汉式
1 class Singleton 2 { 3 public: 4 static Singleton& Ins() 5 { 6 return _ins; 7 } 8 9 Singleton(const Singleton&) = delete; 10 Singleton& operator=(const Singleton&) = delete; 11 12 protected: 13 Singleton() { std::cout << "constructor" << std::endl; } 14 ~Singleton() { std::cout << "destructor" << std::endl; } 15 16 private: 17 static Singleton _ins; 18 }; 19 20 Singleton Singleton::_ins;
模板单例
主要解决系统中存在多个模块需要使用单例模式,通过模板+继承解决代码冗余。
1 template<typename T> 2 class Singleton 3 { 4 public: 5 static T& Ins() 6 { 7 static T _ins; 8 return _ins; 9 } 10 11 protected: 12 Singleton() { std::cout << "constructor" << std::endl; } 13 ~Singleton() { std::cout << "destructor" << std::endl; } 14 Singleton(const Singleton&) = delete; 15 Singleton& operator=(const Singleton&) = delete; 16 }; 17 18 class AppInstance : public Singleton<AppInstance> 19 { 20 friend Singleton<AppInstance>; //声明友元 21 AppInstance() {} //必须私有化 22 ~AppInstance() {} 23 public: 24 void func() { std::cout << "func"<< std::endl; } 25 };
总结
除非频繁访问或者频繁创建和销毁,尽量不要使用单例模式,可以用组合的方式代替。
在使用单例模式,需要确保线程安全、内存安全。设计时,尽量做到好用、简单。
标签:总结,std,Singleton,const,c++,ins,线程,单例,delete From: https://www.cnblogs.com/BroccoliFighter/p/17675445.html