前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >C++奇迹之旅:C++的单例模式

C++奇迹之旅:C++的单例模式

作者头像
学习起来吧
发布2025-02-28 09:04:45
发布2025-02-28 09:04:45
20500
代码可运行
举报
文章被收录于专栏:学习C/++学习C/++
运行总次数:0
代码可运行

在 C++ 中,单例模式(Singleton Pattern) 是一种创建型设计模式,其核心目标是确保一个类只有一个实例,并提供一个全局访问点。它常用于管理全局唯一的资源(如配置、日志系统、线程池等)。以下是单例模式的详细实现和分析:

📝 一、单例模式的核心原则
  1. 私有化构造函数:禁止外部通过 new 创建实例。
  2. 禁用拷贝和赋值:防止通过拷贝或赋值生成新对象。
  3. 静态方法获取实例:通过类内部的静态方法控制实例的创建和访问。
  4. 全局唯一性:无论从何处访问,始终返回同一个实例。
二、基础实现(懒汉式,线程不安全)

最简单的单例实现,但存在多线程安全问题:

代码语言:javascript
代码运行次数:0
复制
class Singleton {
private:
    static Singleton* instance;  // 静态成员保存唯一实例
    Singleton() {}               // 私有构造函数
    Singleton(const Singleton&) = delete;            // 禁用拷贝构造
    Singleton& operator=(const Singleton&) = delete; // 禁用赋值操作符

public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;
问题:
  • 线程不安全:若多个线程同时调用 getInstance(),可能创建多个实例。

三、线程安全的懒汉式(双重检查锁定)

通过双重检查锁定(Double-Checked Locking) 优化性能和线程安全:

代码语言:javascript
代码运行次数:0
复制
#include <mutex>

class Singleton {
private:
    static Singleton* instance;
    static std::mutex mtx;  // 互斥锁
    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

public:
    static Singleton* getInstance() {
        if (instance == nullptr) {              // 第一次检查(避免每次加锁)
            std::lock_guard<std::mutex> lock(mtx);
            if (instance == nullptr) {          // 第二次检查(确保线程安全)
                instance = new Singleton();
            }
        }
        return instance;
    }
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;
特点:
  • 双重检查:减少锁的使用频率,提升性能。
  • 互斥锁:确保多线程环境下只创建一个实例。

四、饿汉式(线程安全)

在程序启动时(静态初始化阶段)直接创建实例,天然线程安全:

代码语言:javascript
代码运行次数:0
复制
class Singleton {
private:
    static Singleton* instance;
    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

public:
    static Singleton* getInstance() {
        return instance;
    }
};

// 在类外部直接初始化实例
Singleton* Singleton::instance = new Singleton();
特点:
  • 提前初始化:实例在程序启动时创建,可能增加启动时间。
  • 线程安全:静态初始化在 C++11 后是线程安全的。

五、C++11 后的现代实现(Meyers’ Singleton)

利用局部静态变量的特性,C++11 保证其初始化是线程安全的:

代码语言:javascript
代码运行次数:0
复制
class Singleton {
private:
    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

public:
    static Singleton& getInstance() {
        static Singleton instance;  // C++11 保证线程安全的局部静态初始化
        return instance;
    }
};
优势:
  • 简洁性:无需手动管理锁或指针。
  • 自动析构:实例在程序结束时自动销毁,避免内存泄漏。
  • 返回引用:避免外部误操作指针(如 delete)。

六、单例模式的销毁问题

若单例实例需要显式释放资源,可以添加销毁方法:

代码语言:javascript
代码运行次数:0
复制
class Singleton {
    private:
    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

public:
    static void destroyInstance() {
        if (instance != nullptr) {
            delete instance;
            instance = nullptr;
        }
    }
};
注意:
  • 谨慎使用:单例的生命周期通常与程序一致,手动销毁可能引入风险。

七、单例模式的优缺点

优点

缺点

全局唯一访问点,方便资源管理

隐藏依赖关系,增加代码耦合性

节省内存和资源

难以扩展(如需要多个实例时需重构)

延迟初始化(懒汉式)

多线程环境下需处理同步问题

单元测试困难(全局状态难以隔离或模拟)


八、适用场景
  1. 配置管理:全局唯一的配置对象。
  2. 日志系统:统一管理日志写入。
  3. 数据库连接池:共享数据库连接资源。
  4. 硬件访问:如打印机、GPU 设备等独占资源。

九、完整示例(现代实现)
代码语言:javascript
代码运行次数:0
复制
#include <iostream>

class Logger {
private:
    Logger() {}  // 私有构造函数
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

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

