序列化多态类型的常见混淆
Common confusions with serializing polymorphic types
我看过许多涉及序列化派生类的问题、教程和文档,但我无法就几个问题达成共识,包括(并在以下代码中说明):
-
boost::serialization::base_object
与BOOST_SERIALIZATION_BASE_OBJECT_NVP
-
archive & mData;
与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:)
-
boost::serialization::base_object
与BOOST_SERIALIZATION_BASE_OBJECT_NVP
NVP 包装器仅适用于具有元素命名的存档,例如 XML。
除非你使用它,否则base_object<>
更干净、更简单。
-
archive & mData;
与archive & BOOST_SERIALIZATION_NVP(mData);
同上
BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbstractPoint);
的用处
我认为这只是一种优化 - 抑制每个存档类型的注册类型信息,因为您告诉框架它永远不会反序列化该类型的实例
- 要求对层次结构中不需要序列化任何内容的类进行
serialize()
。
你不需要它,除非你需要关于那里的多态碱基的类型信息。你什么时候需要它?当您需要反序列化基类型的指针时。
因此,如果您有
struct A{ virtual ~A(); };
struct B:A{};
struct C:B{};
struct D:B{};`
如果您(反)序列化A*
,您将需要序列化A
(但不是B
)。如果您(反)序列化B*
,您将需要序列化B
。
的类型不是多态的(虚拟的),或者你不使用它,你不需要任何基本序列化(例如,如果你(反)序列化C
或直接D
)。
最后,如果您有struct A{}; struct B:A{};
则根本不需要告诉 Boost 序列化有关基本类型的信息(您可以从 B
中执行序列化)。
更新以响应您的示例:
- 案例1.cpp看起来不错
当然.cpp case2 需要调用基序列化;不一定使用 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; }
案例3.cpp:确实,它与案例1完全相同,但具有动态分配和对象跟踪功能
case4.cpp:与 case1 完全相同,但具有动态分配和对象跟踪功能;铌!!它需要显式序列化基础!
template<class TArchive> void serialize(TArchive& archive, unsigned) { archive & boost::serialization::base_object<AbstractPoint>(*this) & mData; }
案例 5.cpp:是的,但更典型的做法是使用
boost/serialization/export.hpp
中的CLASS_EXPORT*
宏
比特罗特保险:
- 案例1.cpp
- 案例2.cpp
- 案例3.cpp
- 案例4.cpp
- 案例5.cpp
根据@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;
}
- ArduinoJson 6.15.2:JsonObject没有命名类型
- 防止主数据类型C++的隐式转换
- 大量序列中核苷酸类型的快速计数
- 如何从C++中的依赖类型中获得它所依赖的类型
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 缓冲区填充了不同类型的数据和严格的混叠
- 模板对象的C 数组:施放到常见的非类型模板参数是UB吗?
- 类型的混叠和自我引用
- 预安装库中的常见数据类型(Int、Char 等)大写
- 两个排版/元组中的常见类型
- 在多个DLL中注册常见的Qt元类型
- 常见的父类型 ifstream 和 ofstream
- 具有相同签名和捕获的常见 lambda 类型
- 序列化多态类型的常见混淆
- 确定模板参数包中"optimal"常见数值类型
- 取消引用类型punned指针将打破严格的混叠规则:将字节数组转换为数字
- NEON矢量数据类型的混叠
- 为什么常见的布尔和int8_t类型是C++中的int32_t
- 用于 lambda 和函数的常见类型转换,稍后要引用
- 常见数据类型的长度是多少