设计模式——单例模式

C++ 单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。

一、单例模式的主要特点和目的

单一实例:

单例模式的核心特点是在整个程序的生命周期中,一个类只有一个实例被创建。无论在程序的哪个部分尝试获取该类的实例,都将得到同一个对象。

这对于一些需要全局唯一资源的场景非常有用,例如数据库连接管理器、配置文件读取器、日志记录器等。这些对象在整个程序中只需要一个实例来管理相关资源,避免了资源的重复分配和冲突。

全局访问点:

提供一个静态方法或全局函数,允许在程序的任何地方方便地访问单例对象。通常这个方法被命名为 getInstance()。

这种全局访问的方式使得单例对象可以在不同的模块和函数之间共享,而无需通过复杂的参数传递或对象引用传递。

控制资源访问和共享:

单例模式可以有效地控制对某些关键资源的访问。例如,一个数据库连接管理器单例可以确保所有的数据库操作都使用同一个连接,避免了频繁地创建和销毁连接所带来的开销。

同时,单例对象可以在不同的部分之间共享数据和状态,实现了一种简洁的信息传递方式。

二、单例模式的实现方式

懒汉式(Lazy Initialization):

在第一次调用获取实例的方法时才创建单例对象。

优点是只有在实际需要的时候才创建对象,避免了不必要的资源浪费。但是,这种方式在多线程环境下可能会出现问题,因为多个线程可能同时判断实例是否为 nullptr,并尝试创建实例,导致创建多个实例。

以下是懒汉式单例的示例代码:

cpp 复制代码
   class Singleton {
   private:
       static Singleton* instance;
       Singleton() {}
   public:
       static Singleton* getInstance() {
           if (instance == nullptr) {
               instance = new Singleton();
           }
           return instance;
       }
   };

   Singleton* Singleton::instance = nullptr;

饿汉式(Eager Initialization):

在程序启动时就创建单例对象。

优点是线程安全,因为对象在程序启动时就已经创建好了,不存在多线程竞争的问题。缺点是如果单例对象的创建比较耗时或者占用较多资源,可能会影响程序的启动性能。

以下是饿汉式单例的示例代码:

cpp 复制代码
   class Singleton {
   private:
       static Singleton* instance;
       Singleton() {}
   public:
       static Singleton* getInstance() {
           return instance;
       }
   };

   Singleton* Singleton::instance = new Singleton();

线程安全的懒汉式(Thread-Safe Lazy Initialization):

使用互斥锁或其他线程同步机制来确保在多线程环境下只有一个实例被创建。

以下是使用互斥锁实现的线程安全懒汉式单例的示例代码:

cpp 复制代码
   #include <mutex>

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

   Singleton* Singleton::instance = nullptr;
   std::mutex Singleton::mutex_instance;

三、单例模式的应用场景

资源管理:

如数据库连接管理器、文件系统访问器等。这些资源通常需要在整个程序中共享,并且只需要一个实例来管理它们的连接和状态。

例如,数据库连接管理器单例可以确保所有的数据库操作都使用同一个连接,避免了频繁地创建和销毁连接所带来的开销。

配置管理:

配置文件读取器单例可以在程序启动时读取配置文件,并在整个程序中提供对配置信息的全局访问。这样可以避免在多个地方重复读取配置文件,提高程序的性能和可维护性。

日志记录:

日志记录器单例可以在整个程序中记录日志信息,确保所有的日志都被写入到同一个文件或输出到同一个控制台。这样可以方便地管理和查看程序的日志信息。

全局状态管理:

在一些复杂的程序中,可能需要一个全局的状态管理器来跟踪程序的状态。单例模式可以用于实现这样的状态管理器,确保在整个程序中只有一个状态管理器实例,方便地管理和共享程序的状态信息。

四、单例模式的优缺点

优点:

单一实例控制:确保一个类只有一个实例,避免了资源的重复分配和冲突。

全局访问:提供了一个方便的全局访问点,使得单例对象可以在程序的任何地方被访问。

易于管理:由于只有一个实例,所以对资源的管理更加简单和高效。

缺点:

违反单一职责原则:

单例类既负责创建和管理自身的实例,又负责提供业务逻辑。这可能导致单例类的职责过于复杂,难以维护和扩展。

测试困难:

由于单例对象在程序中全局可见,并且通常在程序启动时就被创建,这使得对使用单例的代码进行单元测试变得困难。测试时可能需要对单例对象进行模拟或替换,这需要一些额外的技巧和工具。

可能导致内存泄漏:

如果单例对象在程序的整个生命周期中都存在,并且没有正确地释放资源,可能会导致内存泄漏。特别是在长时间运行的程序中,这可能会成为一个问题。

总的来说,C++ 单例模式是一种非常有用的设计模式,它可以在很多场景下提供方便的资源管理和全局访问。但是,在使用单例模式时,需要注意其优缺点,并根据具体的应用场景进行合理的设计和实现。

相关推荐
长按助力退休13 小时前
设计模式实战,这 5 个最常用
设计模式
Carl_奕然15 小时前
【智能体】Agent的四种设计模式之:Multi-Agent Collaboration
设计模式
Lost of 程序猿16 小时前
建造者模式实战:告别“十参数构造函数“的数据导出任务
后端·设计模式·c#·asp.net
SL_staff16 小时前
合规性折旧:当知识资产因主权缺位在审计中‘功能性清零’
java·设计模式·开源
Zane199420 小时前
工厂方法一定比简单工厂高级?三种工厂模式到底该怎么选
设计模式
Hespethorn21 小时前
设计模式是语言缺陷的补丁 —— 从工厂、单例、策略三个模式说起
设计模式
Lost of 程序猿21 小时前
命令模式实战:把“下单后的动作“打包成可执行的任务
后端·设计模式·c#·asp.net·命令模式
一只旭宝21 小时前
【C++复习】四种类型转换 + 常见设计模式 + Redis/MySQL(后端面试复习完整版)
c++·redis·笔记·mysql·设计模式
YYYing.1 天前
【设计模式系列 (四) 】建造者模式
c++·后端·设计模式·建造者模式·c/c++
Zane19942 天前
同一段单例代码,为什么在高并发下偶尔会创建出两个实例?聊聊哪种单例实现才是真的安全
设计模式