C++笔记(面向对象)详解单例模式

1. 什么是单例模式?

单例模式确保一个类只有一个实例,并提供一个全局访问点。

生活比喻:

复制代码
公司只有一个CEO:
- 无论哪个部门要找CEO,找到的都是同一个人
- CEO的职位是唯一的,不能有多个CEO
- 大家通过固定的方式联系CEO

2. 基本的单例模式实现

2.1 懒汉式(线程不安全)

复制代码
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;
    }
    
    void showMessage() {
        std::cout << "Singleton Instance: " << this << std::endl;
    }
};

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

3. 线程安全的单例模式

3.1 懒汉式 + 互斥锁(线程安全)

复制代码
#include <mutex>

class ThreadSafeSingleton {
private:
    static ThreadSafeSingleton* instance;
    static std::mutex mtx;  // 互斥锁
    
    ThreadSafeSingleton() {
        std::cout << "Singleton created!" << std::endl;
    }
    
    ~ThreadSafeSingleton() {
        std::cout << "Singleton destroyed!" << std::endl;
    }
    
    // 禁止拷贝和赋值
    ThreadSafeSingleton(const ThreadSafeSingleton&) = delete;
    ThreadSafeSingleton& operator=(const ThreadSafeSingleton&) = delete;

public:
    static ThreadSafeSingleton* getInstance() {
        std::lock_guard<std::mutex> lock(mtx);  // 加锁
        if (instance == nullptr) {
            instance = new ThreadSafeSingleton();
        }
        return instance;
    }
    
    void businessLogic() {
        std::cout << "Doing business logic..." << std::endl;
    }
};

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

3.2 双重检查锁定(优化性能)

复制代码
class DoubleCheckedSingleton {
private:
    static std::atomic<DoubleCheckedSingleton*> instance;
    static std::mutex mtx;
    
    DoubleCheckedSingleton() = default;
    ~DoubleCheckedSingleton() = default;
    DoubleCheckedSingleton(const DoubleCheckedSingleton&) = delete;
    DoubleCheckedSingleton& operator=(const DoubleCheckedSingleton&) = delete;

public:
    static DoubleCheckedSingleton* getInstance() {
        // 第一次检查(不加锁)
        DoubleCheckedSingleton* temp = instance.load(std::memory_order_acquire);
        if (temp == nullptr) {
            std::lock_guard<std::mutex> lock(mtx);
            // 第二次检查(加锁后)
            temp = instance.load(std::memory_order_relaxed);
            if (temp == nullptr) {
                temp = new DoubleCheckedSingleton();
                instance.store(temp, std::memory_order_release);
            }
        }
        return temp;
    }
};

std::atomic<DoubleCheckedSingleton*> DoubleCheckedSingleton::instance{nullptr};
std::mutex DoubleCheckedSingleton::mtx;

4. Meyers' Singleton(最推荐的现代实现)

4.1 局部静态变量方式(C++11后线程安全)

复制代码
class MeyersSingleton {
private:
    MeyersSingleton() {
        std::cout << "Meyers Singleton created!" << std::endl;
    }
    
    ~MeyersSingleton() {
        std::cout << "Meyers Singleton destroyed!" << std::endl;
    }
    
    MeyersSingleton(const MeyersSingleton&) = delete;
    MeyersSingleton& operator=(const MeyersSingleton&) = delete;

public:
    static MeyersSingleton& getInstance() {
        static MeyersSingleton instance;  // C++11保证线程安全
        return instance;
    }
    
    void showAddress() {
        std::cout << "Instance address: " << this << std::endl;
    }
};

5. 实际应用场景示例

5.1 配置文件管理器

复制代码
#include <unordered_map>
#include <string>

class ConfigManager {
private:
    std::unordered_map<std::string, std::string> config;
    
    ConfigManager() {
        // 加载默认配置
        config["database.host"] = "localhost";
        config["database.port"] = "3306";
        config["app.name"] = "MyApp";
        std::cout << "ConfigManager initialized" << std::endl;
    }
    
