序列化多态类型的常见混淆

时间:2016-03-02 17:18:31

标签: c++ serialization boost boost-serialization

我看过许多涉及序列化派生类的问题,教程和文档,但我未能就几个问题达成共识,包括(并在下面的代码中说明):

  • boost::serialization::base_object vs BOOST_SERIALIZATION_BASE_OBJECT_NVP
  • archive & mData; vs archive & BOOST_SERIALIZATION_NVP(mData);
  • BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbstractPoint);
  • 的用处
  • 要求serialize()层次结构中不需要序列化任何内容的类。

代码:

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

#include <fstream>

class AbstractPoint
{
public:
    virtual ~AbstractPoint(){}
    virtual void DoSomething() = 0;

    // Even though the class is abstract, we still need this
    template<class TArchive>
    void serialize(TArchive& archive, const unsigned int version)
    {
        // do nothing
    }
};

// This doesn't seem to do anything
//BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbstractPoint);

class Point : public AbstractPoint
{
public:
    Point() = default;
    Point(const double data) : mData(data) {}

    void DoSomething(){}

    template<class TArchive>
    void serialize(TArchive& archive, const unsigned int version)
    {
        // These two seem equivalent. Without one of them, unregistered void cast
        archive & boost::serialization::base_object<AbstractPoint>(*this);
        //archive & BOOST_SERIALIZATION_BASE_OBJECT_NVP(AbstractPoint);

        // These two seem equivalent
        archive & mData;
        //archive & BOOST_SERIALIZATION_NVP(mData);
    }

    double mData;
};

int main()
{
    std::shared_ptr<AbstractPoint> point(new Point(7.4));

    std::ofstream outputStream("test.txt");
    boost::archive::text_oarchive outputArchive(outputStream);
    outputArchive.register_type<Point>();
    outputArchive << point;
    outputStream.close();

    std::shared_ptr<AbstractPoint> pointRead;
    std::ifstream inputStream("test.txt");
    boost::archive::text_iarchive inputArchive(inputStream);
    inputArchive.register_type<Point>();
    inputArchive >> pointRead;

    std::shared_ptr<Point> castedPoint = std::dynamic_pointer_cast<Point>(pointRead);
    std::cout << castedPoint->mData << std::endl;
    return 0;
}

另一个主要问题是在“真实”环境中注册课程(当有链接等时),但这似乎是一个单独的问题。

在文档中有这样的东西的“黄金标准”示例会很棒,但至少在StackOverflow上:)

2 个答案:

答案 0 :(得分:3)

  • boost::serialization::base_object vs BOOST_SERIALIZATION_BASE_OBJECT_NVP

只有具有元素命名的存档(如XML)才需要NVP包装器。

除非您使用它,否则base_object<>更清晰,更简单。

  • archive & mData; vs archive & BOOST_SERIALIZATION_NVP(mData);

同上

  • BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbstractPoint);
  • 的用处

我认为它只是一个优化 - 抑制每个存档类型的注册类型信息,因为你告诉框架它永远不会反序列化类型的实例

  • 要求serialize()层次结构中不需要序列化任何内容的类。

除非您需要有关多态基的类型信息,否则您不需要它。你什么时候需要那个?当你需要反序列化基类型的指针时。

因此,如果你有

struct A{ virtual ~A(); };
struct B:A{};

