单例模式的C++实现示例,懒汉+饿汉

本文对比分析了懒汉式和饿汉式两种单例模式在并发环境下的实现与性能。懒汉式通过延迟加载节省资源,但在多线程中需要同步机制确保线程安全;饿汉式则在类加载时即创建实例,无并发问题但无法延迟加载。两种方式各有优劣,适用场景不同。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

核心思想

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。单例模式的核心在于通过限制类的实例化过程,使得在整个应用程序生命周期内,只有一个对象存在。

**私有构造函数:**防止外部代码通过new关键字创建实例。
**静态实例:**类内部维护一个静态的、唯一的实例。
**全局访问点:**提供一个静态方法(如getInstance)来获取该实例。

单例模式的核心是控制对象的唯一性,并确保全局访问的便捷性。

使用场景

全局唯一资源:如配置文件管理器、日志系统、数据库连接池等。
共享资源:如线程池、缓存管理器等需要全局共享的对象。
控制资源访问:如硬件设备的访问控制(打印机、扫描仪等)。
减少对象创建开销:当对象的创建成本较高时,使用单例模式可以避免重复创建。
状态管理:如应用程序的状态管理器或会话管理器。

解决的问题

资源竞争问题:
在某些场景下,多个实例可能导致资源竞争或数据不一致。单例模式通过确保只有一个实例,避免这些问题。

全局访问问题:
如果某些对象需要在全局范围内访问,直接使用全局变量可能导致代码耦合性高。单例模式提供了一个统一的访问点。

性能开销问题:
频繁创建和销毁对象可能导致性能问题。单例模式通过复用唯一实例,减少对象创建和销毁的开销。

代码可维护性问题:
如果对象的创建逻辑分散在各处,可能导致代码难以维护。单例模式将创建逻辑集中管理。

优点

唯一性:确保一个类只有一个实例,避免资源竞争和数据不一致。
全局访问:提供一个统一的访问点,方便全局使用。
延迟初始化:单例模式可以支持延迟初始化,只有在第一次使用时才创建实例。
减少开销:避免重复创建对象,减少内存和性能开销。

缺点

全局状态:单例模式引入了全局状态,可能导致代码耦合性高,难以测试和维护。
违反单一职责原则:单例模式既负责业务逻辑,又负责实例的管理,可能违反单一职责原则。
线程安全问题:在多线程环境下,需要额外处理线程安全问题(如双重检查锁、静态局部变量等)。
扩展性差:单例模式难以扩展为多例模式,如果需要支持多个实例,可能需要对代码进行较大修改。

饿汉单例

饿汉式的特点是在类加载的时候就创建实例,所以称为"饿汉式",因为它比较"急切"地去创建实例。饿汉式的实现通过一个静态变量instance来持有唯一实例,因为静态变量在程序启动时就会初始化。由于在程序启动时就创建实例,所以不存在多线程并发访问创建实例的问题,这种方式是线程安全的。饿汉式的缺点是无法实现延迟加载,即使在某些情况下没有使用到该单例对象,它仍然会被创建和占用内存。此外,由于静态变量的生命周期与程序的生命周期相同,如果应用程序中从未使用过该单例对象,那么它可能会浪费一些内存资源。
适合场景:饿汉式是一种简单但不够灵活的单例模式实现方法。它适用于单例对象的创建成本较低的场景。

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

private:
    static EagerSingleton instance; // 类外初始化
    EagerSingleton() = default;
    ~EagerSingleton() = default;
    // 禁用拷贝和赋值
    EagerSingleton(const EagerSingleton&) = delete; // 禁用拷贝
    EagerSingleton& operator=(const EagerSingleton&) = delete; // 禁用赋值
};
};

// 类外初始化(C++11后支持线程安全的静态初始化)
EagerSingleton EagerSingleton::instance;

懒汉单例

懒汉式的优点是延迟加载,在实际使用中才创建对象,避免了不必要的资源消耗。在C++11或更高的版本中,静态局部变量的创建是线程安全的,因此利用此特性实现懒汉单利变得非常简单,如下代码实现,这是目前最推荐的方式。如果使用new创建单例对象,就需要配合双重检查锁实现,并且要注意指令重排机制,这里面涉及到编译器优化的问题,有可能对象所需的内存已经创建好了,但是对象还没初始化完成(即构造函数还没执行),这块内存地址就已经被赋值给对象指针了。一定要使用new来创建单例对象的话,建议配合std::call_once来使用,这是线程安全的,就不存在编译器优化导致指令重排的问题了。

// 饿汉单例模式,基于静态局部变量方式
class lazySingleton
{
private:
    lazySingleton(){ cout << "lazy singleton construct." << endl; };

    ~lazySingleton(){ cout << "lazy singleton distruct." << endl; };

    lazySingleton(const lazySingleton&) = delete;; //拷贝构造

    lazySingleton& operator=(const lazySingleton&) = delete;;  //=运算符重载


public:
    static lazySingleton* getInstance()
    {
        static lazySingleton myInstance; // 首次调用时初始化,C++11以上是线程安全的
        return &myInstance;
    }
    void test(){ cout<<"this is the lazy singleton."<<endl; };
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值