授课语音

单例模式(Singleton Pattern)

1. 介绍

单例模式是一种设计模式,其主要目的是确保一个类只有一个实例,并提供一个全局访问点来访问该实例。

单例模式的核心概念

  1. 唯一性:确保一个类只有一个实例。即使多个线程尝试创建该类的对象,也只会存在一个实例。
  2. 全局访问:单例模式提供一个全局访问点来获取这个唯一实例,通常通过静态方法实现,以便在任何地方都可以访问该实例。
  3. 延迟初始化:在一些实现中,单例实例在第一次被请求时才被创建,这样可以避免不必要的开销。这一过程称为延迟初始化(lazy initialization)。

单例模式的变体

  1. 懒汉式单例(Lazy Initialization):在第一次被访问时创建实例,优点是延迟初始化,但需要处理多线程环境下的同步问题。
  2. 饿汉式单例(Eager Initialization):在类加载时就创建实例,这种方式简单且线程安全,但如果实例创建的开销较大而应用从未使用该实例,则可能会造成资源浪费。
  3. 双重检查锁(Double-Check Locking):在多线程环境下,通过双重检查和锁机制来确保实例创建的线程安全性。通常结合volatile关键字使用以避免指令重排序带来的问题。
  4. 登记式单例(Registry-based Singleton):使用注册表管理单例实例。通过静态方法或其他机制从注册表中获取单例实例,适合需要支持动态实例管理的场景。
  5. 枚举单例(Enum Singleton):使用枚举类型实现单例模式,这是 Java 推荐的实现方式,能够确保线程安全,并防止反序列化攻击。

实际框架中的应用

单例模式在许多实际框架和系统中有广泛的应用。以下是一些常见的应用场景:

  1. 配置管理:在C++框架中,配置管理类通常使用单例模式。例如,某些C++库或框架的配置类可能实现为单例,以确保在整个应用程序中配置的一致性。

  2. 数据库连接池:数据库连接池通常使用单例模式来管理连接。例如,使用C++的数据库库(如SOCI或libpqxx)时,连接池可能会实现为单例,以确保高效管理数据库连接。

  3. 日志记录:C++中的日志记录器通常使用单例模式来确保日志记录的一致性。例如,某些C++日志库(如spdlog或glog)可以配置为单例,以便在整个应用程序中共享同一个日志记录器实例。

  4. 缓存系统:缓存系统如单例模式的实现通常确保在整个应用程序中只有一个缓存实例,从而提高性能和资源利用率。可以使用C++的标准库或第三方库实现这样的缓存。

  5. C++应用:在C++应用中,某些全局配置或服务可能被设计为单例,以确保在整个应用程序上下文中只存在一个实例,从而保证共享服务和配置的一致性。

这些场景展示了单例模式在C++平台上的广泛应用,能够有效管理资源和提供一致性。

2. 代码案例

#include <iostream>
#include <memory>
#include <map>
#include <mutex>

class SingletonExamples {
public:
    // 1. 饿汉式单例(Eager Initialization)
    class EagerSingleton {
    private:
        static EagerSingleton instance; // 实例
        std::string value;

        // 私有构造方法
        EagerSingleton() : value("EagerSingleton Value") {}

    public:
        // 提供全局访问点
        static EagerSingleton& GetInstance() {
            return instance;
        }

        // 显示实例的值
        void ShowValue() {
            std::cout << "EagerSingleton value: " << value << std::endl;
        }
    };
    
    EagerSingleton EagerSingleton::instance;

    // 2. 懒汉式单例(Lazy Initialization)
    class LazySingleton {
    private:
        static LazySingleton* instance; // 存储单例实例
        static std::mutex mutex; // 锁对象
        std::string value;

        // 私有构造方法
        LazySingleton() : value("LazySingleton Value") {}

    public:
        // 提供全局访问点,使用懒加载方式
        static LazySingleton* GetInstance() {
            if (instance == nullptr) { // 第一次检查
                std::lock_guard<std::mutex> lock(mutex); // 加锁
                if (instance == nullptr) { // 第二次检查
                    instance = new LazySingleton();
                }
            }
            return instance;
        }

        // 显示实例的值
        void ShowValue() {
            std::cout << "LazySingleton value: " << value << std::endl;
        }
    };

    LazySingleton* LazySingleton::instance = nullptr;
    std::mutex LazySingleton::mutex;

    // 3. 双重检查锁(Double-Check Locking)
    class DoubleCheckLockingSingleton {
    private:
        static DoubleCheckLockingSingleton* instance; // 存储单例实例
        static std::mutex mutex; // 锁对象
        std::string value;

        // 私有构造方法
        DoubleCheckLockingSingleton() : value("DoubleCheckLockingSingleton Value") {}

    public:
        // 提供全局访问点,使用双重检查锁机制
        static DoubleCheckLockingSingleton* GetInstance() {
            if (instance == nullptr) { // 第一次检查
                std::lock_guard<std::mutex> lock(mutex); // 加锁
                if (instance == nullptr) { // 第二次检查
                    instance = new DoubleCheckLockingSingleton();
                }
            }
            return instance;
        }

        // 显示实例的值
        void ShowValue() {
            std::cout << "DoubleCheckLockingSingleton value: " << value << std::endl;
        }
    };

    DoubleCheckLockingSingleton* DoubleCheckLockingSingleton::instance = nullptr;
    std::mutex DoubleCheckLockingSingleton::mutex;

    // 4. 登记式单例(Registry-based Singleton)
    class RegistrySingleton {
    private:
        static std::map<std::string, RegistrySingleton*> registry; // 注册表
        std::string value;

        // 私有构造方法
        RegistrySingleton() : value("RegistrySingleton Value") {}

    public:
        // 提供全局访问点,通过键从注册表获取实例
        static RegistrySingleton* GetInstance(const std::string& key) {
            if (registry.find(key) == registry.end()) {
                registry[key] = new RegistrySingleton(); // 动态管理单例
            }
            return registry[key];
        }

        // 显示实例的值
        void ShowValue() {
            std::cout << "RegistrySingleton value: " << value << std::endl;
        }
    };

    std::map<std::string, RegistrySingleton*> RegistrySingleton::registry;

    // 5. 枚举单例(Enum Singleton)
    enum class EnumSingleton {
        INSTANCE // 定义唯一的枚举实例
    };

    // 显示实例的值
    static void ShowEnumValue() {
        std::cout << "EnumSingleton value: EnumSingleton Value" << std::endl;
    }
};

// 示例代码
int main() {
    // 1. 饿汉式单例
    SingletonExamples::EagerSingleton::GetInstance().ShowValue();

    // 2. 懒汉式单例
    SingletonExamples::LazySingleton::GetInstance()->ShowValue();

    // 3. 双重检查锁
    SingletonExamples::DoubleCheckLockingSingleton::GetInstance()->ShowValue();

    // 4. 登记式单例
    SingletonExamples::RegistrySingleton::GetInstance("key1")->ShowValue();

    // 5. 枚举单例
    SingletonExamples::ShowEnumValue();

    return 0;
}

详细中文注释

  • 类说明:每个单例实现类都有一个内部初始化方法,用于设置初始值。在每个 ShowValue 方法中打印该实例的值,以验证实例的唯一性。
  • 示例代码:在主程序中,我们创建每种单例的实例并调用相应的方法以验证单例模式的有效性。
去1:1私密咨询

系列课程: