📌深入 C++ 单例模式:原理、实现方式对比与 shared_ptr 架构设计

📌深入 C++ 单例模式:原理、实现方式对比与 shared_ptr 架构设计

1️⃣ 什么是单例?基本语义与使用场景

单例([[Singleton]])是对象创建模式中最常见的一种,其目标是确保类在系统中只有一个实例,并提供全局访问入口。

✅ 常见使用场景:

  • 配置中心(ConfigManager)
  • 日志系统(Logger)
  • 资源池(如内存池、线程池)
  • 调度器、会话管理器
  • 框架注册表、插件系统

2️⃣ 单例的五种常见实现方式

推荐使用方式:

  1. 懒汉式 + 线程安全(C++11 标准)局部静态变量(Meyers Singleton)— 【推荐使用】
  2. shared_ptr 单例(更灵活)

🍃1. 饿汉式单例(Early Instantiation)

class Singleton {
public:
    static Singleton& instance() {
        return inst;
    }
private:
    Singleton() {}
    static Singleton inst;
};
Singleton Singleton::inst;
  • ✅ 简单易懂,线程安全(由 C++ 静态对象初始化保障)
  • ❌ 资源可能浪费,程序启动就构造实例

🍃2. 懒汉式(Lazy Initialization)+ 非线程安全

class Singleton {
public:
    static Singleton* getInstance() {
        if (!instance_)
            instance_ = new Singleton;
        return instance_;
    }
private:
    Singleton() {}
    static Singleton* instance_;
};

Singleton* Singleton::instance_ = nullptr;
  • ✅ 仅在需要时创建实例
  • ❌ 非线程安全,多个线程可能同时构造多个实例

🍃3. 懒汉式 + 线程安全(C++11 标准)局部静态变量(Meyers Singleton)— 【推荐使用】

class Singleton {
public:
    static Singleton& instance() {
        static Singleton inst;  // C++11 保证线程安全
        return inst;
    }
private:
    Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};
  • ✅ 推荐使用:线程安全、懒加载、无锁开销
  • ✅ 析构自动管理,依赖 C++ 静态局部变量特性
  • ❗ 禁止拷贝构造、赋值运算,防止实例复制
  • ✅ 最简洁、安全、高效的方式(默认推荐)

后面会有这种方式的语法语义与底层源码解析

🍃4. 懒汉式 + 双重检查锁(DCLP Double-Checked Locking)

class Singleton {
public:
    static Singleton* instance() {
        if (!inst) {
            std::lock_guard<std::mutex> lock(mtx);
            if (!inst) inst = new Singleton;
        }
        return inst;
    }
private:
    static Singleton* inst;
    static std::mutex mtx;
};
  • ✅ 懒加载,适用于需要动态构造析构控制的场景
  • ❌ 实现复杂,容易出错
  • ⚠️ 在 C++11 前实现容易出错(缺乏内存屏障)

🍃5. 智能指针 + 延迟构造

class Singleton {
public:
    static std::shared_ptr<Singleton> instance() {
        static std::shared_ptr<Singleton> inst(new Singleton);
        return inst;
    }
};

支持自定义析构、更适合资源生命周期管理

🍃6. 模板化 shared_ptr 单例(进阶)— 现代 C++ 的正确打开方式

template <typename T>
class SharedSingleton {
public:
    using Ptr = std::shared_ptr<T>;

    static void setDeleter(std::function<void(T*)> deleter) {
        getDeleter() = std::move(deleter);
    }

    template<typename... Args>
    static Ptr instance(Args&&... args) {
        std::call_once(getOnceFlag(), [&] {
            instance_() = Ptr(new T(std::forward<Args>(args)...), getDeleter());
        });
        return instance_();
    }

    static void reset() {
        std::lock_guard<std::mutex> lock(getMutex());
        instance_().reset();
        getOnceFlag() = std::once_flag();
    }

private:
    static Ptr& instance_() {
        static Ptr inst;
        return inst;
    }

    static std::once_flag& getOnceFlag() {
        static std::once_flag flag;
        return flag;
    }

    static std::mutex& getMutex() {
        static std::mutex mtx;
        return mtx;
    }

    static std::function<void(T*)>& getDeleter() {
        static std::function<void(T*)> deleter = [](T* p) { delete p; };
        return deleter;
    }
};

3️⃣ 实现方式对比分析

实现方式 构造控制 析构控制 支持传参 线程安全 生命周期可控 工程推荐度
饿汉式 🟡
懒汉式+DCLP ⚠️ ❌(风险大)
Meyers Singleton ✅(C++11)
shared_ptr静态 部分
shared_ptr模板封装 ✅✅✅

4️⃣ 懒汉式 + 线程安全(C++11 标准)局部静态变量(Meyers Singleton)— 语法语义与底层源码解析

🍃局部静态变量的线程安全(C++11)

static Singleton& getInstance() {
    static Singleton instance;
    return instance;
}

C++11 标准 [stmt.dcl/4]:[[局部静态变量的初始化]]是[[线程安全]]的。

[[编译器]]在初始化前加锁,保证只有一个线程可以构造它。

编译器生成等价伪代码(示意):

Singleton& getInstance() {
    static bool initialized = false;
    static char storage[sizeof(Singleton)];
    static std::once_flag flag;

    std::call_once(flag, [&] {
        new (&storage) Singleton();
        initialized = true;
    });

    return *reinterpret_cast<Singleton*>(&storage);
}

🍃析构顺序与静态对象生命周期问题

  • 局部 static 实例会在 main() 退出后被自动[[析构]]
  • 如果你希望手动控制生命周期(如 exit() 后仍然存在),需配合 newatexitshared_ptr 实现。

🍃源码级剖析(glibc + clang libc++)

[[glibc]] 层面:pthread_once 实现

[[glibc]] 中 std::call_once 依赖 pthread_once

int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));

pthread_once 使用锁 + 状态标志 + [[memory barrier]] 保证只初始化一次 是 [[C++11]] std::call_once 的基础

[[libc++]] 实现(<mutex>

namespace std {
  template<class Callable>
  void call_once(once_flag& flag, Callable&& f);
}

内部采用了双状态原子标记(__called, __complete

GCC 实现中使用 __atomic_load_n + __sync_bool_compare_and_swap 保证[[原子性]]

5️⃣ 底层原理解析:语义 + ABI 行为 + 构造顺序

🧵 std::call_once + std::once_flag

static std::once_flag f;
std::call_once(f, [] { singleton = new Singleton(); });

🔥 内存模型分析

  • 局部 static:存储于静态区
  • shared_ptr:堆区资源,引用计数释放

6️⃣ ResourcePool / ThreadPool 工程实践

auto pool = SharedSingleton<BufferPool>::instance(32, 8*1024);
auto buf = pool->acquire();
auto pool = SharedSingleton<ThreadPool>::instance(8);
pool->enqueue([] { do_work(); });

✅ 总结与设计哲学

能力点 Meyers Singleton shared_ptr 封装
安全性
灵活性 ✅✅
生命周期控制 ✅✅✅
测试友好性 ✅✅
工程适配度 🟡 ✅✅✅