创建型模式-单例模式
目录创建型模式-单例模式1. 意图2. 动机3. 解决方法4. 单例模式结构5. 实现5.1 懒汉式-单线程-手动释放版5.2 懒汉式-单线程-自动释放版5.3 懒汉式-多线程版5.4 懒汉式-Meyer's Singleton(推荐)5.5 饿汉式5.6 宏实现版本5.7 模板版本5.8 宏+模板(推荐)6. 使用场景和优缺点7. 参考
1. 意图
意图是模式的简短、精炼的一句话总结,回答“这个模式是什么”的问题。
单例模式是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。
2. 动机
动机是解释为什么需要这个模式,描述模式要解决的具体问题和场景。
对于一些类中,只有一个实例是很重要的。例如一个系统中,只应该有一个文件系统和窗口管理器,
3. 解决方法
所有单例的实现都包含以下两个相同的步骤:
将默认构造函数设为私有或者保护, 防止其他对象使用单例类的 new运算符。
新建一个静态构建方法作为构造函数。 该函数会 “偷偷” 调用私有构造函数来创建对象, 并将其保存在一个静态成员变量中。 此后所有对于该函数的调用都将返回这一缓存对象。
如果你的代码能够访问单例类, 那它就能调用单例类的静态方法。 无论何时调用该方法, 它总是会返回相同的对象。
4. 单例模式结构
5. 实现
5.1 懒汉式-单线程-手动释放版
"懒"的体现:“节能体现”,只在第一次调用 getInstance() 时才创建实例,不到万不得已(真正需要)绝不创建对象。
class USingleton
{
public:
// 全局访问点
static USingleton* getInstance()
{
if (instance == nullptr)
{
instance = new USingleton();
}
return instance;
}
void doSomething()
{
std::cout << "Doing something..." << std::endl;
}
// 清理资源(可选)
static void destroy()
{
delete instance;
instance = nullptr;
}
private:
static USingleton* instance;
// 私有构造函数
USingleton() = default;
// 私有拷贝构造函数和赋值运算符
USingleton(const USingleton&) = delete;
USingleton& operator=(const USingleton&) = delete;
};
/// xxx.cpp
// 静态成员初始化
USingleton* USingleton::instance = nullptr;
特性
懒加载:首次调用getInstance()时才创建实例
简单直观:最基础的单例实现
内存泄漏风险:需要手动调用destroy()清理
优点
缺点
✅ 实现简单:代码简洁,易于理解
❌ 非线程安全:多线程下可能创建多个实例
✅ 延迟初始化:节省启动时资源占用
❌ 内存泄漏风险:C++中需手动管理内存
✅ 代码直观:适合教学单例模式原理
❌ 违反RAII:不符合C++资源管理最佳实践
✅ 灵活控制:可控制初始化时机
❌ 异常不安全:构造函数异常可能导致状态不一致
❌ 效率问题:线程安全版本需加锁影响性能
5.2 懒汉式-单线程-自动释放版
借助类成员变量(内部类,完成内部类自动析构调用)。
class USingleton
{
public:
// 全局访问点
static USingleton* getInstance()
{
if (instance == nullptr)
{
instance = new USingleton();
std::cout << "construct ..." << std::endl;
}
return instance;
}
void doSomething()
{
std::cout << "Doing something..." << std::endl;
}
private:
// 内部类,用于垃圾回收
class GC
{
public:
~GC()
{
if (instance != NULL)
{
delete instance;
instance = nullptr;
std::cout << "destroy ..." << std::endl;
}
}
};
static GC gc;
private:
static USingleton* instance;
// 私有构造函数
USingleton() = default;
// 私有拷贝构造函数和赋值运算符
USingleton(const USingleton&) = delete;
USingleton& operator=(const USingleton&) = delete;
};
/// xxx.cpp
// 在程序启动前就创建实例
USingleton* USingleton::instance = nullptr;
//全局静态变量,会被自动销毁,从而实现对单例的垃圾回收
USingleton::GC USingleton::gc;
5.3 懒汉式-多线程版
双重检查锁定版本-关键实现代码:
class USingleton
{
public:
// 全局访问点
static USingleton* getInstance()
{
// 第一次检查(无锁)
if (instance == nullptr) {
// 获取锁
std::lock_guard
// 第二次检查(持有锁)
if (instance == nullptr)
{
instance = new USingleton();
std::cout << "construct ..." << std::endl;
}
}
return instance;
}
void doSomething()
{
std::cout << "Doing something..." << std::endl;
}
// 清理资源(可选)
static void destroy()
{
std::lock_guard
if (instance != nullptr)
{
delete instance;
instance = nullptr;
std::cout << "~destory ..." << std::endl;
}
}
private:
static USingleton* instance;
static std::mutex mutex;
// 私有构造函数
USingleton() = default;
// 私有拷贝构造函数和赋值运算符
USingleton(const USingleton&) = delete;
USingleton& operator=(const USingleton&) = delete;
};
/// xxx.cpp
// 静态成员初始化
USingleton* USingleton::instance = nullptr;
std::mutex USingleton::mutex;
5.4 懒汉式-Meyer's Singleton(推荐)
这是一种基于局部静态变量的懒汉式单例实现,由C++专家Scott Meyers在《Effective C++》中提出,被认为是现代C++中最优雅的单例实现。
在单例模式中,坚持使用返回引用(Meyer's方式),除非有特殊需求(如需要兼容C接口)。不要因为"可以转换"就随意混用(返回指针形式),保持代码的清晰性和一致性更重要。
class USingleton
{
public:
// 全局访问点
static USingleton& getInstance()
{
static USingleton s_us_inst;
return s_us_inst;
}
void doSomething()
{
std::cout << "Doing something..." << std::endl;
}
private:
// 私有构造函数
USingleton() = default;
// 私有拷贝构造函数和赋值运算符
USingleton(const USingleton&) = delete;
USingleton& operator=(const USingleton&) = delete;
};
5.5 饿汉式
饿汉式最核心的体现就是 "时间确定性"。它在程序生命周期的最早期(main函数执行之前)就完成初始化,这种设计哲学认为:
"已知的坏消息优于未知的惊喜":宁愿在启动时就知道失败,也不要在业务高峰时突然崩溃
"确定的延迟优于不确定的等待":把初始化延迟明确放在启动阶段,而不是隐藏在业务调用中
"可控的启动过程优于随机的运行时行为":系统管理员可以明确知道"程序要么完全启动成功,要么完全失败"
class USingleton
{
public:
// 全局访问点
static USingleton& getInstance()
{
return s_us_inst;
}
void doSomething()
{
std::cout << "Doing something..." << std::endl;
}
private:
// 私有构造函数
USingleton() = default;
static USingleton s_us_inst;
// 私有拷贝构造函数和赋值运算符
USingleton(const USingleton&) = delete;
USingleton& operator=(const USingleton&) = delete;
};
/// xxx.cpp 在程序启动前就创建实例
USingleton USingleton::s_us_inst;
5.6 宏实现版本
针对单例的实现,如果程序中存在多个,那么其实现大致相同,导致重复代码的出现。可以使用模板或者宏的方式来解决重复度。
// singleton_macro_simple.h
#ifndef SINGLETON_MACRO_SIMPLE_H
#define SINGLETON_MACRO_SIMPLE_H
#include
// 声明和实现在一起(适合头文件只有的情况)
#define SINGLETON(classname) \
private: \
static classname* instance; \
static std::mutex mtx; \
classname() {} \
classname(const classname&) = delete; \
classname& operator=(const classname&) = delete; \
public: \
static classname* getInstance() { \
if (instance == nullptr) { \
std::lock_guard
if (instance == nullptr) { \
instance = new classname(); \
} \
} \
return instance; \
} \
static void destroy() { \
std::lock_guard
if (instance != nullptr) { \
delete instance; \
instance = nullptr; \
} \
}
// 在cpp文件中定义静态成员
#define SINGLETON_INSTANCE(classname) \
classname* classname::instance = nullptr; \
std::mutex classname::mtx;
#endif
/// 测试类
class USingleton
{
SINGLETON(USingleton)
public:
void doSomething()
{
std::cout << "Doing something..." << std::endl;
}
};
/// xxx.cpp
#include "TestSingleton.h"
SINGLETON_INSTANCE(USingleton)
5.7 模板版本
// 更好的方案:使用模板而不是宏
template
class Singleton {
protected:
Singleton() = default;
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static T& getInstance() {
static std::once_flag initFlag;
static T* instance = nullptr;
std::call_once(initFlag, []() {
instance = new T();
});
return *instance;
}
};
/// 测试类
class USingleton : public Singleton
{
public:
void doSomething()
{
std::cout << "Doing something..." << std::endl;
}
};
5.8 宏+模板(推荐)
// 首先需要定义Singleton模板类
template
class Singleton {
public:
static T& getInstance()
{
static T s_instance;
return s_instance;
}
protected:
Singleton() = default;
virtual ~Singleton() = default;
// 禁止拷贝和赋值
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
// 宏定义
#define SINGLETON_DECLARE(ClassName) \
private: \
ClassName(); \
ClassName(const ClassName&) = delete; \
ClassName& operator=(const ClassName&) = delete; \
friend class Singleton
public: \
static ClassName& getInstance() { \
return Singleton
} \
static ClassName* pointer() { \
return &getInstance(); \
}
/// 测试类
class USingleton
{
SINGLETON_DECLARE(USingleton)
public:
void doSomething()
{
std::cout << "Doing something..." << std::endl;
}
};
/// xxx.cpp
#include "TestSingleton.h"
USingleton::USingleton()
{
std::cout << "Hello, World" << std::endl;
}
注意,如果在宏定义中, ClassName()声明了没有实现,则需要在对应的cpp中实现构造函数。
6. 使用场景和优缺点
如果程序中的某个类对于所有客户端只有一个可用的实例, 可以使用单例模式。
如果你需要更加严格地控制全局变量, 可以使用单例模式。
优缺点对比总结
方面
优势
劣势
实例控制
✅ 严格保证唯一性
❌ 灵活性受限
访问便利性
✅ 全局直接访问
❌ 可能造成过度使用
资源管理
✅ 延迟初始化节省资源
❌ 多线程下资源竞争
设计原则
-
❌ 违反单一职责原则
代码质量
-
❌ 可能掩盖设计缺陷
可测试性
-
❌ 单元测试困难
7. 参考
书籍:《设计模式:可复用面向对象软件的基础》;
单例的模版+宏的实现 | 公孙二狗
AI: DeepSeek