1.单例模式:what :

C++中,单例模式是一种设计模式,确保一个类仅有一个实例存在整个程序的生命周期中,并且为这个实例提供一个全局访问点。比如大型软件系统中,有一个配置类,只需要创建一个实例就可以为整个系统提供服务。如果创建多个实例,不仅会浪费系统资源,还可能导致配置信息不一致。通过单例模式,我们可以保证配置类在整个程序中只有一个实例,所有需要获取或修改系统配置的地方都可以通过这个单例来操作。

1. 可以节省内存资源;2. 方便管理全局资源。3.避免资源重复占用和冲突。多线程通过共享实例。

2.单例模式:How:

实现的具体的关键点:

首先将构造函数私有化。这是为了防止外部代码通过new操作符随意创建类的实例,从而确保整个实例只有一个实例存在。

class Singleton{
private:
    Singleton(){};
};

其次,需要有一个私有静态实例指针来保证保存唯一的实例。因为是静态的整个程序运行期间都存在,并且只有一份。

class Singleton{
private:
    Singleton(){}
    static Singleton* instance;//私有静态实例指针。
};
Singleton* Singleton:: instance = nullptr;//类外初始化指针为nullptr;

最后,提供一个公共静态方法作为获取静态实例的接口。

class Singleton{
private:
    Singleton() {}
    static Singleton* instance;
public:
    static Singleton* getInstace(){
    if(instance == nullptr){
        instance = new Singleton();
    }
    return instance;
}
static Singleton* Singleton:: instance = nullptr;

2.1懒汉式

刚刚我们实现的就叫做懒汉式,为什么叫懒汉式呢,因为是在getInstance中,才回去创建实例,在一些情况下可以去节省资源。不过多线程不安全。为什么呢:比如线程A和线程B都进入了判定实例不为空的代码体中,这就会造成多个实例。

为什么叫“懒汉式”

  • 懒汉式(Lazy Initialization):只有当调用 getInstance 的时候,才真正去构造对象。

  • 优点:如果程序运行过程中压根没用到 Singleton,就不会浪费资源去构造对象。

  • 缺点:多线程环境下,第一次构造时存在线程安全问题。

加锁实现:不让多个线程进入代码体就行。

class Singleton{
private:
    Singleton() {}
    static Singleton* instance;
    static std::mutex mtx;
public:
    static Singleton* getInstace(){
    std::lock_guard<std::mutex> lock(mtx);
    if(instance == nullptr){
        instance = new Singleton();
    }
    return instance;
}
static Singleton* Singleton:: instance = nullptr;
static Singleton::mutex;

互斥锁:std::lock_gurad<std::mutex> lock(mtx);RAII风格:为了解决懒汉模式的线程安全问题,可以使用互斥锁。这里使用了std::lock_guard,它是一个 RAII(Resource Acquisition Is Initialization)类,在构造时自动加锁,在析构时自动解锁。当一个线程进入getInstance方法时,std::lock_guard会自动加锁,这样其他线程就无法进入这个代码块,直到当前线程解锁。在加锁后,再判断instance是否为空,如果为空则创建实例,从而保证了在多线程环境下只有一个实例被创建。

虽然加锁实现保证了线程安全,但也带来了性能开销。每次调用getInstance方法时都需要加锁和解锁,而加锁和解锁操作是有一定时间成本的。如果这个方法被频繁调用,那么频繁的加锁解锁操作会降低程序的执行效率。就像每次进入一个房间都要进行繁琐的安检(加锁)和解除安检(解锁),会大大影响进入房间的速度。在高并发场景下,这种性能开销可能会成为系统的瓶颈。

#include <mutex>

class Singleton {
private:
    Singleton() {}                                // 私有构造函数
    ~Singleton() {}                               // 私有析构函数
    Singleton(const Singleton&) = delete;         // 禁止拷贝
    Singleton& operator=(const Singleton&) = delete; // 禁止赋值

    static Singleton* instance;   // 指针实例
    static std::mutex mtx;        // 互斥锁

public:
    static Singleton* getInstance() {
        if (instance == nullptr) {        // 第一次检查
            std::lock_guard<std::mutex> lock(mtx);
            if (instance == nullptr) {    // 第二次检查
                instance = new Singleton();
            }
        }
        return instance;
    }
};

// 静态成员初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;
  • 双重检查锁(DCLP)

    • 第一次 if (instance == nullptr):大多数情况下,实例已经创建好了,就不用加锁,提高性能。

    • 第二次 if (instance == nullptr):在锁住的情况下再判断一次,避免两个线程同时通过第一次检查,导致创建多个实例。

  • C++11 内存模型保证

    • C++11 对静态局部变量的初始化做了线程安全保证。

