饿汉

饿汉式(Eager Initialization)是一种简单的单例模式实现方法,在类加载时就创建唯一实例。

class Singleton {
private:
    // 将构造函数、拷贝构造函数和赋值运算符设为私有,防止外部实例化和复制
    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
public:
    static Singleton& getInstance() {
        static Singleton instance;  // 在静态函数中创建唯一实例
        return instance;
    }
};

Singleton类中的构造函数、拷贝构造函数和赋值运算符被私有化,并且使用了delete关键字,这样可以防止外部直接实例化对象或进行拷贝。而getInstance()方法是静态方法,它返回一个指向唯一实例的引用。

在getInstance()方法中,我们使用了局部静态变量instance来保存唯一的实例。由于局部静态变量的特性,它只会在首次调用getInstance()方法时创建,之后的调用都会直接返回该实例。这样能够保证在程序启动时就创建了单例对象。

通过调用Singleton::getInstance()就可以获取到全局唯一的Singleton实例。

说明:

  • 饿汉式的特点是在类加载的时候就创建实例,所以称为"饿汉式",因为它比较"急切"地去创建实例。
  • 饿汉式的实现通过一个静态变量instance来持有唯一实例,因为静态变量在程序启动时就会初始化。
  • 由于在程序启动时就创建实例,所以不存在多线程并发访问创建实例的问题,这种方式是线程安全的。
  • 饿汉式的缺点是无法实现延迟加载,即使在某些情况下没有使用到该单例对象,它仍然会被创建和占用内存。

此外,由于静态变量的生命周期与程序的生命周期相同,如果应用程序中从未使用过该单例对象,那么它可能会浪费一些内存资源。

饿汉式是一种简单但不够灵活的单例模式实现方法。它适用于单例对象的创建成本较低的场景。

懒汉

C++中的懒汉式(Lazy Initialization)是一种延迟加载的单例模式实现方式。它只有在需要使用单例对象时才进行创建,而不是在类加载时就创建实例。

class Singleton {
private:
    // 私有的静态成员指针,用于存储单例对象
    static Singleton* instance;

    // 将构造函数、拷贝构造函数和赋值运算符私有化,防止外部实例化和复制
    Singleton() {}
    Singleton(const Singleton& other) {}
    Singleton& operator=(const Singleton& other) {}

public:
    // 静态成员函数,用于获取单例对象的引用
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};

// 初始化静态成员变量为nullptr
Singleton* Singleton::instance = nullptr;

Singleton类具有一个私有的静态成员指针instance,并通过调用静态成员函数getInstance()返回单例对象的引用。在首次调用getInstance()时,会检查instance是否为null,如果是,则创建一个新的Singleton对象并赋值给instance,否则直接返回现有的instance。

懒汉式单例模式延迟了对象的创建时间,当第一次使用单例对象时才进行实例化。这种延迟加载的方式可以节省资源并满足按需创建的需求。但是需要注意的是,在多线程环境下,懒汉式单例模式可能会引发线程安全问题,因为多个线程可能同时访问到getInstance()方法,从而导致创建多个实例。为了解决这个问题,可以使用线程安全的技术(如加锁)来保证只创建一个实例。

C++懒汉式(Lazy Initialization)在多线程环境下可能存在线程安全性问题。当多个线程同时调用实例获取方法时,可能会导致创建多个实例,违背了单例模式的初衷。

以下是两种常见的解决方案:

  1. 加锁:使用互斥锁(mutex)来保证在实例创建过程中只有一个线程能够进入关键代码段,其他线程需要等待。在懒汉式实例获取方法中加入互斥锁可以解决线程安全性问题。

    class Singleton {
    private:
        static Singleton* instance;
        static std::mutex mutex;
    
        Singleton() {}
        Singleton(const Singleton& other) {}
        Singleton& operator=(const Singleton& other) {}
    
    public:
        static Singleton* getInstance() {
            std::lock_guard<std::mutex> lock(mutex);  // 加锁
    
            if (instance == nullptr) {
                instance = new Singleton();
            }
            return instance;
        }
    };
    
    Singleton* Singleton::instance = nullptr;
    std::mutex Singleton::mutex;
    
  2. 双重检查锁定(Double-Checked Locking):双重检查锁定是一种优化的加锁方式,在加锁前后都进行了判断,减少了不必要的锁开销。

class Singleton {
private:
    static Singleton* instance;
    static std::mutex mutex;

    Singleton() {}
    Singleton(const Singleton& other) {}
    Singleton& operator=(const Singleton& other) {}

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;

局部静态变量(Local static variable)

C++中,使用局部静态变量实现单例模式是一种常见且简洁的方式。局部静态变量指的是在函数内部定义的静态变量,这种变量在程序执行过程中只会被初始化一次。

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

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

利用了 C++11 标准的静态局部变量初始化的线程安全性质。C++11 规定对于静态局部变量的初始化是线程安全的,并且只会在第一次调用该函数时进行初始化。因此,无需额外的线程同步措施,能够确保只有一个实例被创建。

当调用 getInstance() 方法时,静态局部变量 instance 会被初始化,并返回该实例的引用。由于静态局部变量的生命周期在程序运行期间持续存在,所以每次调用 getInstance() 方法都会返回同一个实例。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部