外观
口述最简单的实现单例的 code
⭐ 题目日期:
金山 - 2024/12/31
📝 题解:
class Singleton {
public:
// 获取单例对象的唯一接口(线程安全、延迟初始化)
static Singleton& getInstance() {
static Singleton instance; // C++11 保证静态局部变量初始化线程安全
return instance;
}
// 删除拷贝构造函数和赋值操作符
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
// 私有构造函数,禁止外部实例化
Singleton() = default;
};
口述要点解析
线程安全:
- C++11 标准规定,静态局部变量的初始化是线程安全的,无需手动加锁。编译器会自动插入线程安全代码(如原子操作或互斥锁)。
- 对比旧标准的“双重检查锁定”(DCLP),此实现更简洁且无潜在风险。
延迟初始化:
- 静态局部变量
instance
在首次调用getInstance()
时初始化,避免不必要的资源占用。
- 静态局部变量
防拷贝和防赋值:
- 显式删除拷贝构造函数和赋值操作符(
= delete
),防止通过拷贝或赋值创建新实例。
- 显式删除拷贝构造函数和赋值操作符(
构造函数私有化:
- 将默认构造函数设为私有(
private
),禁止外部直接实例化对象。
- 将默认构造函数设为私有(
常见追问与应对
为什么用静态局部变量而不是静态成员变量?
- 静态成员变量需要在类外单独初始化(违反封装),而静态局部变量通过函数访问,保证唯一性和延迟初始化。
如何保证线程安全?
- C++11 标准明确规定,静态局部变量的初始化由编译器保证线程安全,类似隐式加锁,但无性能损耗。
是否可以用指针返回单例?
- 可以(如返回
Singleton*
),但返回引用更安全,避免用户误调用delete
。
- 可以(如返回
是否需要考虑析构函数?
- 单例通常生命周期与程序一致,无需手动释放。若需资源清理,可将析构函数设为私有,或在
getInstance()
中控制。
- 单例通常生命周期与程序一致,无需手动释放。若需资源清理,可将析构函数设为私有,或在
其他实现对比(补充说明)
双重检查锁定(DCLP):
// 不推荐!旧标准下的复杂实现,C++11 后已过时 Singleton* Singleton::instance = nullptr; std::mutex Singleton::mutex; Singleton* Singleton::getInstance() { if (instance == nullptr) { // 第一次检查 std::lock_guard<std::mutex> lock(mutex); if (instance == nullptr) { // 第二次检查 instance = new Singleton(); } } return instance; }
- 缺点:需手动管理内存,旧编译器可能存在指令重排问题(需
volatile
或内存屏障),代码冗余。
- 缺点:需手动管理内存,旧编译器可能存在指令重排问题(需
饿汉式单例:
// 程序启动时立即初始化,可能浪费资源 class Singleton { static Singleton instance; // 在类外初始化 // ... };
- 缺点:无法延迟初始化,可能影响启动速度。