    ~ConfigManager() = default;
    ConfigManager(const ConfigManager&) = delete;
    ConfigManager& operator=(const ConfigManager&) = delete;

public:
    static ConfigManager& getInstance() {
        static ConfigManager instance;
        return instance;
    }
    
    void setConfig(const std::string& key, const std::string& value) {
        config[key] = value;
    }
    
    std::string getConfig(const std::string& key) const {
        auto it = config.find(key);
        return it != config.end() ? it->second : "";
    }
    
    void printAllConfigs() const {
        for (const auto& pair : config) {
            std::cout << pair.first << " = " << pair.second << std::endl;
        }
    }
};

5.2 日志管理器

复制代码
#include <fstream>
#include <iostream>

class Logger {
private:
    std::ofstream logFile;
    
    Logger() {
        logFile.open("app.log", std::ios::app);
        if (!logFile.is_open()) {
            throw std::runtime_error("Cannot open log file");
        }
        logFile << "=== Application Started ===" << std::endl;
    }
    
    ~Logger() {
        if (logFile.is_open()) {
            logFile << "=== Application Ended ===" << std::endl;
            logFile.close();
        }
    }
    
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

public:
    static Logger& getInstance() {
        static Logger instance;
        return instance;
    }
    
    void log(const std::string& message) {
        if (logFile.is_open()) {
            logFile << message << std::endl;
        }
        std::cout << "[LOG] " << message << std::endl;
    }
    
    void error(const std::string& message) {
        if (logFile.is_open()) {
            logFile << "[ERROR] " << message << std::endl;
        }
        std::cerr << "[ERROR] " << message << std::endl;
    }
};

5.3 数据库连接池

复制代码
#include <vector>
#include <memory>

class DatabaseConnection { /* ... */ };

class ConnectionPool {
private:
    std::vector<std::unique_ptr<DatabaseConnection>> connections;
    size_t maxConnections;
    
    ConnectionPool() : maxConnections(10) {
        initializePool();
    }
    
    void initializePool() {
        for (size_t i = 0; i < maxConnections; ++i) {
            connections.push_back(std::make_unique<DatabaseConnection>());
        }
        std::cout << "Connection pool initialized with " 
                  << maxConnections << " connections" << std::endl;
    }
    
    ~ConnectionPool() {
        connections.clear();
        std::cout << "Connection pool destroyed" << std::endl;
    }
    
    ConnectionPool(const ConnectionPool&) = delete;
    ConnectionPool& operator=(const ConnectionPool&) = delete;

public:
    static ConnectionPool& getInstance() {
        static ConnectionPool instance;
        return instance;
    }
    
    DatabaseConnection* getConnection() {
        // 简单的连接获取逻辑
        if (!connections.empty()) {
            auto connection = connections.back().release();
            connections.pop_back();
            return connection;
        }
        return nullptr;
    }
    
    void returnConnection(DatabaseConnection* connection) {
        if (connection) {
            connections.push_back(std::unique_ptr<DatabaseConnection>(connection));
        }
    }
};

6. 模板单例(通用实现)

cpp

