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

C++自定义智能指针模板类转型后虚函数访问冲突问题求解

问题解答

1. 更合适的现成方案

完全不需要自行实现智能指针,用C++标准库组件即可组合出满足你需求的能力:
你需要的核心特性是「库侧可主动作废所有用户持有的指针副本、引用计数自动释放、支持多态转型」,使用std::shared_ptr<std::unique_ptr<T>>即可实现:

  • 库内部持有主控的std::shared_ptr<std::unique_ptr<接口类>>实例
  • 给用户返回该shared_ptr的拷贝
  • 需要主动释放对象时,直接调用主控指针->reset(),所有用户持有的副本里的unique_ptr都会同步变为空,用户通过if (*用户指针)即可判断有效性
  • 多态转型直接使用标准库提供的std::static_pointer_cast、std::dynamic_pointer_cast即可,所有边界情况标准库都已处理,同时原生支持线程安全、异常安全,可靠性远高于自行实现的版本。

2. 现有实现的合理性问题

你的设计思路是对的,但代码存在几个明显缺陷:

  • 引用计数不是原子类型,多线程场景下会出现竞争,导致double free、内存泄漏等问题
  • 拷贝赋值运算符没有处理自赋值场景:自赋值时会先减少引用计数把当前pw释放,后续访问野指针直接触发崩溃
  • 用reinterpret_cast强制转换不同类型的pointer_wrapper指针属于完全的未定义行为:多态类父子类指针转型往往需要地址偏移,直接转包装类指针会导致内部存储的对象指针没有做对应偏移,调用虚函数时访问错误的虚表地址,就是你遇到的执行权限违例的根本原因
  • 没有实现移动构造、移动赋值,会产生不必要的引用计数增减操作,性能有损耗
  • 如果接口基类没有定义虚析构函数,destroy方法删除基类指针时会产生未定义行为。

3. 正确的convert实现方法

核心改造思路是把引用计数和类型解耦,抽出非模板的基类存储引用计数,模板子类只负责类型相关的对象操作:

第一步:定义无类型的引用计数基类

#include <atomic>
#include <type_traits>
#include <stdexcept>

class pointer_wrapper_base {
private:
    std::atomic<int32_t> reference_count = 1;
public:
    void increase_reference_count() { 
        reference_count.fetch_add(1, std::memory_order_relaxed); 
    }
    void decrease_reference_count() {
        if(reference_count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
            delete this;
        }
    }
    int32_t get_reference_count() const { 
        return reference_count.load(std::memory_order_acquire); 
    }
    virtual void destroy() = 0;
    virtual bool is_valid() const = 0;
    virtual ~pointer_wrapper_base() = default;
};

第二步:修改模板化的包装类继承基类

template<class T>
class pointer_wrapper : public pointer_wrapper_base {
private:
    T* raw_pointer;
public:
    explicit pointer_wrapper(T* const raw_pointer): raw_pointer(raw_pointer) { }
    T* get_raw_pointer() const { return raw_pointer; }
    void destroy() override {
        if(raw_pointer != nullptr) {
            delete raw_pointer;
            raw_pointer = nullptr;
        }
    }
    bool is_valid() const override { return raw_pointer != nullptr; }
    ~pointer_wrapper() override { destroy(); }
};

第三步:修改ptr类实现安全的convert方法

template<class T> 
class ptr {
private:
    pointer_wrapper_base* pw_base = nullptr;
    T* cached_raw = nullptr; // 缓存转型后的指针,避免重复转换

    // 内部构造方法,供convert调用
    template<class X>
    ptr(pointer_wrapper_base* base, X* raw) : pw_base(base), cached_raw(static_cast<T*>(raw)) {
        if(pw_base) pw_base->increase_reference_count();
    }

public:
    explicit ptr(T* const raw_pointer) {
        if(raw_pointer) {
            pw_base = new pointer_wrapper<T>(raw_pointer);
            cached_raw = raw_pointer;
        }
    }

    ptr(const ptr<T>& other) : pw_base(other.pw_base), cached_raw(other.cached_raw) {
        if(pw_base) pw_base->increase_reference_count();
    }

    ptr<T>& operator=(const ptr<T>& other) {
        if(this == &other) return *this; // 处理自赋值
        if(pw_base) pw_base->decrease_reference_count();
        pw_base = other.pw_base;
        cached_raw = other.cached_raw;
        if(pw_base) pw_base->increase_reference_count();
        return *this;
    }

    T* operator->() const {
        if(!is_valid()) throw std::runtime_error("accessing invalid pointer");
        return cached_raw;
    }

    bool is_valid() const {
        return pw_base != nullptr && pw_base->is_valid() && cached_raw != nullptr;
    }

    int32_t get_reference_count() const {
        return pw_base ? pw_base->get_reference_count() : 0;
    }

    void destroy() {
        if(pw_base) pw_base->destroy();
    }

    // 向上转型(编译期安全)
    template<class X, std::enable_if_t<std::is_base_of_v<X, T>, int> = 0>
    ptr<X> convert() const {
        if(!is_valid()) return ptr<X>(nullptr, nullptr);
        return ptr<X>(pw_base, static_cast<X*>(cached_raw));
    }

    // 向下转型(运行期检查,转型失败返回空指针)
    template<class X, std::enable_if_t<std::is_base_of_v<T, X>, int> = 0>
    ptr<X> convert() const {
        if(!is_valid()) return ptr<X>(nullptr, nullptr);
        X* casted = dynamic_cast<X*>(cached_raw);
        return ptr<X>(pw_base, casted);
    }

    ~ptr() {
        if(pw_base) pw_base->decrease_reference_count();
    }
};

改造后所有同对象的不同类型ptr实例共享同一个引用计数和销毁标记,调用任意一个实例的destroy方法都会让所有实例的is_valid返回false,同时转型逻辑符合C++标准要求,不会出现未定义行为。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 12:48:05