struct C:B{};
struct D:B{};` 
如果你(de)序列化A,那么 需要序列化B(但不是A*)。如果您(de)序列化B,则需要序列化B*

同样,如果您的类型不是多态的(虚拟的)或者您不是这样使用它,那么您不需要任何基本序列化(例如,如果您(de)序列化CD直接)。

最后,如果您有struct A{}; struct B:A{};,则根本不需要告诉Boost序列化基本类型(您可以在B内进行序列化。)

更新以响应您的样品:

  1. case1.cpp看起来不错
  2. 当然,
  3. case2.cpp 需要来调用基本序列化;不一定使用base_object因为你需要多态序列化:

    template<class TArchive> void serialize(TArchive& archive, unsigned) {
        archive & boost::serialization::base_object<AbstractPoint>(*this)
                & mData;
        // OR:
        archive & static_cast<AbstractPoint&>(*this) 
                & mData;
        // OR even just:
        archive & mParentData 
                & mData;
    }
    
  4. case3.cpp:确实,它与case1完全相同,但具有动态分配和对象跟踪

  5. case4.cpp:与case1完全相同,但具有动态分配和对象跟踪功能; NB !! 它需要显式序列化基础!

    template<class TArchive> void serialize(TArchive& archive, unsigned) {
        archive & boost::serialization::base_object<AbstractPoint>(*this)
                & mData;
    }
    
  6. case5.cpp:是的,但使用CLASS_EXPORT*

  7. 中的boost/serialization/export.hpp宏更为常见
      

    比特罗保险:

         

答案 1 :(得分:1)

遵循@ sehe的建议,以下是一些示例用法:

序列化派生类对象,而不是转发给父

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

#include <fstream>

class AbstractPoint
{
public:
    virtual ~AbstractPoint(){}
    virtual void DoSomething() = 0;
};

class Point : public AbstractPoint
{
public:
    Point() = default;
    Point(const double data) : mData(data) {}

    void DoSomething(){}

    template<class TArchive>
    void serialize(TArchive& archive, const unsigned int version)
    {
        archive & mData;
    }

    double mData;
};

int main()
{
    Point point(7.4);

    std::ofstream outputStream("test.txt");
    boost::archive::text_oarchive outputArchive(outputStream);
    outputArchive << point;
    outputStream.close();

    Point pointRead;
    std::ifstream inputStream("test.txt");
    boost::archive::text_iarchive inputArchive(inputStream);
    inputArchive >> pointRead;

    std::cout << pointRead.mData << std::endl;
    return 0;
}

序列化派生类对象,包括(自动)转发到父级:

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

#include <fstream>

class AbstractPoint
{
public:
    virtual ~AbstractPoint(){}
    virtual void DoSomething() = 0;

    double mParentData = 3.1;

    template<class TArchive>
    void serialize(TArchive& archive, const unsigned int version)
    {
        archive & mParentData;
    }
};

class Point : public AbstractPoint
{
public:
    Point() = default;
    Point(const double data) : mData(data) {}

    void DoSomething(){}

    template<class TArchive>
    void serialize(TArchive& archive, const unsigned int version)
    {
        // this is not required, the parent serialize() seems to be called automatically
        // archive & boost::serialization::base_object<AbstractPoint>(*this);

        archive & mData;
    }

    double mData;
};

int main()
{
    Point point(7.4);

    std::ofstream outputStream("test.txt");
    boost::archive::text_oarchive outputArchive(outputStream);
    outputArchive << point;
    outputStream.close();

    Point pointRead;
    std::ifstream inputStream("test.txt");
    boost::archive::text_iarchive inputArchive(inputStream);
    inputArchive >> pointRead;

    std::cout << pointRead.mParentData << std::endl;
    std::cout << pointRead.mData << std::endl;
    return 0;
}

序列化派生类指针,而不是转发给父 (注意对象没有任何变化)

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

#include <fstream>

class AbstractPoint
{
public:
    virtual ~AbstractPoint(){}
    virtual void DoSomething() = 0;
};

class Point : public AbstractPoint
{
public:
    Point() = default;
    Point(const double data) : mData(data) {}

    void DoSomething(){}

    template<class TArchive>
    void serialize(TArchive& archive, const unsigned int version)
    {
        archive & mData;
    }

    double mData;
};

int main()
{
    std::shared_ptr<Point> point(new Point(7.4));

    std::ofstream outputStream("test.txt");
    boost::archive::text_oarchive outputArchive(outputStream);
    outputArchive << point;
    outputStream.close();

    std::shared_ptr<Point> pointRead;
    std::ifstream inputStream("test.txt");
    boost::archive::text_iarchive inputArchive(inputStream);
    inputArchive >> pointRead;

    std::cout << pointRead->mData << std::endl;
    return 0;
}

序列化派生类指针,转发到父 (注意对象没有任何变化)

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

#include <fstream>

class AbstractPoint
{
public:
    virtual ~AbstractPoint(){}
    virtual void DoSomething() = 0;

    template<class TArchive>
    void serialize(TArchive& archive, const unsigned int version)
    {
        archive & mParentData;
    }

    double mParentData = 3.1;
};

class Point : public AbstractPoint
{
public:
    Point() = default;
    Point(const double data) : mData(data) {}

    void DoSomething(){}

    template<class TArchive>
    void serialize(TArchive& archive, const unsigned int version)
    {
        archive & mData;
    }

    double mData;
};

int main()
{
    std::shared_ptr<Point> point(new Point(7.4));

    std::ofstream outputStream("test.txt");
    boost::archive::text_oarchive outputArchive(outputStream);
    outputArchive << point;
    outputStream.close();

    std::shared_ptr<Point> pointRead;
    std::ifstream inputStream("test.txt");
    boost::archive::text_iarchive inputArchive(inputStream);
    inputArchive >> pointRead;

    std::cout << pointRead->mParentData << std::endl;
    std::cout << pointRead->mData << std::endl;
    return 0;
}

序列化基类指针 (我们现在必须使用档案注册派生类的类型,以及使用boost::serialization::base_object

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

#include <fstream>

class AbstractPoint
{
public:
    virtual ~AbstractPoint(){}
    virtual void DoSomething() = 0;

    // This is required if we want to serialize an AbstractPoint pointer
    template<class TArchive>
    void serialize(TArchive& archive, const unsigned int version)
    {
        // do nothing
    }
};

class Point : public AbstractPoint
{
public:
    Point() = default;
    Point(const double data) : mData(data) {}

    void DoSomething(){}

    template<class TArchive>
    void serialize(TArchive& archive, const unsigned int version)
    {
        // Without this, we get unregistered void cast
        archive & boost::serialization::base_object<AbstractPoint>(*this);

        archive & mData;
    }

    double mData;
};

int main()
{
    std::shared_ptr<AbstractPoint> point(new Point(7.4));

    std::ofstream outputStream("test.txt");
    boost::archive::text_oarchive outputArchive(outputStream);
    outputArchive.register_type<Point>();
    outputArchive << point;
    outputStream.close();

    std::shared_ptr<AbstractPoint> pointRead;
    std::ifstream inputStream("test.txt");
    boost::archive::text_iarchive inputArchive(inputStream);
    inputArchive.register_type<Point>();
    inputArchive >> pointRead;

    std::shared_ptr<Point> castedPoint = std::dynamic_pointer_cast<Point>(pointRead);
    std::cout << castedPoint->mData << std::endl;
    return 0;
}