单例模式(Singleton Pattern)
单例模式属于创建型设计模式,核心目的就是:
一个类只允许创建一个实例,并提供一个全局访问这个实例的入口。
你之前 Qt 项目里经常使用的:
Singleton::getInstance();
Singleton::getInstance();
就是典型的单例模式。
1. 为什么需要单例?
假设程序中有一个全局的 ProgramSetting:
class ProgramSetting
{
public:
int nCenterPointX;
int nCenterPointY;
};
如果普通创建:
ProgramSetting ps1;
ProgramSetting ps2;
那么:
ps1
↓
一份配置
ps2
↓
另一份配置
修改:
ps1.nCenterPointX = 100;
并不会影响:
ps2.nCenterPointX
但是有些对象在整个程序中本来就应该只有一份,例如:
程序配置
日志管理器
数据库连接管理器
设备管理器
全局状态管理器
线程管理器
网络管理器
这时候可以使用单例。
2. 最简单的单例
一个经典实现:
class Singleton
{
public:
static Singleton* getInstance()
{
if (m_instance == nullptr)
{
m_instance = new Singleton;
}
return m_instance;
}
private:
Singleton() {}
~Singleton() {}
private:
static Singleton* m_instance;
};
Singleton* Singleton::m_instance = nullptr;
使用:
Singleton* s1 = Singleton::getInstance();
Singleton* s2 = Singleton::getInstance();
此时:
s1 == s2
结果是:
true
也就是说:
getInstance()
│
▼
┌──────────────┐
│ Singleton │
│ 唯一对象 │
└──────────────┘
▲ ▲
│ │
s1 s2
3. 为什么构造函数要 private?
这是单例最关键的地方之一。
private:
Singleton() {}
这样外部就不能:
Singleton s;
也不能:
Singleton* s = new Singleton;
因为构造函数是 private。
外部只能:
Singleton::getInstance();
所以:
把构造函数隐藏起来,防止外部创建对象。
4. Qt/C++中更推荐的写法
现代 C++ 中可以直接使用局部静态变量:
class Singleton
{
public:
static Singleton& getInstance()
{
static Singleton instance;
return instance;
}
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
使用:
Singleton& s = Singleton::getInstance();
或者:
Singleton::getInstance().xxx();
这种方式的优点是:
不需要 new
不需要手动 delete
生命周期由 C++ 管理
C++11 起局部静态对象初始化具有线程安全保证
5. 你项目中的 Singleton<T>
你之前代码里有:
MotorStatus* ms = Singleton::getInstance();
ProgramSetting* ps = Singleton::getInstance();
这种实际上是模板单例。
可以自己实现一个:
template<class T>
class Singleton
{
public:
static T* getInstance()
{
static T instance;
return &instance;
}
private:
Singleton() = delete;
};
不过这里其实不能这么简单写,因为 T 的构造权限需要考虑。
更常见的写法:
template<typename T>
class Singleton
{
public:
static T* getInstance()
{
static T instance;
return &instance;
}
};
要求:
T
的构造函数能够被访问。
例如:
class ProgramSetting
{
public:
ProgramSetting()
{
}
int nCenterPointX = 0;
int nCenterPointY = 0;
};
然后:
ProgramSetting* ps1 =
Singleton::getInstance();
ProgramSetting* ps2 =
Singleton::getInstance();
两者指向同一个对象:
ps1 == ps2
6. Qt项目中的实际例子
例如你的程序有一个:
ProgramSetting
负责保存程序配置。
可以:
class ProgramSetting
{
public:
static ProgramSetting* instance()
{
static ProgramSetting obj;
return &obj;
}
int nCenterPointX = 0;
int nCenterPointY = 0;
private:
ProgramSetting() = default;
ProgramSetting(const ProgramSetting&) = delete;
ProgramSetting& operator=(const ProgramSetting&) = delete;
};
任何地方:
ProgramSetting* ps =
ProgramSetting::instance();
ps->nCenterPointX = 500;
其他类:
ProgramSetting* ps =
ProgramSetting::instance();
qDebug() << ps->nCenterPointX;
得到:
500
因为整个程序只有一个 ProgramSetting。
7. 单例模式的结构
可以把单例理解成三个关键点:
Singleton
│
┌──────────┴──────────┐
│ │
private构造函数 getInstance()
│ │
│ ▼
│ 唯一的对象
│ │
└─────────────────────┘
也就是:
① 私有构造
private:
Singleton();
防止外部:
new Singleton;
② 唯一实例
static Singleton instance;
保证只创建一个。
③ 全局访问入口
static Singleton& getInstance();
所有地方通过它获取对象。
8. 单例模式和全局变量有什么区别?
看起来很像:
GlobalData g_data;
和:
Singleton::getInstance();
但是单例可以把对象的:
创建
生命周期
访问方式
初始化
封装起来。
例如:
class LogManager
{
public:
static LogManager& instance();
void writeLog(const QString& text);
private:
LogManager();
};
外部不用关心:
什么时候创建?
什么时候销毁?
是否已经创建?
只需要:
LogManager::instance().writeLog(“hello”);
9. 单例模式的一个重要问题
单例非常方便,但不要什么东西都做成单例。
例如:
Singleton<Camera>::getInstance();
Singleton<Motor>::getInstance();
Singleton<ProgramSetting>::getInstance();
Singleton<Database>::getInstance();
Singleton<Network>::getInstance();
Singleton<Worker>::getInstance();
Singleton<Widget>::getInstance();
如果整个项目大量使用单例,会产生:
A
│
├── Singleton B
│
├── Singleton C
│
├── Singleton D
│
└── Singleton E
最后类之间产生大量隐式依赖。
例如:
void Camera::start()
{
ProgramSetting::getInstance()->xxx;
MotorStatus::getInstance()->xxx;
LogManager::getInstance()->xxx;
NetworkManager::getInstance()->xxx;
}
这个类就和很多全局对象绑定在一起了。
测试也会变得困难。
10. 单例模式和普通类最大的区别
普通类:
Camera camera1;
Camera camera2;
Camera camera3;
可以有:
Camera1
Camera2
Camera3
单例:
Camera::instance();
Camera::instance();
Camera::instance();
得到:
┌─────────────┐
│ Camera │
│ 唯一实例 │
└─────────────┘
▲ ▲ ▲
│ │ │
① ② ③
11. 单例模式和其他设计模式的关系
你最近学习的这些模式,可以这样区分:
模式主要解决的问题
工厂怎么创建对象
单例对象只能有一个
适配器接口不兼容
装饰器动态增加功能
外观隐藏复杂子系统
代理控制对象访问
桥接抽象和实现分离
组合树形结构统一处理
享元共享大量对象,节省资源
职责链请求沿链传递处理
备忘录保存/恢复对象状态
其中单例属于创建型模式。
12. 用你项目里的代码理解
You often write code like this:
MotorStatus* ms =
Singleton<MotorStatus>::getInstance();
ProgramSetting* ps =
Singleton<ProgramSetting>::getInstance();
可以把它理解成:
程序
│
┌──────────┴──────────┐
│ │
MotorStatus ProgramSetting
单例 单例
│ │
┌────┴────┐ ┌────┴────┐
│ │ │ │
FormA Worker FormB Camera
所有模块拿到的都是同一个 MotorStatus 和同一个 ProgramSetting。
所以你项目中:
ps->nCenterPointX
这种全局配置数据非常适合用单例来管理。
一句话记忆:
单例模式 = 把“只能有一个的对象”封装起来,并提供统一的访问入口




![打卡信奥刷题(3584)用C++实现信奥题 P11523 [THUPC 2025 初赛] 摊位分配-171主机测评](https://www.171host.com/wp-content/uploads/2026/09/20260922020544-6ab1e2783b78e-220x150.png)