单例模式是设计模式中最简单也最常用的模式之一。它的核心思想是:确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。
无论是哪种单例,都必须遵循以下两个关键原则:
-
私有化构造函数: 将构造函数声明为
private,防止外部直接使用new或直接创建对象。 -
禁用拷贝/赋值: 禁用拷贝构造函数和赋值运算符(使用
= delete),确保实例的唯一性
一、饿汉单例模式
原理:_utils_instance 被声明为类的 static 成员,并在类外部定义时被初始化。
静态成员在程序启动时(main 函数执行之前)就会被创建。
特点:
1.线程安全:实例是在主线程启动之前完成初始化的,不存在多线程竞争问题。
2.预先加载: 只要程序启动,无论用不用得到,实例都会被创建。
3.优点:实现简单、天生线程安全,运行速度快。
4.缺点:浪费资源当单例对象很大,且在程序运行中很晚才会被用到,会造成不必要的资源占用。
代码实现:
class Utils1{
public:
static Utils1* getUtilsInstance()
{
return &_utils_instance;
}
void print() const{
std::cout << "print() method" << std::endl;
}
private:
Utils1(){}
// 禁用拷贝构造函数和赋值运算符,确保单例的唯一性
Utils1(const Utils1&) = delete;
Utils1& operator=(const Utils1&) = delete;
private:
static Utils1 _utils_instance;
};
Utils1 Utils1::_utils_instance;
二、双重检查锁定懒汉模式
原理:实例 _utils_instance 初始化为 nullptr,仅在第一次调用getUtils2Instance() 时才创建。使用两个 if 检查,只有在实例尚未创建时才进行昂贵的加锁操作,最大限度地减少了锁的开销,提高了多线程环境下的访问效率。
特点:
1.线程安全条件线程安全: 必须配合 std::atomic / 内存屏障,否则在多线程下存在风险。
2.加载时机延迟加载:。 真正需要时才创建实例,节省启动资源。
3.优点:避免了不必要的资源浪费,并且在实例创建后,访问速度快(无需加锁)。
4.缺点:实现复杂,容易出错,需要深入理解 C++ 内存模型。
代码实现:
class Utils2{
public:
static Utils2 *getUtils2Instance()
{
if(_utils_instance == nullptr)
{
// 锁在if里面。需使用锁加if双重判断。即当多个线程同时进行到了if里面。
// 锁在if外面,单线程使用时候比较重
std::lock_guard<std::mutex> lock(mtx);
if(_utils_instance == nullptr)
{
_utils_instance = new Utils2();
}
}
return _utils_instance;
}
private:
Utils2(){};
// 禁用拷贝构造函数和赋值运算符,确保单例的唯一性
Utils2(const Utils2&) = delete;
Utils2& operator=(const Utils2&) = delete;
private:
static std::mutex mtx;
static Utils2* _utils_instance;
};
// 静态成员初始化
std::mutex Utils2::mtx;
Utils2* Utils2::_utils_instance = nullptr; // 初始化为 nullptr
三、局部静态变量懒汉模式 (推荐)
原理:instance 是 getInstance() 函数内的局部静态变量。C++11 标准保证: 如果多线程同时尝试初始化一个局部静态变量,只会有一个线程能够完成初始化。实例的创建被推迟到函数第一次被调用时,同时由编译器确保了线程安全。
特点:
1.线程安全完全线程安全: 由 C++ 标准保证,无需手动加锁。
2.加载时机延迟加载:真正需要时才创建实例。
3.优点:简洁优雅线程安全,延迟加载。 是现代 C++ 中实现单例模式的最佳实践。
class Utils3{
public:
static Utils3& getInstance()
{
// C++ 标准保证了局部静态变量的初始化是线程安全的
static Utils3 instance;
return instance;
}
};

被折叠的 条评论
为什么被折叠?



