You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于unique_ptr实现C++单例类的代码技术咨询

关于用unique_ptr实现C++单例的问题修正

首先得指出你这段代码里几个核心的问题,这些问题会导致编译失败或者单例逻辑失效:


原代码的主要问题

  1. 返回std::unique_ptr<A>的设计错误
    unique_ptr是独占所有权的智能指针,每次返回它都会转移所有权,这意味着第一次调用getInstance后,静态成员single会变成空指针,后续调用就再也拿不到已创建的实例了——这完全违背了单例“全局唯一实例”的初衷。

  2. std::make_unique无法访问私有构造函数
    因为std::make_unique是全局命名空间下的函数,它没办法访问类A的私有构造函数,直接写std::make_unique<A>(log)会触发编译错误。

  3. 冗余的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:40:27