题目
编写程序,实现现代 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;"
两份引用是否指向同一实例: 【完全一致,全局唯一单例生效】