c++中如何实现单例模式_c++单例模式的代码实现步骤【指南】

单例不能用全局变量代替,因其不满足延迟初始化、全局唯一和线程安全三要素;C++11起推荐用local static实现,简洁且线程安全。

为什么不能用全局变量代替单例

全局变量在多线程环境下不安全,且无法控制初始化时机——static局部变量在 C++11 起才保证线程安全的首次初始化,而全局对象的构造顺序跨编译单元不可控,容易引发 undefined behavior。单例的核心诉求是「延迟初始化 + 全局唯一 + 线程安全」,这三点全局变量一个都做不到。

最简安全写法:C++11 以后推荐用 local static

利用函数内 static 变量的“首次调用时初始化”和“C++11 标准保证的线程安全”特性,代码极简且无锁:

class Singleton {
public:
    static Singleton& getInstance() {
        static Singleton instance; // ✅ 线程安全,仅首次调用构造
        return instance;
    }

    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

private:
    Singleton() = default; // 可加日志或资源初始化
};

注意点:

  • getInstance() 必须返回引用(Singleton&),返回值会触发拷贝,破坏单例语义
  • 构造函数设为 private,防止外部 new 或直接实例化
  • 显式删除拷贝构造与赋值操作符,避免误用
  • 不要在构造函数里调用其他单例的 getInstance(),可能触发静态初始化顺序问题

需要手动管理生命周期?慎用 std::unique_ptr + new

只有在必须延迟析构(比如依赖其他单例销毁后才释放)或需定制销毁逻辑时,才考虑堆分配。但代价是引入显式内存管理风险:

class Singleton {
public:
    static Singleton& getInstance() {
        static std::unique_ptr instance;
        if (!instance) {
            instance = std::make_unique();
        }
        return *instance;
    }

private:
    Singleton() = default;
    ~Singleton() = default; // 可加清理逻辑
};

隐患:

  • 如果程序异常退出(如 std::abort()),~Singleton() 可能不被调用
  • 多个单例之间若存在析构依赖,仍可能 crash(C++ 不保证静态对象析构顺序)
  • 比 local static 写法多一次指针解引用,性能略低(通常可忽略)

双检锁(DCLP)在 C++ 中已过时且极易出错

早期为兼容 C++0

3 手写双重检查锁定(Double-Checked Locking Pattern),现在完全没必要。它要求:

  • std::atomic 存储指针(裸指针 + volatile 在 C++11 后无效)
  • 两次 load() 都需指定内存序(如 memory_order_acquire
  • 中间 new 操作必须确保构造完成后再发布指针,否则其他线程可能看到未初始化对象

哪怕抄对了,也比 local static 多出锁开销和复杂度。C++11 标准明确支持 local static 的线程安全初始化,这是标准给出的正解。

真正要注意的是:单例不是银弹。它的全局状态、隐式依赖和测试困难性,在大型项目中往往比实现本身更伤人。