题目

编写程序,实现现代 C++ 极其推崇的单例设计模式——Meyers' Singleton(迈耶斯单例)。将构造函数、拷贝构造函数与拷贝赋值运算符彻底私有化或 = delete;仅通过静态公有成员函数 getInstance() 返回局部静态变量(Local Static Variable)的引用。深入解析 C++11 标准对局部静态变量初始化在多线程环境下的绝对线程安全保证(Magic Statics)。

输入格式:无需外部输入。

输出格式:输出多次调用单例实例所返回的物理内存地址恒定性日志。

数据范围与约定:C++11 局部静态变量并发初始化保证、单例生命周期。

思路与算法

1. 双重检查锁(DCLP)的淘汰
在 C++03 中手写单例常需要裸指针、双重条件检查与互斥锁,极易因指令重排(Instruction Reordering)产生多线程未定义行为。

2. C++11 Magic Statics 标准保障
C++11 §6.7 明确规定:如果控制流在变量初始化期间并发进入声明处,并发执行线程将自动阻塞等待,直到初始化圆满完成。因此局部静态单例原生具备绝对的并发线程安全!

3. 三禁用铁律
单例类必须:①构造函数私有化 private: Singleton();;②拷贝构造 = delete;③拷贝赋值 = delete,保证全系统物理唯一性。

完整程序

#include <iostream>

class DatabaseConnectionPool {
private:
    int maxConnections_;

    // 1. 构造函数私有化
    DatabaseConnectionPool() : maxConnections_(20) {
        std::cout << "  [初始化] 数据库连接池全局唯一实例创建完成,最大连接数: " 
                  << maxConnections_ << "\n";
    }

public:
    // 2. 彻底禁止拷贝与赋值
    DatabaseConnectionPool(const DatabaseConnectionPool&) = delete;
    DatabaseConnectionPool& operator=(const DatabaseConnectionPool&) = delete;

    // 3. 核心:Meyer's Singleton 局部静态变量
    static DatabaseConnectionPool& getInstance() {
        // C++11 保证下述初始化语句具备原生的并发线程安全性
        static DatabaseConnectionPool instance;
        return instance;
    }

    void executeQuery(const std::string& sql) {
        std::cout << "  [执行 SQL] 连接池分发查询 -> \"" << sql << "\"\n";
    }

    const void* getMemoryAddress() const {
        return this;
    }
};

int main() {
    std::cout << "--- 1. 首次获取单例实例 ---\n";
    DatabaseConnectionPool& pool1 = DatabaseConnectionPool::getInstance();
    pool1.executeQuery("SELECT * FROM users WHERE status = 1;");

    std::cout << "\n--- 2. 二次获取单例实例并校验内存地址 ---\n";
    DatabaseConnectionPool& pool2 = DatabaseConnectionPool::getInstance();
    pool2.executeQuery("UPDATE configs SET valid = 1;");

    std::cout << "\n两份引用是否指向同一实例: "
              << (&pool1 == &pool2 ? "【完全一致,全局唯一单例生效】" : "异常") << "\n";

    return 0;
}

运行示例

输出:

--- 1. 首次获取单例实例 ---
  [初始化] 数据库连接池全局唯一实例创建完成,最大连接数: 20
  [执行 SQL] 连接池分发查询 -> "SELECT * FROM users WHERE status = 1;"

--- 2. 二次获取单例实例并校验内存地址 ---
  [执行 SQL] 连接池分发查询 -> "UPDATE configs SET valid = 1;"

两份引用是否指向同一实例: 【完全一致,全局唯一单例生效】