如何构造多个单例对象?参数化单例调用返回同一实例问题排查与解决
问题原因分析
你遇到的问题核心在于C++函数内静态变量的初始化规则:函数级别的静态变量,会在第一次进入该函数时完成初始化,之后无论你调用多少次这个函数、传入什么参数,这个静态变量都只会存在一份,不会重新创建。
具体到你的代码,static A u(i);是定义在Inst(int i)函数内部的静态变量:
- 第一次调用
Inst(1)时,程序进入函数,初始化u为A(1),之后这个u就一直存在于程序的整个生命周期; - 当你调用
Inst(2)时,函数再次被执行,但静态变量u已经初始化过了,所以不会再执行A(2)的构造,直接返回已经存在的u(也就是之前创建的no_=1的实例)。
这就是为什么b和c会共享同一个实例的原因。
解决方案:用容器映射参数到实例
要实现「每个参数对应一个唯一实例」的需求,我们可以用一个静态容器(比如std::unordered_map)来存储不同参数对应的实例,让每个参数值对应一个独立的对象。
基础实现版本
#include <bits/stdc++.h> #include <unordered_map> #include <memory> class A { private: A() { printf("init a unparam A\n"); } A(int i) : no_(i) { printf("init a A with no = %d\n", no_); } virtual ~A() = default; // 禁用拷贝构造和赋值运算符,避免实例被意外复制 A(const A&) = delete; A& operator=(const A&) = delete; // 静态容器:存储参数到实例的映射 static std::unordered_map<int, std::unique_ptr<A>> instances_; public: static A& Inst() { static A u; return u; } static A& Inst(int i) { auto it = instances_.find(i); if (it == instances_.end()) { // 如果不存在对应参数的实例,创建并插入容器 instances_[i] = std::make_unique<A>(i); } // 返回对应参数的实例引用 return *instances_[i]; } void print() { printf("my no = %d\n", no_); } int no_; }; // 静态成员必须在类外部初始化 std::unordered_map<int, std::unique_ptr<A>> A::instances_;
测试运行结果
当你运行原来的main函数时,输出会符合你的预期:
init a unparam A my no = 0 init a A with no = 1 my no = 1 init a A with no = 2 my no = 2
线程安全优化(可选)
如果你的程序是多线程环境,上面的基础版可能会出现竞争条件(比如多个线程同时调用Inst(i)且该参数的实例尚未创建时,可能会重复创建)。这时可以加上互斥锁保证线程安全:
#include <mutex> class A { private: // ... 其他成员不变 static std::mutex mutex_; // 新增互斥锁 public: static A& Inst(int i) { // 加锁确保同一时间只有一个线程能修改容器 std::lock_guard<std::mutex> lock(mutex_); auto it = instances_.find(i); if (it == instances_.end()) { instances_[i] = std::make_unique<A>(i); } return *instances_[i]; } // ... }; // 初始化互斥锁 std::mutex A::mutex_;
总结
- 函数内的静态变量是函数级别的单例,与传入的参数无关,只会初始化一次;
- 要实现「参数化单例」,需要用容器来维护参数和实例的映射关系;
- 记得禁用拷贝构造和赋值运算符,防止实例被意外复制;
- 多线程场景下需要添加互斥锁保证线程安全。
内容的提问来源于stack exchange,提问作者nick
相关产品推荐
相关产品推荐

