C++ 单例模式超详细讲解
C++ 单例模式(Singleton)超详细讲解
我会用最通俗、最完整 的方式,把单例模式的背景、意义、用法、优势、实现、坑点一次性讲透,新手也能完全看懂。
一、什么是单例模式?
单例模式 = 一个类,在整个程序运行期间,只能创建唯一一个对象,不能创建第二个。
简单说:
-
这个类永远只有一个实例
-
无论你在代码哪里用,拿到的都是同一个对象
-
禁止外部手动
new、禁止拷贝、禁止赋值
二、背景:为什么会出现单例模式?
在程序里,有些对象天生就应该只有一个,如果允许多个实例,会出大问题:
常见场景:
-
日志管理器(多个日志对象会导致日志混乱、重复写入)
-
配置管理器(全局配置只应该读一份)
-
数据库连接池(重复创建连接浪费资源)
-
线程池、缓存管理器、打印机管理器、游戏引擎管理器
-
全局工具类(只需要一份,不需要反复创建)
如果不控制实例数量:
-
资源浪费
-
数据不一致
-
逻辑错误
-
程序崩溃
于是单例模式 就诞生了,专门解决:
全局唯一实例 + 统一访问点
三、单例模式的核心意义
一句话总结:
保证一个类在程序生命周期内有且仅有一个实例,并提供一个全局访问点。
它解决两个核心问题:
-
全局唯一:避免重复创建、资源冲突
-
全局可访问:不用到处传递对象,随时能获取
四、单例模式的优势(为什么要用?)
1. 严格控制唯一实例
从语法层面禁止创建多个对象,从根源避免错误。
2. 节约系统资源
像数据库连接、日志、配置文件,只初始化一次,不重复消耗。
3. 全局统一访问
任何地方直接获取实例,不用层层传参,代码更简洁。
4. 延迟初始化(懒汉模式)
用到时才创建,不浪费启动时间。
5. 避免全局变量的缺点
全局变量:
-
没有封装
-
可以被随意修改
-
没有控制构造 / 析构
单例:
-
完全封装
-
可控生命周期
-
线程安全可控制
-
更优雅、更安全
五、单例模式的 4 个关键规则(必须记住)
要写一个正确的 C++ 单例,必须满足:
-
构造函数私有化 :禁止外部
new -
删除拷贝构造 + 赋值运算符:禁止拷贝、赋值
-
提供一个静态获取实例函数 :
getInstance\(\) -
唯一实例存储在静态成员变量
六、C++ 单例的两种经典实现
1. 饿汉模式(Eager)
程序一启动就创建实例
-
优点:简单、天生线程安全
-
缺点:启动稍慢
cpp
class Singleton {
public:
// 禁用拷贝和赋值
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
// 全局访问点
static Singleton& getInstance() {
return instance;
}
// 测试函数
void doSomething() {
// ...
}
private:
// 构造函数私有化
Singleton() {}
// 程序启动时就创建唯一实例
static Singleton instance;
};
// 静态成员初始化
Singleton Singleton::instance;
使用方式:
cpp
Singleton& obj = Singleton::getInstance();
obj.doSomething();
2. 懒汉模式(Lazy)------ 最常用
用到时才创建实例(延迟初始化)
C++11 之后最推荐、最简单、线程安全的写法:
cpp
class Singleton {
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton& getInstance() {
// C++11 保证 static 内部变量线程安全
static Singleton instance;
return instance;
}
void doSomething() {}
private:
Singleton() {}
};
✅ 这是现代 C++ 最优单例实现
-
线程安全
-
延迟加载
-
自动销毁
-
代码极简
七、单例模式怎么用?
cpp
// 获取单例对象
auto& logger = Singleton::getInstance();
// 使用成员函数
logger.doSomething();
// 永远是同一个对象
auto& logger2 = Singleton::getInstance();
// logger 和 logger2 是同一个实例
八、单例模式的缺点
没有任何设计模式完美,单例也有坑:
-
过度使用会造成代码耦合
-
难以单元测试(因为全局唯一,无法 mock)
-
多线程下要注意线程安全
-
生命周期不好控制(程序结束才销毁)
-
隐藏依赖关系(代码看不到参数传递)
结论:单例很好,但不要滥用。
九、单例模式适用场景
你可以放心用单例的情况:
-
日志系统
-
配置管理
-
数据库连接池
-
缓存
-
线程池
-
全局管理器(引擎、UI、音频等)
总结(超精简版)
-
单例 = 一个类只能有一个对象
-
目的:全局唯一、节约资源、统一访问
-
核心规则:私有构造 + 禁用拷贝 + 静态访问点
-
最佳实现:C++11 静态局部变量(懒汉、线程安全)
-
优势:安全、简洁、无资源浪费
-
缺点:不能滥用,会造成耦合
(注:文档部分内容可能由 AI 生成)