C++ boost序列化多态问题

标签 c++ serialization boost polymorphism

我使用基类的指针来序列化派生类的对象。下面的代码似乎有效。但我对执行的命令很模糊。输出信息为:
CC连载开始
BB连载开始
AA序列化开始
AA连载结束
BB连载结束
CC连载结束
CC连载开始
BB连载开始
AA序列化开始
AA连载结束
BB连载结束
CC连载结束
class_aa
class_bb
class_cc
由于serialize函数不是virtual的,我想知道为什么CC类的serialize函数会先执行,然后BB,AA的serialize函数。

谢谢

#include <boost/serialization/serialization.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/archive/text_oarchive.hpp> 
#include <boost/archive/text_iarchive.hpp>
#include <iostream>
#include <string>
#include <boost/serialization/export.hpp>
#include <fstream>
using namespace std;

class AA
{
public:
    virtual void foo() = 0;
    AA(string aa) :aa_name(aa) { }
    AA() {}

    template<class Archive>
    void serialize(Archive& ar, unsigned int)
    {
        cout << "AA serialize start" << endl;
        ar& aa_name;
        cout << "AA serialize end" << endl;
    }

    string aa_name;
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(AA);

class BB : public AA
{
public:
    void foo() {}
    virtual void bar() = 0;
    BB(string aa, string bb) : AA(aa), bb_name(bb) {}
    BB() {}

    template<class Archive>
    void serialize(Archive& ar, unsigned int)
    {
        cout << "BB serialize start" << endl;
        ar& boost::serialization::base_object<AA>(*this);
        ar& bb_name;
        cout << "BB serialize end" << endl;
    }
    string bb_name;
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(BB);

class CC : public BB
{
public:
    CC(string aa, string bb, string cc) : BB(aa, bb), cc_name(cc) {}
    CC() {}
    void bar() {} 

    template<class Archive>
    void serialize(Archive& ar, unsigned int)
    {
        cout << "CC serialize start" << endl;
        ar& boost::serialization::base_object<BB>(*this);
        ar& cc_name;
        cout << "CC serialize end" << endl;
    }

    string cc_name;
};
BOOST_CLASS_EXPORT(CC)


int main(int, char const**)
{
    AA* obj = new CC("class_aa", "class_bb", "class_cc");
    ofstream outfile("archive_test.txt");
    boost::archive::text_oarchive out_archive(outfile);
    out_archive << obj;
    outfile.close();

    ifstream infile("archive_test.txt");
    boost::archive::text_iarchive ia(infile);
    AA* c;
    ia >> c;
    cout << c->aa_name << endl << dynamic_cast<CC*>(c)->bb_name << endl << dynamic_cast<CC*>(c)->cc_name << endl;
    infile.close();
}

最佳答案

Q. since the serialize function is not virtual, I am wondering why the serialize fuction in class CC will execute at first, and then BB, AA's serialize function.

原因是在通过多态引用/指针进行序列化时¹,存档将包含序列化类型的类型 ID,因此库知道要反序列化的类型。

您实际上可以在存档中看到这个:

22 serialization::archive 18 0 0 1 7 Derived 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?

奖金

对于评论,一些技术分析:

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?

关于C++ boost序列化多态问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62771254/

相关文章:

c++ - 如何通过 SFINAE 测试类中是否存在内部类?

c# - 如何序列化 List<T>?

Java:序列化/反序列化到/从 XML 而不是二进制

c++ - 事件与 boost::function

c++ - 变体与继承

c++ - llvm 以 char * 作为参数调用外部函数

c++ - 何时使用 sqlite3_blob_write/read?

c# - 我将默认 JsonSerializer 设置为 Utf8Json

c++ - boost 文件系统 3 路径包含检查

c++ - 如何创建对模板类执行操作的静态模板成员函数?