【设计模式】单例模式
1. 什么是单例模式
该模式的主要目的就是确保某个类有且仅有一个实例存在,在任何位置都可以通过接口获取这个唯一实例。如:
- 设备管理器:系统可以有多个设备,但是只有一个设备管理器,用于管理设备驱动;
- 数据池:用来缓存数据的数据结构,需要在一处写,多处读取,或者多处写,多处读取;
2. 单例模式的应用
-
特征
1. 确保全局只有一个实例(static private)被创建:
为此,单例类只能提供私有(private)构造函数,保证用户不能随意自定义实例;实例靠自己来创建;
2. 提供该实例的全局访问接口:
通过静态(static)公有方法,创建或者获取该静态私有对象;
3. 禁止赋值和拷贝
4. 线程安全
-
如何使用
越小越好,越简单越好,线程安全,内存不泄露。
3. 单例模式的几种实现及优缺点
- 懒汉式:懒汉很懒,只有用的时候才实例化
写法1:(基础写法,有缺陷)
class Singleton
{
private:
static Singleton* instance;
private:
Singleton() {};
~Singleton() {};
Singleton(const Singleton&) = delete; //C++11特性,不允许调用该函数
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton* getInstance()
{
if(instance == NULL)
instance = new Singleton();
return instance;
}
};
// init static member
Singleton* Singleton::instance = NULL;
这是个最基础的单例模式实现,他有那些问题呢?
- 线程安全问题,如多个线程同时尝试获取单例,判断m_pInstance都为空,实例化多个对象。解决方法:加锁
- 内存是否泄漏?只负责new出对象,却不负责delete对象,导致内存泄漏?一般而言,单例类实例都是常驻的,不需要手动释放。如果非要释放,不能在析构函数中,可以考虑增加一个destroy()方法,或者智能指针;
写法2:(安全写法,也有不足)
class Singleton { public:
typedef std::shared_ptr<Singleton> sharePtr;
~Singleton() {} Singleton(Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; static sharePtr getInstance() { //double checked lock if(!m_pInstance) { std::lock_guard<std::mutex> lk(m_mutex); if(!m_pInstance) m_pInstance = std::shared_ptr<Singleton>(new Singleton); } return m_instance_ptr; } private: Singleton() {} static sharePtr m_pInstance; static std::mutex m_mutex; }; Singleton::sharePtr Singleton::m_pInstance = NULL; std::mutex Singleton::m_mutex;
不足之处在于:
- 约束用户也要使用只能指针,非必要不约束;
- 锁也有开销,大数据量情况下,会成为严重的性能瓶颈;
- 代码增多;
- 某些平台(编译器和指令架构不同),双检锁会失效;
写法3:(最推荐写法,局部静态变量 - magic static)
class Singleton
{
private:
Singleton() {};
~Singleton() {};
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton& getInstance()
{
static Singleton instance;
return instance;
}
};
又叫做Meyers' SingletonMeyer's的单例,是著名《Effective C++》作者Meyers提出的,用到的特性是C++11中的Magic Static特性:
If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. 如果当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束。
这样保证了并发线程在获取静态局部变量的时候一定是初始化过的,所以具有线程安全性:
- 通过局部静态变量特性,保证了线程安全(C++11,GCC>4.3,VS2015)
- 不需要共享指针,代码简洁;
- 注意使用时,需要声明单例的引用才能获取对象;否则返回指针的话,无法避免用户delete导致对象提前销毁。
写法4:单例模板
待续。。。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具