设计模式:单例模式
复习一下:
单例模式(Singleton Pattern)
原理:
单例模式确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。它通过将类的构造函数设为私有,防止外部代码直接实例化该类。同时,提供一个静态方法来获取唯一的实例。在首次调用获取实例的方法时,会创建这个实例,之后的调用都返回这个已创建的实例。
示例代码:
class Singleton {
private:
static Singleton* instance;
Singleton() {}
public:
static Singleton* getInstance() {
if (instance == nullptr) {
instance = new Singleton;
}
return instance;
}
};
Singleton* Singleton::instance = nullptr;
使用场景:
适用于资源共享的情况,如数据库连接池。因为只需要一个数据库连接池实例来管理连接,避免多次创建和销毁连接池带来的开销。还用于日志系统,全局只有一个日志对象记录各种日志信息,方便统一管理和配置。
优化
上面的代码虽然实现了单例模式,但存在线程安全问题。在多线程环境下,多个线程可能同时检查到 instance 为 nullptr,从而创建多个实例。可以使用双重检查锁定(Double-Checked Locking)或者静态局部变量的方式来优化。
-
双重检查锁定
双重检查锁定会在第一次检查 instance 是否为 nullptr 之后加锁,再次检查 instance 是否为 nullptr,以确保只有一个线程能创建实例。
#include <mutex> class Singleton { private: static Singleton* instance; static std::mutex mutex_; Singleton() {} public: static Singleton* getInstance() { if (instance == nullptr) { std::lock_guard<std::mutex> lock(mutex_); if (instance == nullptr) { instance = new Singleton; } } return instance; } }; Singleton* Singleton::instance = nullptr; std::mutex Singleton::mutex_;
-
静态局部变量
C++11 之后,静态局部变量的初始化是线程安全的,因此可以使用静态局部变量来实现单例模式。
class Singleton { private: Singleton() {} public: static Singleton& getInstance() { static Singleton instance; return instance; } };
这种方式更为简洁,并且自动处理了线程安全问题,也避免了手动管理内存。
使用静态局部变量的方式来实现单例模式,其原理主要基于静态局部变量的两个关键特性:延迟初始化和线程安全初始化。
延迟初始化
静态局部变量是在程序第一次执行到其定义处时才进行初始化的。也就是说,只有当首次调用包含该静态局部变量的函数时,这个静态局部变量才会被创建并初始化。
在单例模式的实现中,静态局部变量的这种特性能够确保单例实例是在真正需要使用它的时候才被创建,而非在程序启动阶段就创建,这样可以避免不必要的资源浪费。
线程安全初始化
C++11 标准规定,静态局部变量的初始化是线程安全的。这意味着在多线程环境下,即使多个线程同时调用包含静态局部变量的函数,也能保证该静态局部变量只会被初始化一次。
当多个线程同时尝试访问并初始化这个静态局部变量时,标准要求编译器必须保证只有一个线程能够执行初始化操作,其他线程需要等待初始化完成后才能继续执行。这样一来,就确保了单例模式中实例的唯一性。