01创建型设计模式——单例模式

一、单例模式简介

        单例模式(Singleton Pattern)是一种创建型设计模式(GoF书中解释创建型设计模式:一种用来处理对象的创建过程的模式),单例模式是其中的一种,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。这个模式适用于那些需要一个全局唯一的对象来协调系统中的行为的情况。即在整个程序运行过程中,该类只存在一个对象(实例)。

GoF一书对单例模式的介绍

 二、单例模式的用处

单例模式常见的使用场景

  • 配置管理:全局配置管理器,确保配置只被加载一次并在全局范围内一致。
  • 日志记录:全局日志记录器,确保所有日志信息被记录在一个地方。
  • 线程池:全局线程池管理,避免多个线程池实例带来的资源浪费。
  • 数据库连接池:全局数据库连接池,统一管理数据库连接的创建和销毁

单例模式的优点包括:

1. 确保全局唯一性

  • 唯一性:单例模式确保一个类只有一个实例。这对于那些需要全局唯一的资源或管理类非常重要,例如配置管理器、日志记录器等。

  • 全局访问:单例模式提供了一个全局访问点来获取这个唯一实例,使得所有代码都可以通过统一的方式来访问该实例。

2. 控制资源访问

  • 资源管理:在某些情况下,某些资源(如数据库连接、线程池)只能由一个实例进行管理。使用单例模式可以有效地控制这些资源的创建和销毁,避免资源的重复创建和管理。

  • 性能优化:通过避免创建多个实例,单例模式可以减少系统开销和资源浪费。

3. 简化接口

  • 简化使用:由于只有一个实例,使用单例模式的类不需要考虑实例的创建和管理,使用时更简单直观。

  • 一致性:可以保证对全局状态的一致性和统一管理,减少了不同实例间的状态不一致问题。

4. 延迟实例化

  • 懒加载:通过懒汉式实现,单例模式可以实现延迟初始化(即实例在第一次使用时创建),从而提高系统启动速度并节省资源,直到确实需要实例时才创建它。

5. 避免多次实例化

  • 避免浪费:有些对象的创建和初始化代价较高,使用单例模式可以避免重复创建这些对象,从而节省计算资源和时间。

三、单例模式的设计步骤

a)私有化构造函数

b)提供一个全局的静态方法(全局访问点)

c)在类中定义一个静态指针,指向该类的对象(静态指针由全局访问点获取)

四、单例模式的两种设计方法

1)懒汉式(Lazy Initialization

这种设计方式只有在第一次使用时才会创建实例,这种方式的优点是延迟初始化。通常使用静态局部变量来实现线程安全的懒加载。

2)饿汉式(Eager Initialization

在这种实现中,单例实例在程序启动时就被创建。也就是不管有没有被使用,只要程序运行就会创建该对象。这个实现简单,但如果单例实例的创建开销较大,可能会导致程序启动变慢,且可能会浪费资源。

懒汉式代码示例:

lazy.cpp

#include <iostream>

//Lazy Initialization
class Singleton {
public:
    // 获取单例实例的公共方法
    static Singleton& getInstance() {
        static Singleton instance; // 静态局部变量,线程安全
        return instance;
    }

    // 禁止复制构造函数和赋值操作符
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
    // 其他成员变量和方法
    void doWorking() const {
    	std::cout<<"我是懒汉式创建型设计模式——单例模式! "<<std::endl;
    }

private:
    Singleton() {} // 私有构造函数
    
};

// 使用示例
int main() {
    Singleton& singleton = Singleton::getInstance();
    Singleton* ptr = &Singleton::getInstance();
    //判断是否调用同一个对象
    if ( &singleton == ptr){
    	std::cout<<"我们是同一个对象! "<<std::endl;
    }else{
    	std::cout<<"我们是不同的对象! "<<std::endl;
    }
    //调用对象的方法
    singleton.doWorking();
    ptr->doWorking();
    
    return 0;
}

运行效果

 

饿汉式代码示例:

eager.cpp

#include <iostream>

//Eager Initialization
class Singleton {
public:
    // 获取单例实例的公共方法
    static Singleton& getInstance() {
        return instance;
    }

    // 禁止复制构造函数和赋值操作符
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
    // 其他成员变量和方法
    void doWorking() const {
    	std::cout<<"我是饿汉式创建型设计模式——单例模式! "<<std::endl;
    }

private:
    Singleton() {} // 私有构造函数

    static Singleton instance; // 声明静态成员

};

Singleton Singleton::instance; // 定义并初始化静态成员(只要程序运行就创建该实例)

// 使用示例
int main() {
    Singleton& singleton = Singleton::getInstance();
    Singleton* ptr = &Singleton::getInstance();
    //判断是否调用同一个对象
    if ( &singleton == ptr){
    	std::cout<<"我们是同一个对象! "<<std::endl;
    }else{
    	std::cout<<"我们是不同的对象! "<<std::endl;
    }
    //调用对象的方法
    singleton.doWorking();
    ptr->doWorking();
    return 0;
}

运行效果

3)单例模式的线程安全问题

使用饿汉式方法创建单例模式,如果不使用静态局部变量,而是使用裸指针+判断的方式创建单例,很容易引发多线程的资源竞争问题。(线程在空闲状态下可以挂起)

使用双重检查锁定(Double-Checked Locking)来确保多线程环境下的单例创建的安全性。

示例代码:

doubleLock.cpp

#include <iostream>
#include <mutex>

//线程安全的双重检查锁定
class Singleton {
public:
    static Singleton* getInstance() {
        if (!instance) {
            std::lock_guard<std::mutex> lock(mutex);
            if (!instance) {
                instance = new Singleton();
            }
        }
        return instance;
    }

    // 禁止复制构造函数和赋值操作符
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
    // 其他成员变量和方法
    void doWorking() const {
    	std::cout<<"我是懒汉式单例模式——我使用了双重检查锁定保证我的创建安全! "<<std::endl;
    }


private:
    Singleton() {} // 私有构造函数
    //类中声明静态变量
    static Singleton* instance;
    static std::mutex mutex;

};

Singleton* Singleton::instance = nullptr;    //定义并初始化指针为空
std::mutex Singleton::mutex;    //定义并初始化互斥锁

// 使用示例
int main() {
    Singleton* singleton = Singleton::getInstance();
    Singleton* ptr = Singleton::getInstance();
    //判断是否调用同一个对象
    if (singleton == ptr){
    	std::cout<<"我们是同一个对象! "<<std::endl;
    }else{
    	std::cout<<"我们是不同的对象! "<<std::endl;
    }
    //调用对象的方法
    singleton->doWorking();
    ptr->doWorking();
    
    return 0;
}

运行效果

 

23种设计模式中,单例模式是比较简单的一种,但是涉及到的知识面也是很多的,比如线程、互斥、同步等。 后面我还会继续讲解其他设计模式,敬请期待啦(¬‿¬)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值