使用Boost.Serialization反序列化含虚基类共享指针对象遇无效弱指针问题
问题背景
使用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。
额外注意事项
- 确保所有派生类已正确注册到序列化系统(你的代码中已在
HasASharedPtrHolder的serialize函数中完成SharedPtrHolder的注册,此部分正确); - 虚基类的序列化Boost.Serialization会自动处理,但结合
enable_shared_from_this时必须保证弱指针初始化时机正确; - 禁止在对象构造过程中调用
shared_from_this,反序列化时对象构造完成后才会被shared_ptr接管,构造阶段调用必然失败。
内容的提问来源于stack exchange,提问作者ofloveandhate

