设计模式-单例模式

本文详细介绍了单例模式的概念及其在程序设计中的作用,如确保日志器、数据库等类的唯一实例。接着,对比分析了饿汉式和懒汉式的优缺点:饿汉式在程序启动时即初始化,效率高但内存占用始终;懒汉式仅在需要时加载,节省内存但可能影响程序流程。文章还深入讨论了懒汉式的非线程安全、线程安全和双重检查(推荐)三种实现方式,并提供了相应的C++代码示例,强调了双重检查在保证线程安全的同时提高了效率。
摘要由CSDN通过智能技术生成

什么是单例模式

在系统中保证一个类只存在唯一实例的模式即为单例模式。如日志器、数据库等。

实现单例模式

单例模式一般分为两种模式:懒汉式、饿汉式。

  • 饿汉式:像一个很饿的人,在程序启动时就完成了加载,在整个程序运行过程中一直存在。
  • 懒汉式:像一个很懒的人,只有在需要使用的时候,动态的加载。

两种模式的优缺点:

模式优点缺点
饿汉式程序运行过程中不需要重新加载,执行效率更快不管是否使用,都会占用内存
懒汉式只有使用到的时候才会占用内存程序运行过程中加载,若加载时间较长,影响程序流程

单例模式的设计要点:

  • 构造函数私有化
  • 对外提供一个接口获取实例
  • 只有一个对象实例

饿汉式

使用静态类成员实现,在成员启动时完成初始化

// 饿汉式,使用静态类成员实现,在成员启动时完成初始化
class SingleTonTest1{
private:
    SingleTonTest1()=default;	// 构造函数私有化
public:
	// 提供接口获取实例对象
    static SingleTonTest1* getInstance(){
        return singleTonTest1.get();
    }

private:
    static std::unique_ptr<SingleTonTest1> singleTonTest1;
};
std::unique_ptr<SingleTonTest1> SingleTonTest1::singleTonTest1 (new SingleTonTest1); // 使用静态类成员,在程序启动时完成初始化

懒汉式

懒汉式一般又三种实现方式: 非线程安全、线程安全、双重检查(推荐)
实例初始化流程如下:

已初始化
未初始化
获取实例
检查实例
返回实例
初始化实例

非线程安全

在第一次获取实例时检查实例是否初始化,若未初始化,则将实例初始化。因在检查实例时未加入互斥锁,所以在第一次初始化实例对象时,可能会有多个线程完成实例的初始化,造成内存泄漏。
实现代码如下:

// 懒汉式,非线程安全
class SingleTonTest2{
private:
    SingleTonTest2()=default;// 构造函数私有化
public:
	// 提供接口获取实例对象
    static SingleTonTest2* getInstance(){
    	// 检查实例是否完成初始化
        if(nullptr == singleTonTest2) // 可能会有多个线程同时检查到实例未初始化,非线程安全
        {
        	// 初始化对象
            singleTonTest2.reset(new SingleTonTest2);
            return singleTonTest2.get();
        }
       	// 返回对象
        return singleTonTest2.get();
    }
private:
    static std::unique_ptr<SingleTonTest2> singleTonTest2;
};
std::unique_ptr<SingleTonTest2> SingleTonTest2::singleTonTest2 (nullptr); // NOLINT

线程安全(不推荐)

在非线程安全的基础上,加锁可以解决多线程造成的多次初始化的问题。这种方式主要问题是不管实例是否已完成了初始化,都需要先加锁,会影响程序运行效率。

// 懒汉式,线程安全
class SingletonTest3{
private:
    SingletonTest3()=default;// 构造函数私有化
public:
// 提供接口获取实例对象
    static SingletonTest3* getInstance() {
        lock.lock(); // 每次检查前,先加互斥锁
        if (nullptr == singletonTest3) {
            singletonTest3.reset(new SingletonTest3);
        }
        lock.unloc();
        // 返回实例
        return singletonTest3.get();
    }
private:
    static std::unique_ptr<SingletonTest3> singletonTest3;
    static std::mutex lock;
};
std::unique_ptr<SingletonTest3> SingletonTest3::singletonTest3 (nullptr); // NOLINT
std::mutex SingletonTest3::lock;

双重检查(推荐)

在线程安全的基础上,我们把检查实例的判断先提出来,在准备初始化前先加锁,再判断一次实例是否已经初始化,这样既保证运行效率又保证了线程安全。

// 懒汉式,双重检查
class SingletonTest4{
private:
    SingletonTest4()=default;// 构造函数私有化
public:
	// 提供接口获取实例对象
    static SingletonTest4* getInstance(){
        if(nullptr == singletonTest4) { // 第一次判断实例是否初始化
            lock.lock(); // 加入互斥锁
            // 第二次判断实例是否初始化,如果已被初始化了,则跳过初始化流程
            if (nullptr == singletonTest4) { 
                singletonTest4.reset(new SingletonTest4);
            }
            lock.unlock();
        }
        return singletonTest4.get();
    }
private:
    static std::unique_ptr<SingletonTest4> singletonTest4;
    static std::mutex lock;
};
std::unique_ptr<SingletonTest4> SingletonTest4::singletonTest4 (nullptr); // NOLINT
std::mutex SingletonTest4::lock;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值