复制代码
template<typename T>
class Singleton {
protected:
    Singleton() = default;
    ~Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

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

// 使用方式
class MyManager : public Singleton<MyManager> {
    friend class Singleton<MyManager>;  // 允许Singleton访问私有构造函数
    
private:
    MyManager() = default;
    
public:
    void doWork() {
        std::cout << "MyManager working..." << std::endl;
    }
};

// 使用
// MyManager& manager = MyManager::getInstance();

7. 单例模式的优缺点

优点:

cpp

复制代码
✅ 严格控制实例数量
✅ 全局访问点方便
✅ 延迟初始化(节省资源)
✅ 避免全局变量污染

缺点:

cpp

复制代码
❌ 违反单一职责原则
❌ 隐藏类间的依赖关系
❌ 对单元测试不友好
❌ 在多线程环境需要特殊处理

8. 测试代码

cpp

复制代码
#include <iostream>
#include <thread>
#include <vector>

void testSingleton() {
    std::cout << "=== 单例模式测试 ===" << std::endl;
    
    // 测试基本单例
    auto& config1 = ConfigManager::getInstance();
    auto& config2 = ConfigManager::getInstance();
    
    std::cout << "config1 address: " << &config1 << std::endl;
    std::cout << "config2 address: " << &config2 << std::endl;
    std::cout << "是否是同一个实例: " << (&config1 == &config2) << std::endl;
    
    // 测试配置功能
    config1.setConfig("server.port", "8080");
    std::cout << "端口配置: " << config2.getConfig("server.port") << std::endl;
}

void threadTask(int id) {
    auto& logger = Logger::getInstance();
    logger.log("Thread " + std::to_string(id) + " is working");
}

void testThreadSafety() {
    std::cout << "\n=== 线程安全测试 ===" << std::endl;
    
    std::vector<std::thread> threads;
    for (int i = 0; i < 5; ++i) {
        threads.emplace_back(threadTask, i);
    }
    
    for (auto& t : threads) {
        t.join();
    }
}

int main() {
    testSingleton();
    testThreadSafety();
    return 0;
}

9. 面试常见问题

Q1:为什么要把构造函数设为private?

A:防止外部直接创建对象,确保只能通过getInstance()获取实例。

Q2:单例模式如何保证线程安全?

A:C++11后,局部静态变量的初始化是线程安全的;或者使用互斥锁。

Q3:单例模式的销毁问题?

A:通常让系统在程序结束时自动销毁,或者实现destroyInstance()方法。

Q4:单例模式 vs 全局变量?

A:单例提供更好的封装性、延迟初始化、继承可能性。

10. 总结

现代C++单例最佳实践

cpp

复制代码
class BestSingleton {
private:
    BestSingleton() = default;
    ~BestSingleton() = default;
    BestSingleton(const BestSingleton&) = delete;
    BestSingleton& operator=(const BestSingleton&) = delete;

public:
    static BestSingleton& getInstance() {
        static BestSingleton instance;  // ✅ 线程安全
        return instance;                // ✅ 自动销毁
    }
};

记住关键点

  • 🎯 私有构造函数 + 删除拷贝操作

  • 🚀 使用局部静态变量(C++11)

  • 🔧 根据需求选择合适的线程安全方案

  • ⚠️ 注意单例的适用场景,不要滥用

相关推荐
疯狂打码的少年11 分钟前
【数据库技术】复习日:关系代数 + SQL + 规范化(整理对比表)
jvm·数据库·笔记·sql
维克兜率天20 分钟前
【维克】特征归一化与标准化:为什么模型对数据的“尺度“很敏感?
人工智能·笔记·python·机器学习·量化
摇滚侠34 分钟前
《SpringBoot 3:入门与应用实战》第 12 章 JDBC 与事务 使用 JdbcTemplate 阅读笔记 31
android·spring boot·笔记
ShineWinsu44 分钟前
对于MySQL:数据库的操作的解析
linux·数据库·c++·mysql·面试·笔试·库的操作
云小逸1 小时前
C++ 第一阶段:对象、内存与生命周期
开发语言·c++
夜航海图1 小时前
S-57 数据解剖:把一个 ENC 文件拆给你看
c++
(Charon)1 小时前
【C++】网络缓冲区设计(三):Chain Buffer链式缓冲区、动态扩容与跨节点读写
服务器·c++
金字塔頂の蝸牛1 小时前
商务日语口语全手册(一)
笔记·日语·语言·外语
从零开始的嵌入式之旅1 小时前
day36
linux·c语言·经验分享·笔记·嵌入式硬件
墨雨晨曦883 小时前
2026/09/07笔记
笔记