【C++设计模式】第一篇:单例模式(Singleton)

注意:复现代码时,确保 VS2022 使用 C++17/20 标准以支持现代特性。

确保全局唯一实例的线程安全实现


1. 模式定义与用途​

核心目标 :保证一个类仅有一个实例,并提供全局访问点。
常见场景:

  • 日志系统(避免多个日志实例竞争文件资源)
  • 配置管理(统一读取和修改全局配置)
  • 硬件接口访问(如打印机设备控制)

2. 线程安全的现代 C++ 实现​

2.1 方法一:局部静态变量(Meyers' Singleton,C++11起线程安全)

cpp 复制代码
#include <iostream>

class Singleton {
public:
    // 删除拷贝构造函数和赋值操作符
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 获取唯一实例
    static Singleton& getInstance() {
        static Singleton instance; // 线程安全(C++11起)
        return instance;
    }

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

private:
    // 私有构造函数,禁止外部创建实例
    Singleton() = default;
};

//测试代码
int main() {
    Singleton::getInstance().logMessage("System started.");
    Singleton::getInstance().logMessage("User logged in.");

    // 验证实例地址唯一
    Singleton& s1 = Singleton::getInstance();
    Singleton& s2 = Singleton::getInstance();
    std::cout << "Addresses equal? " << (&s1 == &s2 ? "Yes" : "No") << std::endl; // 输出:Yes

    return 0;
}

代码解析:

  • 使用局部静态变量 static Singleton instance,C++11 标准保证其初始化线程安全。
  • 删除拷贝构造函数和赋值操作符,防止意外复制实例。
  • 私有构造函数确保外部无法直接创建对象。

2.2 方法二:std::call_once(适用于需要动态初始化的场景)

cpp 复制代码
#include <iostream>
#include <mutex>
#include <memory>

class Singleton {
public:
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    static Singleton& getInstance() {
        std::call_once(initFlag, []() {
            instance = std::unique_ptr<Singleton>(new Singleton());
        });
        return *instance;
    }

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

private:
    Singleton() = default;
    static std::unique_ptr<Singleton> instance;
    static std::once_flag initFlag;
};

// 静态成员初始化
std::unique_ptr<Singleton> Singleton::instance = nullptr;
std::once_flag Singleton::initFlag;

// 测试代码(同上)

代码解析:

  • std::call_once 保证初始化代码仅执行一次,即使多线程环境下也安全。
  • 使用 std::unique_ptr 管理实例,避免内存泄漏。

3. 传统双检锁(Double-Checked Locking)的问题与改进​

旧版 C++(非线程安全)​:

cpp 复制代码
// 警告:C++11 前的实现可能存在线程安全问题!
Singleton* Singleton::getInstance() {
    if (instance == nullptr) {               // 第一次检查
        std::lock_guard<std::mutex> lock(mutex);
        if (instance == nullptr) {           // 第二次检查
            instance = new Singleton();
        }
    }
    return instance;
}

问题:

  • 内存读写顺序问题(指令重排可能导致未初始化完成的对象被访问)。

改进(C++11 起使用原子变量)​:

cpp 复制代码
#include <atomic>

class Singleton {
    static std::atomic<Singleton*> instance;
    static std::mutex mutex;

public:
    static Singleton* getInstance() {
        Singleton* tmp = instance.load(std::memory_order_acquire);
        if (tmp == nullptr) {
            std::lock_guard<std::mutex> lock(mutex);
            tmp = instance.load(std::memory_order_relaxed);
            if (tmp == nullptr) {
                tmp = new Singleton();
                instance.store(tmp, std::memory_order_release);
            }
        }
        return tmp;
    }
};

4. 应用场景示例:日志系统

cpp 复制代码
// 在 Singleton 类中添加日志文件操作
#include <fstream>

class Singleton {
    // ...(同上)
private:
    std::ofstream logFile;

    Singleton() {
        logFile.open("app.log", std::ios::app);
    }

public:
    void logMessage(const std::string& message) {
        if (logFile.is_open()) {
            logFile << message << std::endl;
        }
    }

    ~Singleton() {
        logFile.close();
    }
};

5. 单例模式的优缺点

优点​ ​缺点
全局唯一实例,避免资源冲突 隐藏依赖关系,降低代码可测试性
延迟初始化(节省内存) 长期持有资源可能影响程序退出行为

6. 总结与调试技巧

  • 验证线程安全:在 VS2022 中使用多线程调试,观察实例地址是否唯一。
  • 查看静态变量生命周期:通过断点检查局部静态变量的初始化时机。
  • 禁用拷贝操作:务必删除拷贝构造函数和赋值操作符以防止意外复制
相关推荐
利刃大大5 分钟前
【网络编程】二、UDP网络套接字编程详解
网络·c++·网络协议·udp·socket·套接字
潇-xiao35 分钟前
Qt 通过控件按钮实现hello world + 命名规范(7)
c++·笔记·qt
吃个早饭1 小时前
第十六届蓝桥杯大赛软件赛C/C++大学B组部分题解
c语言·c++·蓝桥杯
长流小哥2 小时前
MySQL C API高效编程:C语言实现数据库操作的深入解析
数据库·c++·mysql
DARLING Zero two♡2 小时前
C++漫步结构与平衡的殿堂:AVL树
c++·二叉搜索树·avl树
李匠20242 小时前
C++GO语言微服务基础技术①
开发语言·c++·微服务·golang
蓝心湄2 小时前
C++-缺省参数
开发语言·c++
六bring个六3 小时前
图形渲染+事件处理最终版
c++·qt·图形渲染·opengl
半青年3 小时前
基于Qt开发的http/https客户端
java·c++·qt·网络协议·http·https·信息与通信
牛奶咖啡133 小时前
学习设计模式《八》——原型模式
学习·设计模式·原型模式·浅度克隆·深度克隆·c#的克隆·原型管理器