Q.既然序列化函数不是虚函数,我想知道为什么CC类中的序列化函数会先执行,然后BB,AA的序列化函数。
原因是在通过多态引用/指针进行序列化时¹存档将包含序列化类型的类型 ID,因此库知道要反序列化的类型。
您实际上可以在存档中看到这个:
22 序列化::archive 18 0 0 1 7 派生 1 0 0 0 0 0 0 8 class_aa
8 class_bb 8 class_cc
您可以使用alternative export macros 来控制如何导出类型。
请注意,在上面的示例中,层次结构缺少虚拟析构函数并且存在内存泄漏。后者可能很明显,但前者可能不明显²。
这里是
Live On Coliru
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/serialization.hpp>
#include <fstream>
#include <iostream>
#include <string>
#include <utility>
class Base {
public:
Base(std::string aa) : aa_name(std::move(aa)) {}
Base() = default;
virtual void foo() const = 0;
virtual ~Base() = default;
private:
friend class boost::serialization::access;
template <class Archive> void serialize(Archive& ar, unsigned int) {
std::cout << "Base serialize start" << std::endl;
ar& aa_name;
std::cout << "Base serialize end" << std::endl;
}
std::string aa_name;
protected:
void qux() const { std::cout << aa_name << std::endl; }
};
class Middle : public Base {
public:
Middle(std::string aa, std::string Middle) : Base(aa), bb_name(std::move(Middle)) {}
Middle() = default;
void foo() const override { qux(); std::cout << bb_name << std::endl; }
virtual void bar() const = 0;
private:
friend class boost::serialization::access;
template <class Archive> void serialize(Archive& ar, unsigned int) {
std::cout << "Middle serialize start" << std::endl;
ar& boost::serialization::base_object<Base>(*this);
ar& bb_name;
std::cout << "Middle serialize end" << std::endl;
}
std::string bb_name;
};
class Derived : public Middle {
public:
Derived(std::string aa, std::string bb, std::string cc) : Middle(aa, bb), cc_name(std::move(cc)) {}
Derived() = default;
void bar() const override { foo(); std::cout << cc_name << std::endl; }
private:
friend class boost::serialization::access;
template <class Archive> void serialize(Archive& ar, unsigned int) {
std::cout << "Derived serialize start" << std::endl;
ar& boost::serialization::base_object<Middle>(*this);
ar& cc_name;
std::cout << "Derived serialize end" << std::endl;
}
std::string cc_name;
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(Base)
BOOST_SERIALIZATION_ASSUME_ABSTRACT(Middle)
BOOST_CLASS_EXPORT(Derived)
int main() {
using Ptr = std::unique_ptr<Base>;
{
std::ofstream outfile("archive_test.txt");
boost::archive::text_oarchive out_archive(outfile);
Ptr obj = std::make_unique<Derived>("class_aa", "class_bb", "class_cc");
out_archive << obj;
}
{
std::ifstream infile("archive_test.txt");
boost::archive::text_iarchive ia(infile);
Ptr obj;
ia >> obj;
if (auto cp = dynamic_cast<Derived*>(obj.get()))
cp->bar();
}
}
打印
Derived serialize start
Middle serialize start
Base serialize start
Base serialize end
Middle serialize end
Derived serialize end
Derived serialize start
Middle serialize start
Base serialize start
Base serialize end
Middle serialize end
Derived serialize end
class_aa
class_bb
class_cc
¹ 引用引出了一个完全不同的主题:object tracking
²When to use virtual destructors?
奖金
给cmets,一些技术分析:
宏 BOOST_CLASS_EXPORT(Derived) 扩展为
namespace boost { namespace serialization {
template <> struct guid_defined<Derived> : boost::mpl::true_ {};
template <> inline const char* guid<Derived>() { return "Derived"; }
} }
namespace boost { namespace archive { namespace detail { namespace extra_detail {
template <> struct init_guid<Derived> {
static guid_initializer<Derived> const& g;
};
guid_initializer<Derived> const& init_guid<Derived>::g =
::boost::serialization::singleton<
guid_initializer<Derived>>::get_mutable_instance()
.export_guid();
} } } }
export_guid() 读作:
guid_initializer const & export_guid() const {
BOOST_STATIC_WARNING(boost::is_polymorphic< T >::value);
// note: exporting an abstract base class will have no effect
// and cannot be used to instantitiate serialization code
// (one might be using this in a DLL to instantiate code)
//BOOST_STATIC_WARNING(! boost::serialization::is_abstract< T >::value);
export_guid(boost::serialization::is_abstract< T >());
return *this;
}
void export_guid(mpl::false_) const {
// generates the statically-initialized objects whose constructors
// register the information allowing serialization of T objects
// through pointers to their base classes.
instantiate_ptr_serialization((T*)0, 0, adl_tag());
}
注意它如何清楚地证明它只对多态类有意义。 instantiate_ptr_serialization实际上注册了with all known archives的类型。
无论您的代码组织如何(动态/共享链接、单独的翻译单元与否),都有大量的模板机制可以让所有内容都实例化(并且只实例化一次)。但最后还是去了register_type:
template<class T>
const basic_pointer_iserializer *
register_type(T * = NULL){
const basic_pointer_iserializer & bpis =
boost::serialization::singleton<
pointer_iserializer<Archive, T>
>::get_const_instance();
this->This()->register_basic_serializer(bpis.get_basic_serializer());
return & bpis;
}
它在哪里注册pointer_iserializer:
template<class Archive, class T>
class pointer_iserializer :
public basic_pointer_iserializer
{
private:
virtual void * heap_allocation() const {
detail::heap_allocation<T> h;
T * t = h.get();
h.release();
return t;
}
virtual const basic_iserializer & get_basic_serializer() const {
return boost::serialization::singleton<
iserializer<Archive, T>
>::get_const_instance();
}
BOOST_DLLEXPORT virtual void load_object_ptr(
basic_iarchive & ar,
void * x,
const unsigned int file_version
) const BOOST_USED;
public:
// this should alway be a singleton so make the constructor protected
pointer_iserializer();
~pointer_iserializer();
};
这会将实际的新建/删除包含在 heap_allocation 中。为简洁起见,我在这里不包括在内,因为我已经在这里进行了更详细的分析:How does boost::serialization allocate memory when deserializing through a pointer?