基于unique_ptr实现C++单例类的代码技术咨询
关于用
unique_ptr实现C++单例的问题修正 首先得指出你这段代码里几个核心的问题,这些问题会导致编译失败或者单例逻辑失效:
原代码的主要问题
返回
std::unique_ptr<A>的设计错误unique_ptr是独占所有权的智能指针,每次返回它都会转移所有权,这意味着第一次调用getInstance后,静态成员single会变成空指针,后续调用就再也拿不到已创建的实例了——这完全违背了单例“全局唯一实例”的初衷。std::make_unique无法访问私有构造函数
因为std::make_unique是全局命名空间下的函数,它没办法访问类A的私有构造函数,直接写std::make_unique<A>(log)会触发编译错误。冗余的
instanceFlag与线程安全问题
其实完全可以通过检查single是否为空来判断实例是否已创建,额外的instanceFlag纯属多余。另外当前的判断逻辑没有任何线程安全保护,多线程环境下可能会创建多个实例,破坏单例的唯一性。
修正后的实现(基于unique_ptr)
下面是修复后的代码,解决了上述所有问题:
#include <iostream> #include <memory> #include <mutex> class A { public: // 返回实例的引用,避免所有权转移 static A& getInstance(int log); ~A() = default; // 示例成员函数,用于验证实例状态 void showLog() const { std::cout << "当前实例的log值:" << mLog << std::endl; } private: static std::unique_ptr<A> single; static std::mutex mtx; // 线程安全锁 int mLog; // 私有构造,禁止外部直接创建对象 explicit A(int log) : mLog(log) {} // 禁用拷贝构造与赋值,确保单例唯一性 A(const A&) = delete; A& operator=(const A&) = delete; }; // 初始化静态成员 std::unique_ptr<A> A::single = nullptr; std::mutex A::mtx; A& A::getInstance(int log) { // 双重检查锁定:先快速判断,避免每次都加锁影响性能 if (!single) { std::lock_guard<std::mutex> lock(mtx); if (!single) { // 因为make_unique无法访问私有构造,这里直接用new创建后交给unique_ptr管理 single.reset(new A(log)); } } return *single; } // 测试代码 int main() { A& inst1 = A::getInstance(10); inst1.showLog(); // 输出:当前实例的log值:10 // 第二次调用传入的log不会生效,因为实例已经创建 A& inst2 = A::getInstance(20); inst2.showLog(); // 输出:当前实例的log值:10 return 0; }
关键修改说明
- 返回
A&而非unique_ptr:所有调用者共享同一个实例的引用,不会触发所有权转移,保证单例的唯一性。 - 移除
instanceFlag:直接通过single是否为空判断实例状态,逻辑更简洁可靠。 - 添加线程安全保护:用
std::mutex和双重检查锁定,确保多线程环境下只会创建一个实例(C++11及以后的内存模型保证了这种写法的正确性)。 - 禁用拷贝与赋值:这是单例模式的标准操作,防止通过拷贝或赋值创建新的实例。
- 改用
single.reset(new A(log)):绕开make_unique无法访问私有构造的问题,让unique_ptr接管手动new出来的对象。
更简洁的C++11单例实现(推荐)
如果你不需要手动管理unique_ptr,可以利用C++11标准中静态局部变量初始化线程安全的特性,写出更简洁的单例代码:
#include <iostream> class A { public: static A& getInstance(int log) { // 静态局部变量只会在第一次调用时初始化,C++11保证这个过程线程安全 static A instance(log); return instance; } ~A() = default; void showLog() const { std::cout << "当前实例的log值:" << mLog << std::endl; } private: int mLog; explicit A(int log) : mLog(log) {} A(const A&) = delete; A& operator=(const A&) = delete; }; // 测试代码 int main() { A& inst1 = A::getInstance(10); inst1.showLog(); A& inst2 = A::getInstance(20); inst2.showLog(); return 0; }
这种写法不仅避免了unique_ptr带来的构造函数访问问题,还省去了手动加锁的代码,同时完全保证线程安全,是C++11以后实现单例的首选方式。
内容的提问来源于stack exchange,提问作者Programmer
相关产品推荐
相关产品推荐