    void log(const std::string& message) {
        std::cout << "Log: " << message << std::endl;
    }
};

int main() {
    Logger::getInstance().log("Application started");
    Logger::getInstance().log("Processing data...");
    return 0;
}
输出:
代码语言:javascript
代码运行次数:0
复制
Log: Application started
Log: Processing data...

以下是一个完整的单例模式实现,包含实例的显式销毁方法,并通过双重检查锁定确保线程安全:


完整代码实现
代码语言:javascript
代码运行次数:0
复制
#include <iostream>
#include <mutex>

class Singleton {
private:
    static Singleton* instance;  // 静态单例实例指针
    static std::mutex mtx;       // 互斥锁(用于线程安全)
    
    // 私有化构造函数和析构函数
    Singleton() {
        std::cout << "Singleton instance created." << std::endl;
    }
    ~Singleton() {
        std::cout << "Singleton instance destroyed." << std::endl;
    }

    // 禁用拷贝和赋值
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

public:
    // 获取单例实例(线程安全)
    static Singleton* getInstance() {
        if (instance == nullptr) {              // 第一次检查(避免每次加锁)
            std::lock_guard<std::mutex> lock(mtx);
            if (instance == nullptr) {          // 第二次检查(确保线程安全)
                instance = new Singleton();
            }
        }
        return instance;
    }

    // 显式销毁单例实例(线程安全)
    static void destroyInstance() {
        std::lock_guard<std::mutex> lock(mtx);  // 加锁保护
        if (instance != nullptr) {
            delete instance;
            instance = nullptr;  // 重置指针,避免悬空引用
        }
    }

    // 示例方法
    void doSomething() {
        std::cout << "Doing something..." << std::endl;
    }
};

// 静态成员初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;

// 使用示例
int main() {
    // 获取单例实例并操作
    Singleton* s1 = Singleton::getInstance();
    s1->doSomething();

    // 显式销毁实例
    Singleton::destroyInstance();

    // 再次获取实例(会创建新实例)
    Singleton* s2 = Singleton::getInstance();
    s2->doSomething();

    return 0;
}

输出结果
代码语言:javascript
代码运行次数:0
复制
Singleton instance created.
Doing something...
Singleton instance destroyed.
Singleton instance created.
Doing something...

关键点说明
  1. 线程安全设计
    • 使用 std::mutexstd::lock_guard 确保 getInstance()destroyInstance() 的线程安全。
    • 双重检查锁定(Double-Checked Locking)减少锁的竞争,提升性能。
  2. 显式销毁逻辑
    • destroyInstance() 方法通过 delete 释放实例内存,并将 instance 指针置为 nullptr
    • 销毁后再次调用 getInstance() 会创建新实例(根据需求可修改为禁止重新创建)。
  3. 资源管理
    • 析构函数 ~Singleton() 可以释放单例持有的资源(如文件句柄、网络连接)。
    • 若不显式调用 destroyInstance(),单例实例将在程序结束时由操作系统回收(可能不符合预期)。

改进建议

若需禁止销毁后重新创建实例,可增加一个标志位:

代码语言:javascript
代码运行次数:0
复制
class Singleton {
private:
    static bool destroyed;  // 新增销毁标志

public:
    static Singleton* getInstance() {
        if (destroyed) {
            throw std::runtime_error("Singleton instance has been destroyed.");
        }
        // ...(原有逻辑)
    }

    static void destroyInstance() {
        // ...(原有逻辑)
        destroyed = true;  // 标记已销毁
    }
};

// 初始化静态成员
bool Singleton::destroyed = false;

🚩总结

单例模式在 C++ 中通过控制实例化过程确保全局唯一性。现代 C++ 推荐使用 局部静态变量 实现(Meyers’ Singleton),既简洁又线程安全。需根据实际需求权衡懒汉式与饿汉式,并注意避免滥用单例导致代码耦合性增加。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-02-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 📝 一、单例模式的核心原则
  • 二、基础实现(懒汉式,线程不安全)
  • 三、线程安全的懒汉式(双重检查锁定)
  • 四、饿汉式(线程安全)
  • 五、C++11 后的现代实现(Meyers’ Singleton)
  • 六、单例模式的销毁问题
  • 七、单例模式的优缺点
  • 八、适用场景
  • 九、完整示例(现代实现)
  • 完整代码实现
  • 输出结果
  • 关键点说明
  • 改进建议
  • 🚩总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档