    • 所以更简洁的写法是使用 静态局部变量法:

class Singleton {
private:
    Singleton() {}
    ~Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

public:
    static Singleton& getInstance() {
        static Singleton instance;  // C++11 保证线程安全初始化
        return instance;
    }
};

2.2饿汉式

class Singleton {
private:
    Singleton() = default;
    ~Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    static Singleton instance;   // 静态对象,类加载时就创建

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

// 定义
Singleton Singleton::instance;
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
#include <chrono>

// ============== 1) 饿汉式(Eager Singleton)==================
// 程序启动(类加载)即创建实例:简单、天然线程安全;但可能从未使用也占资源。
class EagerSingleton {
private:
    EagerSingleton() { std::cout << "[Eager] ctor\n"; }
    ~EagerSingleton() = default;
    EagerSingleton(const EagerSingleton&) = delete;
    EagerSingleton& operator=(const EagerSingleton&) = delete;

    static EagerSingleton instance; // 静态对象(推荐优于 new 指针)
public:
    static EagerSingleton& getInstance() { return instance; }
    void doWork() const { std::cout << "[Eager]   this=" << this << "\n"; }
};
EagerSingleton EagerSingleton::instance; // 定义 + 初始化

// ============== 2) Meyers 懒汉式(C++11+,最推荐)=============
// 第一次调用 getInstance() 才创建;C++11 起,静态局部变量初始化是线程安全的。
class MeyersSingleton {
private:
    MeyersSingleton() { std::cout << "[Meyers] ctor\n"; }
    ~MeyersSingleton() = default;
    MeyersSingleton(const MeyersSingleton&) = delete;
    MeyersSingleton& operator=(const MeyersSingleton&) = delete;
public:
    static MeyersSingleton& getInstance() {
        static MeyersSingleton inst; // 线程安全的一次性初始化
        return inst;
    }
    void doWork() const { std::cout << "[Meyers] this=" << this << "\n"; }
};

// ============== 3) call_once 懒汉式(可控生命周期)=============
// 和 Meyers 一样只初始化一次,但可以在需要时 destroy() 主动释放资源。
class OnceSingleton {
private:
    OnceSingleton() { std::cout << "[Once]   ctor\n"; }
    ~OnceSingleton() { std::cout << "[Once]   dtor\n"; }
    OnceSingleton(const OnceSingleton&) = delete;
    OnceSingleton& operator=(const OnceSingleton&) = delete;

    static std::once_flag once_;
    static OnceSingleton* ptr_;
    static void init() { ptr_ = new OnceSingleton(); }

public:
    static OnceSingleton& getInstance() {
        std::call_once(once_, init);
        return *ptr_;
    }
    static void destroy() { delete ptr_; ptr_ = nullptr; } // 需要时机可控的资源释放
    void doWork() const { std::cout << "[Once]   this=" << this << "\n"; }
};
std::once_flag    OnceSingleton::once_;
OnceSingleton*    OnceSingleton::ptr_ = nullptr;


// ============== 并发压力测试辅助函数 ============================
template<typename Fn>
void run_threads(const char* title, Fn f, int n_threads = 8) {
    std::cout << "\n=== " << title << "(并发演示) ===\n";
    std::vector<std::thread> ths;
    ths.reserve(n_threads);
    for (int i = 0; i < n_threads; ++i) {
        ths.emplace_back([f, i]{
            // 模拟竞争(随机微小延迟)
            std::this_thread::sleep_for(std::chrono::milliseconds(1 + (i % 3)));
            f();
        });
    }
    for (auto& t : ths) t.join();
}

int main() {
    // 1) 饿汉式:程序一开始就已创建(可观察到最先打印 ctor)
    run_threads("EagerSingleton", []{
        EagerSingleton::getInstance().doWork();
    });

    // 2) Meyers 懒汉式:首次调用时才创建(只会出现一次 ctor)
    run_threads("MeyersSingleton", []{
        MeyersSingleton::getInstance().doWork();
    });

    // 3) call_once 懒汉式:首次调用时创建;还可主动 destroy()
    run_threads("OnceSingleton (first wave)", []{
        OnceSingleton::getInstance().doWork();
    });

    // 如果想演示销毁再创建(谨慎:真实业务一般避免重复构造/销毁)
    std::cout << "\n--- destroy OnceSingleton and call again ---\n";
    OnceSingleton::destroy();

    run_threads("OnceSingleton (second wave)", []{
        OnceSingleton::getInstance().doWork();
    });

    // 程序结束:Eager/Meyers 的静态对象由运行时自动析构;
    // OnceSingleton 我们手动释放过一次,第二次不释放也会由 OS 回收内存(演示目的)。
    return 0;
}

局部静态变量(function-local static)的初始化是按需的(第一次进入时才初始化),并且必须是线程安全的。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