23种设计模式 - 单例模式(Singleton)

单例模式(Singleton)------ 全局就我一个,多了没有,爱用不用

大白话解释

想象一个公司只有一台打印机。不管谁来打印,用的都是同一台机器。如果每次有人要打印就新买一台,那公司得破产。

单例模式就是:整个程序运行期间,某个类只能有一个实例,所有人都共用这一个。

常见场景:

  • 配置文件管理器(配置只需加载一次)
  • 日志系统(全局写同一个日志)
  • 数据库连接池(连接资源宝贵)
  • 窗口管理器(只有一个主窗口)

核心思路

  1. 把构造函数藏起来 (设为 private),外面没法 new 出来
  2. 自己内部保存唯一实例
  3. 提供一个全局访问点(静态方法)

C++ 代码示例

版本一:懒汉式(用到的时候才创建)

cpp 复制代码
#include <iostream>
#include <string>

class ConfigManager {
private:
    // 私有构造函数,外部无法直接 new
    ConfigManager() {
        std::cout << "配置管理器初始化完成!\n";
        dbHost = "localhost";
        dbPort = 3306;
    }

    // 禁止拷贝和赋值(C++11 写法)
    ConfigManager(const ConfigManager &) = delete;
    ConfigManager &operator=(const ConfigManager &) = delete;

    std::string dbHost;
    int dbPort;

public:
    // 全局访问点:第一次调用时创建实例
    static ConfigManager &instance() {
        static ConfigManager instance; // C++11 保证线程安全
        return instance;
    }

    std::string getDbHost() const { return dbHost; }
    int getDbPort() const { return dbPort; }

    void setDbHost(const std::string& host) { dbHost = host; }
};

int main() {
    // 获取单例
    ConfigManager &config1 = ConfigManager::instance();
    ConfigManager &config2 = ConfigManager::instance();

    // 验证是同一个对象
    std::cout << "是同一个对象吗?" << (&config1 == &config2 ? "是" : "否") << "\n";

    // 通过 config1 修改
    config1.setDbHost("192.168.0.100");

    // config2 也能看到修改(因为本来就是同一个)
    std::cout << "config2 的数据库地址:" << config2.getDbHost() << "\n";

    return 0;
}

输出:

复制代码
配置管理器初始化完成!
是同一个对象吗?是
config2 的数据库地址:192.168.0.100

版本二:线程安全的指针版(多线程环境)

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

class Logger {
private:
    static Logger *instance;
    static std::mutex mtx;

    Logger() {
        std::cout << "日志系统启动\n";
    }

public:
    static Logger *instance() {
        if (instance == nullptr) {           // 第一次检查(提高效率)
            std::lock_guard<std::mutex> lock(mtx);
            if (instance == nullptr) {       // 第二次检查(保证线程安全)
                instance = new Logger();
            }
        }
        return instance;
    }

    void log(const std::string &msg) {
        std::cout << "[LOG] " << msg << "\n";
    }
};

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

int main() {
    Logger::instance()->log("程序启动");
    Logger::instance()->log("用户登录成功");
    return 0;
}

优缺点

说明
✅ 优点 节约资源,避免重复创建
✅ 优点 全局共享资源,防止冲突
✅ 优点 控制访问,全局只有一个入口
❌ 缺点 没有接口,没有继承,很难扩展新功能
❌ 缺点 单元测试困难(全局状态难以隔离)
❌ 缺点 生命周期管理不灵活,一旦创建,无法手动销毁、重启
❌ 缺点 到处都能直接调用,代码可读性变差,难以清理依赖
相关推荐
重生之我是Java开发战士14 天前
【Java SE】多线程(三):单例模式,阻塞队列,线程池与定时器
java·javascript·单例模式
许彰午15 天前
34_Java设计模式之单例模式
java·单例模式·设计模式
罗超驿17 天前
10.Java单例模式全解析:饿汉式与懒汉式实现及线程安全深度剖析
安全·单例模式·javaee
布朗克16817 天前
33 设计模式精讲
java·单例模式·设计模式
雨浓YN17 天前
基于设计模式的Winform软件框架-01Xml\Log\Ini日志(单例模式+生产者消费者模式)
单例模式·设计模式
仙俊红18 天前
Java 单例模式:类里面为什么可以有自己类型的字段?
java·开发语言·单例模式
swordbob18 天前
prototype 注入到 singleton 里,prototype是否还是线程安全的
安全·spring·单例模式·原型模式
谁似人间西林客20 天前
工业大数据实战:看中国智造如何用数据驱动效率革命
大数据·单例模式
张小姐的猫20 天前
【Linux】多线程 —— 线程池 | 单例模式 | 常见锁
linux·运维·服务器·c++·单例模式·设计模式·策略模式
Java面试题总结21 天前
双重检验锁的单例模式在高并发下的可见性问题
单例模式