在 C++ 中,单例模式经常用于需要全局共享资源的场景,例如日志系统、配置管理器或数据库连接池。实现线程安全的懒加载(即首次使用时才创建实例)单例模式,常见的方法有:
-
C++11 本地静态变量
class Logger { public: static Logger& instance() { static Logger instance; // C++11 之后的实现保证线程安全 return instance; } void log(const std::string& msg) { /* ... */ } private: Logger() = default; Logger(const Logger&) = delete; Logger& operator=(const Logger&) = delete; };C++11 标准规定,对同一块代码块内的
static变量初始化是互斥的,适合大多数情况。 -
双重检查锁定(Double-Checked Locking)
class Singleton { public: static Singleton* getInstance() { if (!instance_) { // 第一检查 std::lock_guard<std::mutex> lock(mutex_); if (!instance_) { // 第二检查 instance_ = new Singleton(); } } return instance_; } // 其它成员函数 private: Singleton() = default; static Singleton* instance_; static std::mutex mutex_; }; Singleton* Singleton::instance_ = nullptr; std::mutex Singleton::mutex_;该实现需要保证
instance_的原子性读写,C++11 起可以使用std::atomic<Singleton*>。但如果不使用原子指针,可能出现微妙的竞态条件。 -
Meyer’s Singleton 与
std::call_onceclass Config { public: static Config& get() { std::call_once(flag_, [](){ instance_ = new Config(); }); return *instance_; } private: Config() = default; static Config* instance_; static std::once_flag flag_; }; Config* Config::instance_ = nullptr; std::once_flag Config::flag_;std::call_once在多线程环境下只会执行一次,保证初始化唯一。 -
懒加载的智能指针
std::shared_ptr <Singleton> Singleton::getInstance() { std::call_once(flag_, [](){ instance_ = std::make_shared <Singleton>(); }); return instance_; }通过
std::shared_ptr方便管理生命周期,尤其是在多线程退出时能自动销毁。
注意事项
- 销毁顺序:若使用静态局部对象,销毁顺序可能导致“静态初始化顺序问题”。如果程序在退出时需要优先销毁单例,建议手动调用析构或使用
std::unique_ptr并在atexit注册析构。 - 异常安全:若单例构造函数抛异常,
std::call_once会将异常重新抛出并允许下一次调用继续尝试,保证了安全。 - 性能:C++11 的静态局部变量初始化已做了足够优化,除非存在极高并发场景,否则无需手动加锁。
总结来说,最推荐的实现方式是 C++11 的本地静态变量或 std::call_once,它们既简洁又可靠,满足绝大多数线程安全懒加载单例的需求。