如何将C++派生类对象序列化到文件并正确反序列化加载?
带虚函数的派生类对象序列化与反序列化问题修复
你的推测完全正确:直接二进制读写带虚函数的类对象时,写入的虚指针(vptr)指向的是当前进程中虚表的内存地址,读取后在新进程里这个地址无效甚至非法,调用虚函数时就会触发崩溃。另外你的读取代码还存在一个低级错误——把f.read写成了f.write,这会导致非法的流操作,进一步加剧问题。
下面是具体的修正方案:
核心思路
放弃直接内存拷贝的方式,手动实现仅针对数据成员的序列化/反序列化逻辑。虚表由编译器在程序运行时自动维护,对象构造时会自动初始化虚指针指向当前进程的有效虚表,不需要我们手动写入或恢复。
1. 修改类定义,添加序列化/反序列化接口
// base.h #include <string> #include <fstream> class Base { public: virtual std::string show_string() = 0; // 序列化:将数据成员写入文件 virtual void serialize(std::ofstream& out) const = 0; // 反序列化:从文件读取数据成员 virtual void deserialize(std::ifstream& in) = 0; int b; }; class A : public Base { public: std::string show_string() override { return "this is A"; } void serialize(std::ofstream& out) const override { // 按固定顺序写入数据成员 out.write(reinterpret_cast<const char*>(&b), sizeof(b)); out.write(reinterpret_cast<const char*>(&a), sizeof(a)); } void deserialize(std::ifstream& in) override { // 读取顺序必须和写入顺序完全一致 in.read(reinterpret_cast<char*>(&b), sizeof(b)); in.read(reinterpret_cast<char*>(&a), sizeof(a)); } int a; };
2. 修正后的写入代码
#include <fstream> #include "./base.h" int main() { // 用binary模式打开,避免文本模式自动转换换行符破坏二进制数据 std::ofstream f("a.bin", std::ios::binary); for (int i = 0; i < 10; ++i) { A a; a.a = i; a.b = i - 1; a.serialize(f); } f.close(); return 0; }
3. 修正后的读取代码
#include <iostream> #include <fstream> #include "./base.h" int main() { std::ifstream f("a.bin", std::ios::binary); for (int i = 0; i < 10; ++i) { A a; a.deserialize(f); std::cout << "a.a = " << a.a << ", a.b = " << a.b << ", " << a.show_string() << std::endl; } f.close(); return 0; }
扩展:支持多态场景(基类指针指向派生类)
如果需要处理基类指针操作不同派生类对象的场景,可以在序列化时加入类型标识,反序列化时根据标识创建对应对象:
// 在Base类中添加类型枚举和类型获取接口 class Base { public: enum class ObjectType { A }; virtual ObjectType get_type() const = 0; // ... 原有成员 }; // A类实现类型获取 class A : public Base { public: ObjectType get_type() const override { return ObjectType::A; } // ... 原有成员 }; // 修改Base的序列化逻辑,先写入类型标识 void Base::serialize(std::ofstream& out) const { ObjectType type = get_type(); out.write(reinterpret_cast<const char*>(&type), sizeof(type)); } // 通用反序列化函数,根据类型创建对应对象 std::unique_ptr<Base> deserialize_object(std::ifstream& in) { Base::ObjectType type; in.read(reinterpret_cast<char*>(&type), sizeof(type)); std::unique_ptr<Base> obj; if (type == Base::ObjectType::A) { obj = std::make_unique<A>(); } if (obj) { obj->deserialize(in); } return obj; }
内容的提问来源于stack exchange,提问作者nothingisme
相关产品推荐
相关产品推荐

