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

如何使用shared_ptr实现带私有析构函数的C++单例?

解决C++17中shared_ptr版单例的私有析构函数访问问题

错误原因

你遇到的编译错误本质是:std::shared_ptr的默认删除器无法访问Singleton2的私有析构函数。当shared_ptr销毁托管对象时,会调用标准库提供的std::default_delete<T>,而这个删除器的代码不属于你的类作用域,因此没有权限调用私有析构函数。

你的unique_ptr版本能正常工作,大概率是因为其删除器的调用上下文处于类的作用域内,或是你已经为它配置了可访问私有析构的自定义删除器。

可行解决方案

方案1:自定义内部删除器

在单例类内部定义删除器结构体,利用内部类天然的访问权限调用私有析构:

#include <memory>

class Singleton2 {
private:
    Singleton2() = default;
    ~Singleton2() = default;

    // 内部删除器,可直接访问私有成员
    struct Deleter {
        void operator()(Singleton2* ptr) const {
            delete ptr;
        }
    };

public:
    static std::shared_ptr<Singleton2> getInstance() {
        static std::shared_ptr<Singleton2> instance(new Singleton2(), Deleter());
        return instance;
    }

    // 禁止拷贝、移动操作
    Singleton2(const Singleton2&) = delete;
    Singleton2& operator=(const Singleton2&) = delete;
    Singleton2(Singleton2&&) = delete;
    Singleton2& operator=(Singleton2&&) = delete;
};

方案2:将默认删除器设为友元

直接授权std::default_delete<Singleton2>访问私有析构,让shared_ptr可以使用默认删除器:

#include <memory>

class Singleton2 {
private:
    Singleton2() = default;
    ~Singleton2() = default;

    // 让标准库默认删除器有权访问私有析构
    friend std::default_delete<Singleton2>;

public:
    static std::shared_ptr<Singleton2> getInstance() {
        static std::shared_ptr<Singleton2> instance(new Singleton2());
        return instance;
    }

    // 禁止拷贝、移动操作
    Singleton2(const Singleton2&) = delete;
    Singleton2& operator=(const Singleton2&) = delete;
    Singleton2(Singleton2&&) = delete;
    Singleton2& operator=(Singleton2&&) = delete;
};

方案3:静态局部对象+空删除器

如果不需要动态分配内存,可使用静态局部对象,配合空删除器避免shared_ptr误删(静态对象会在程序结束时自动销毁):

#include <memory>

class Singleton2 {
private:
    Singleton2() = default;
    ~Singleton2() = default;

public:
    static std::shared_ptr<Singleton2> getInstance() {
        // C++11及以后,静态局部对象初始化线程安全
        static Singleton2 instance;
        // 空删除器:告诉shared_ptr无需销毁对象
        static std::shared_ptr<Singleton2> ptr(&instance, [](Singleton2*){});
        return ptr;
    }

    // 禁止拷贝、移动操作
    Singleton2(const Singleton2&) = delete;
    Singleton2& operator=(const Singleton2&) = delete;
    Singleton2(Singleton2&&) = delete;
    Singleton2& operator=(Singleton2&&) = delete;
};

各方案对比

  • 方案1:自定义删除器逻辑独立,不依赖标准库细节,适合需要自定义销毁逻辑的场景。
  • 方案2:代码最简洁,直接利用友元授权,适合常规单例场景。
  • 方案3:无动态分配开销,性能最优,适合对内存分配敏感的场景,但需注意静态对象的生命周期管理。

内容的提问来源于stack exchange,提问作者StarvingDays

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 05:15:43