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

使用Boost.Serialization反序列化含虚基类共享指针对象遇无效弱指针问题

Boost.Serialization反序列化后调用shared_from_this触发bad weak pointer错误

问题背景

使用Boost.Serialization序列化并反序列化包含指向虚基类的shared_ptr的对象时,遇到bad weak pointer异常。具体场景:

  • 抽象基类Base,内部持有默认值为nullptr的shared_ptr<Base>;
  • 具体派生类SharedPtrHolder,同时继承虚基类Base和std::enable_shared_from_this<SharedPtrHolder>,其重写的虚方法依赖shared_from_this;
  • 类HasASharedPtrHolder持有指向Base的shared_ptr。

反序列化后,HasASharedPtrHolder的shared_ptr有效、虚函数调用正常,但调用shared_from_this时抛出bad weak pointer异常。序列化前一切正常,实际场景中该问题出现在访问者模式的Visit调用中,以下是最小复现示例:

#include <memory>

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/nvp.hpp>

//this #define MUST appear before #include <boost/test/unit_test.hpp>
#define BOOST_TEST_MODULE "MWE"
#define BOOST_TEST_DYN_LINK

#include <boost/test/unit_test.hpp>
#include <fstream>

// abstract, and owns a pointer to Base.
class Base{
public:
    Base() = default;
    virtual ~Base() = default;


    void set_ptr(std::shared_ptr<Base> const& n){
        this->_next = n;
    }

    const std::shared_ptr<Base> get_ptr() const{
        return this->_next;
    }

    virtual void do_thing_using_shared_from_this(){
        throw std::runtime_error("failed to override, this is the base");
    }

    virtual void print(){
        std::cout << "this is base, this should have been overridden" << std::endl;
    }

private:

    std::shared_ptr<Base> _next = nullptr;


    friend class boost::serialization::access;

    template <typename Archive>
    void serialize(Archive& ar, const unsigned version) {
        ar & _next;
    }



};

// concrete, with a method using `shared_from_this`
// this is a proxy for a visitable type.
class SharedPtrHolder: public virtual Base, public std::enable_shared_from_this<SharedPtrHolder>
{
public:
    SharedPtrHolder() = default;
    virtual ~SharedPtrHolder() = default;
        
    void check_shared_from_this() const{
        std::shared_ptr<const SharedPtrHolder> another_shared_ptr = this->shared_from_this();
    }

    virtual void do_thing_using_shared_from_this() override
    {
        auto as_shared = this->shared_from_this();
    }

    virtual void print() override{
        std::cout << "this is derived" << std::endl;
    }
private:




    friend class boost::serialization::access;

    template <typename Archive>
    void serialize(Archive& ar, const unsigned version) {
        ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Base);
    }

};



// this is a proxy for a type that owns visitable pointers.
class HasASharedPtrHolder{

public:
    HasASharedPtrHolder(std::shared_ptr<Base> const& p): _ptr(p) 
    {}

    HasASharedPtrHolder() = default;

    ~HasASharedPtrHolder() = default;

    void set_ptr(std::shared_ptr<Base> const& n){
        this->_ptr = n;
    }

    void check_shared_from_this() const{
        _ptr->do_thing_using_shared_from_this(); // this call fails after de-serialization, due to bad weak pointers
    }

    void call_a_virtual_function(){
        _ptr->print();
    }
private:
    std::shared_ptr<Base> _ptr = nullptr;


    friend class boost::serialization::access;

    template <typename Archive>
    void serialize(Archive& ar, const unsigned version) {
        ar.template register_type<SharedPtrHolder>(); // have to register, because have pointer to base
        ar & _ptr; // serialize the data this type owns
    }


};


BOOST_AUTO_TEST_SUITE(boost_serialization)



BOOST_AUTO_TEST_CASE(serialize_deserialize_has_a)
{
    { // to create a scope


        auto a_ptr = std::make_shared<SharedPtrHolder>();
        auto b_ptr = std::make_shared<SharedPtrHolder>();

        b_ptr->set_ptr(a_ptr);

        HasASharedPtrHolder sys(b_ptr);

        std::ofstream fout("serialization_basic");
        
        boost::archive::text_oarchive oa(fout);
        
        sys.check_shared_from_this();
        // write class instance to archive
        oa << sys;
    }
    
    
    {
        std::ifstream fin("serialization_basic");
        
        boost::archive::text_iarchive ia(fin);
        // read class state from archive

        HasASharedPtrHolder sys;
 
        ia >> sys;

        sys.call_a_virtual_function(); // this call is fine
        sys.check_shared_from_this(); // bad weak pointer, due to shared_from_this
    }

}

BOOST_AUTO_TEST_SUITE_END()

问题原因

std::enable_shared_from_this的工作依赖于对象被shared_ptr接管时初始化内部弱指针。但Boost.Serialization反序列化时,先通过默认构造函数创建对象,再将其关联到shared_ptr——这个过程不会触发enable_shared_from_this的弱指针初始化(弱指针仅在对象首次被shared_ptr管理时设置)。加上SharedPtrHolder继承自虚基类,Boost.Serialization的对象构造逻辑更复杂,进一步导致enable_shared_from_this的状态无法与反序列化后的shared_ptr正确关联。

解决方案

方法1:使用boost::serialization::shared_ptr_helper(推荐)

Boost.Serialization提供了专门处理enable_shared_from_this的工具,让SharedPtrHolder继承boost::serialization::shared_ptr_helper即可自动处理反序列化时的弱指针初始化:

class SharedPtrHolder: public virtual Base, 
                       public std::enable_shared_from_this<SharedPtrHolder>,
                       public boost::serialization::shared_ptr_helper
{
    // 原有代码保持不变
};

方法2:手动重置shared_ptr触发初始化

若不想引入额外基类,可在反序列化完成后,通过重新绑定shared_ptr触发enable_shared_from_this的弱指针初始化:

// 反序列化完成后添加
auto temp = std::static_pointer_cast<SharedPtrHolder>(sys._ptr);
sys._ptr.reset();
sys._ptr = temp;

注意:需确保_ptr可访问(或提供getter方法),此方式需适配所有派生类,通用性不如方法1。

额外注意事项

  1. 确保所有派生类已正确注册到序列化系统(你的代码中已在HasASharedPtrHolder的serialize函数中完成SharedPtrHolder的注册,此部分正确);
  2. 虚基类的序列化Boost.Serialization会自动处理,但结合enable_shared_from_this时必须保证弱指针初始化时机正确;
  3. 禁止在对象构造过程中调用shared_from_this,反序列化时对象构造完成后才会被shared_ptr接管,构造阶段调用必然失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 03:35:55