我尝试为两个类
Geometry
和 Dimension
实现一个序列化器,它们之间具有循环依赖关系。这意味着 Geometry
可以有一个 Dimension
并且 Dimension
知道它的 Geometry
。另外,我有 DataModel
,其中包含几何形状和尺寸的向量。我的课程是这样的:
class IGeometry
{
public:
virtual ~IGeometry() = default;
};
class IDimension
{
public:
virtual ~IDimension() = default;
virtual const std::vector<IGeometry*>& GetGeometries() const = 0;
};
class Dimension : public virtual IDimension
{
public:
Dimension(std::vector<IGeometry*> f) : geometries{ std::move(f) } {}
~Dimension() override = default;
const std::vector<IGeometry*>& GetGeometries() const override
{
return geometries;
}
private:
std::vector<IGeometry*> geometries;
};
class Geometry : public virtual IGeometry
{
public:
~Geometry() override {}
void AddDimension(IDimension* dimension)
{
dimensions.emplace_back(dimension);
}
const std::vector<IDimension*>& GetDimensions() const { return dimensions; }
private:
std::vector<IDimension*> dimensions{};
};
struct DataModel
{
std::vector<IGeometry*> geometries;
std::vector<IDimension*> dimensions;
};
我正在使用来自 boost 的非侵入式序列化,如下所示:
BOOST_SERIALIZATION_SPLIT_FREE(Geometry)
BOOST_CLASS_EXPORT(Dimension)
BOOST_CLASS_EXPORT(Geometry)
namespace boost
{
namespace serialization
{
template<class Archive>
void serialize(Archive& ar, IGeometry& g, const unsigned int version){ }
template<class Archive>
void serialize(Archive& ar, IDimension& d, const unsigned int version){ }
template<class Archive>
void save(Archive& ar, const Geometry& g, const unsigned int version)
{
ar& boost::serialization::base_object<IGeometry>(g);
ar& g.GetDimensions();
}
template<class Archive>
void load(Archive& ar, Geometry& g, const unsigned int version)
{
ar& boost::serialization::base_object<IGeometry>(g);
std::vector<IDimension*> dimensions;
ar& dimensions;
for(auto* dimension : dimensions)
{
g.AddDimension(dimension);
}
}
template<class Archive>
void serialize(Archive& ar, Dimension& d, unsigned int version)
{
ar& boost::serialization::base_object<IDimension>(d);
}
template<class Archive>
void save_construct_data(Archive& ar, const Dimension* t, const unsigned int)
{
ar& t->GetGeometries();
}
template<class Archive>
void load_construct_data(Archive& ar, Dimension* t, const unsigned int file_version)
{
std::vector<IGeometry*> foos;
ar& foos;
::new(t)Dimension(foos);
}
template<class Archive>
void serialize(Archive& ar, DataModel& model, const unsigned int version)
{
ar& model.dimensions & model.geometries ;
}
}
}
void SaveModel(const DataModel& model)
{
std::ofstream ofs("filename");
boost::archive::text_oarchive oa(ofs);
oa << model;
}
void RestoreModel(DataModel& model)
{
std::ifstream ofs("filename");
boost::archive::text_iarchive ia(ofs);
ia >> model;
}
int main()
{
{
auto* p0 = new Geometry();
auto* p1 = new Geometry();
auto* d2 = new Dimension({ p0, p1 });
p0->AddDimension(d2);
p1->AddDimension(d2);
DataModel model;
model.geometries.emplace_back(p0);
model.geometries.emplace_back(p1);
model.dimensions.emplace_back(d2);
SaveModel(model);
}
DataModel model2;
RestoreModel(model2);
return 0;
}
main() 中的示例在尝试恢复模型时失败,出现以下异常:
Exception thrown at 0x00007FF7F62BA385 in boost.exe: 0xC0000005: Access violation reading location 0xFFFFFFFFFFFFFFFF.
我的猜测,在恢复时,Boost 会尝试用
Dimension
中的 2 个几何图形对 load_construct_data
进行反序列化。然后它尝试反序列化其中一个几何图形,该几何图形再次以 Dimension
作为参考,当然当前尚未构建。
我是新来的
boost::serialization
。
我的问题是:
没有抽象类它工作得很好!
循环引用很好。您的问题似乎来自于虚拟基类。
特别是在这里评论
virtual
关键字:
class Dimension : public /*virtual*/ IDimension {
让问题消失。我唯一的预感是,在加载构造数据发生之前,需要以某种方式构造基础对象。
事实上,即使使用虚拟基础,消除加载/保存构造数据的需要也确实有效:
仔细想想,也有道理:
两种解决方案:
virtual
基类修饰符