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)

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

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

相关推荐
吗~喽3 小时前
【C++】内存管理
c++
晓py3 小时前
SQL调优专题笔记:打造你的数据库性能优化思维体系
数据库·笔记·sql
三无少女指南3 小时前
在 Ubuntu 上使用 Docker 部署思源笔记:一份详尽的实践教程以及常见错误汇总
笔记·ubuntu·docker
上去我就QWER4 小时前
解锁Qt元对象系统:C++编程的超强扩展
c++·qt
freedom_1024_4 小时前
【c++ qt】QtConcurrent与QFutureWatcher:实现高效异步计算
java·c++·qt
路弥行至4 小时前
C语言入门教程 | 第七讲:函数和程序结构完全指南
c语言·经验分享·笔记·其他·算法·课程设计·入门教程
minji...4 小时前
C++ 模板进阶
开发语言·c++
凌然先生4 小时前
12.如何利用ArcGIS进行基本的空间数据格式转换
经验分享·笔记·arcgis·电脑
AC是你的谎言5 小时前
c++仿muduo库实现高并发服务器--connection类
linux·服务器·c++·学习