C++ 序列化:如何避免指针的双重保存?(并获得一个免费的.c错误)
我目前有一个stl:list,其中包含一些基本对象和一些派生类 我可以毫无问题地加载和保存此列表。使用BOOST_CLASS_EXPORT(…)宏,一切都正常工作,直到我添加以下内容:C++ 序列化:如何避免指针的双重保存?(并获得一个免费的.c错误),c++,boost-serialization,C++,Boost Serialization,我目前有一个stl:list,其中包含一些基本对象和一些派生类 我可以毫无问题地加载和保存此列表。使用BOOST_CLASS_EXPORT(…)宏,一切都正常工作,直到我添加以下内容: -------------------------------------------------------------------------------------- 我需要一些包含列表中其他对象指针的对象 (为了避免过于抽象:这些是一些“游戏对象”,它们对称为“区域”的对象有一些引用,它们都是从基本游戏类
-------------------------------------------------------------------------------------- 我需要一些包含列表中其他对象指针的对象 (为了避免过于抽象:这些是一些“游戏对象”,它们对称为“区域”的对象有一些引用,它们都是从基本游戏类派生的。)
--------------------------------------------------------------------------------------
现在我正在序列化列表,每个对象都会被单独序列化
ar & characterList;
游戏对象包含以下代码:
template<class Archive>
void save(Archive & ar, const unsigned int version) const {
ar & boost::serialization::base_object<M_Character>(*this);
ar & area; // If I add this line, it will crash
}
template<class Archive>
void load(Archive & ar, const unsigned int version) const {
ar & boost::serialization::base_object<M_Character>(*this);
ar & area; // This one as well
}
)
因此该区域将被序列化两次,因为首先该区域将从列表中保存,然后从对象中保存
我怎样才能避免这种情况?是否可以第一次保存整个对象,第二次只保存指针
或者我应该尝试完全不同的方法(从带有ID的列表中获取指针)好的,我已经为您创建了一个演示:
struct Area
{
Area(int i):id(i) {}
int id;
};
struct List : boost::noncopyable
{
std::vector<Area*> areas;
~List() {
std::for_each(areas.begin(), areas.end(), std::default_delete<Area>());
}
};
struct M_Character {
virtual ~M_Character() {}
};
struct GameObject : M_Character, boost::noncopyable
{
Area* area;
GameObject(Area* area = nullptr) : area(area) {}
};
BOOST_CLASS_EXPORT_GUID(GameObject, "GameObject")
您将注意到它如何运行良好():
不过,你在Coliru上看不到的是,这会泄露内存。Valgrind告诉您在反序列化过程中分配的4个字节丢失-显然这是游戏对象的区域
但是嘿!在序列化指针时,Boost序列化不是要执行对象跟踪吗
好问题。是的但是(这很容易忽略),它将只对同一对象图中的指针执行。因此,您可以通过
用游戏对象序列化列表(破坏封装,容易出错)
制作一个用作“容器”的超级对象(或者从技术上说:对象图的根)
没有更多的内存泄漏
完整代码清单
#包括
#包括
#包括
#包括
#包括
结构区
{
区域(inti):id(i){}
int-id;
私人:
Area(){}//仅用于反序列化
friend boost::serialization::access;
模板
无效序列化(存档&ar,未签名){ar&id;}
};
结构列表:boost::不可复制
{
病媒区;
~List(){
std::for_each(areas.begin()、areas.end()、std::default_delete());
}
私人:
friend boost::serialization::access;
模板
无效序列化(存档&ar,未签名){ar&AREA;}
};
结构M_字符{
虚拟~M_字符(){}
私人:
friend boost::serialization::access;
模板
无效序列化(存档&/*ar*/,未签名){}
};
结构游戏对象:M_字符,boost::不可复制
{
面积*面积;
游戏对象(Area*Area=nullptr):区域(Area){}
私人:
friend boost::serialization::access;
模板
无效序列化(存档(&R),未签名){
ar&boost::serialization::base_对象(*this);
ar&area;
}
};
BOOST_CLASS_EXPORT_GUID(游戏对象,“游戏对象”)
#包括
结构世界:boost::不可复制
{
名单;
GameObject*_对象;
World():_对象(nullptr){
~World(){删除_对象;}
私人:
friend boost::serialization::access;
模板
无效序列化(存档(&R),未签名){
应收账款清单;
ar&u对象;
}
};
字符串序列化(世界常量(&w)
{
std::stringstream-ss;
boost::archive::text\u oarchive oa(ss);
oa>w;
}
int main()
{
世界;
对于(int i=0;i<10;++i)
世界。列表。区域。推回(新区域(i));
//构建原始obj
world.the_object=新游戏对象(world.list.areas[3]);//共享列表中的区域指针
std::string const serialized=serialize(世界);
std::难道信息太少了吗。你能把它做成SSCCE吗?嗯,这可能需要一些时间,因为我自己的库很大。无论如何,我会试试。-一个带有.lib和头文件的小演示程序足够了吗?嗯。这不是独立的,但你可以试试我。我通常不会花太多时间模拟/存根不可用的部分。我e为您创建了一个SSCCE,简化为一个区域列表和一个游戏对象。如果它还没有回答,那么至少它将帮助您获得一个SSCCE:)哇!非常感谢!我有一个包含stl::list的世界级(与本示例中的stl\uu向量结构不同)但这不应该是问题所在-似乎我错过了boost::noncopyable在我的世界级中,我会尝试一下!太棒了!!!谢谢!!似乎现在它开始工作了…我从来没有想过向世界添加基类…:)@MrMonday从逻辑上讲,仅仅添加基类不应该改变行为(尽管它可以防止违反“三人法则”,而在《准则》的其他部分,这可能是导致您之前看到的问题的原因)
struct Area
{
Area(int i):id(i) {}
int id;
};
struct List : boost::noncopyable
{
std::vector<Area*> areas;
~List() {
std::for_each(areas.begin(), areas.end(), std::default_delete<Area>());
}
};
struct M_Character {
virtual ~M_Character() {}
};
struct GameObject : M_Character, boost::noncopyable
{
Area* area;
GameObject(Area* area = nullptr) : area(area) {}
};
BOOST_CLASS_EXPORT_GUID(GameObject, "GameObject")
int main()
{
List l;
for (int i = 0; i < 10; ++i)
l.areas.push_back(new Area(i));
std::unique_ptr<M_Character> obj, roundtrip;
// build original obj
obj.reset(new GameObject(l.areas[3])); // sharing the area pointer from the list
std::string const serialized = serialize(obj.get());
std::cout << serialized << '\n';
std::cout << "-------------------------------------------------\n";
// create roundtrip
roundtrip.reset(deserialize(serialized));
std::cout << "EQUAL? " << std::boolalpha << (serialized == serialize(roundtrip.get())) << "\n";
}
clang++ -std=c++11 -Os -Wall -pedantic main.cpp -lboost_system -lboost_serialization && ./a.out
22 serialization::archive 10 1 10 GameObject 1 0
0 1 0
1 2 1 0
2 3
-------------------------------------------------
EQUAL? true
struct World : boost::noncopyable
{
List list;
GameObject* the_object;
World() : the_object(nullptr) {}
~World() { delete the_object; }
private:
friend boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, unsigned) {
ar & list;
ar & the_object;
}
};
clang++ -std=c++11 -Os -Wall -pedantic main.cpp -lboost_system -lboost_serialization && ./a.out
22 serialization::archive 10 0 0 0 0 0 0 10 0 3 1 0
0 0 3
1 1 3
2 2 3
3 3 3
4 4 3
5 5 3
6 6 3
7 7 3
8 8 3
9 9 4 1 0
10 0 0 3 3
-------------------------------------------------
EQUAL? true
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/vector.hpp>
struct Area
{
Area(int i):id(i) {}
int id;
private:
Area() { } // used only in deserialization
friend boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, unsigned) { ar & id; }
};
struct List : boost::noncopyable
{
std::vector<Area*> areas;
~List() {
std::for_each(areas.begin(), areas.end(), std::default_delete<Area>());
}
private:
friend boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, unsigned) { ar & areas; }
};
struct M_Character {
virtual ~M_Character() {}
private:
friend boost::serialization::access;
template<class Archive>
void serialize(Archive & /*ar*/, unsigned) { }
};
struct GameObject : M_Character, boost::noncopyable
{
Area* area;
GameObject(Area* area = nullptr) : area(area) {}
private:
friend boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, unsigned) {
ar & boost::serialization::base_object<M_Character>(*this);
ar & area;
}
};
BOOST_CLASS_EXPORT_GUID(GameObject, "GameObject")
#include <sstream>
struct World : boost::noncopyable
{
List list;
GameObject* the_object;
World() : the_object(nullptr) {}
~World() { delete the_object; }
private:
friend boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, unsigned) {
ar & list;
ar & the_object;
}
};
std::string serialize(World const& w)
{
std::stringstream ss;
boost::archive::text_oarchive oa(ss);
oa << w;
return ss.str();
}
void deserialize(std::string const& input, World& w)
{
std::stringstream ss(input);
boost::archive::text_iarchive ia(ss);
ia >> w;
}
int main()
{
World world;
for (int i = 0; i < 10; ++i)
world.list.areas.push_back(new Area(i));
// build original obj
world.the_object = new GameObject(world.list.areas[3]); // sharing the area pointer from the list
std::string const serialized = serialize(world);
std::cout << serialized << '\n';
std::cout << "-------------------------------------------------\n";
// create roundtrip
World roundtrip;
deserialize(serialized, roundtrip);
std::cout << "EQUAL? " << std::boolalpha << (serialized == serialize(roundtrip)) << "\n";
}