无需Boost:如何序列化C++含虚函数的类并保障性能?
多态类的手动序列化/反序列化实现(高性能)
问题分析
直接通过write((char*)&a, sizeof(a))写入带虚函数的类对象会导致运行崩溃,核心原因是:
- 对象内存中的**虚函数表指针(vptr)**是进程运行时确定的地址,写入文件后,在加载进程中该地址要么无效,要么指向错误的虚表,调用虚函数时必然触发崩溃。
- 这种内存直接拷贝的方式还存在跨平台兼容性问题(不同编译器的内存布局、字节序差异),无法保证稳定性。
解决方案思路
手动序列化类的数据成员而非整个对象内存,同时为多态类添加类型标识,确保反序列化时能创建正确的子类实例,完全避开vptr的问题。
具体实现
1. 修改基类与子类,添加序列化接口
在基类中定义纯虚的序列化/反序列化方法,子类实现具体逻辑,同时加入类型标识区分不同子类:
// base.h #include <string> #include <fstream> // 类型标识,用于反序列化时区分子类 enum class ObjectType { TYPE_A }; class Base { public: virtual std::string show_string() = 0; // 序列化:将对象数据写入文件 virtual void serialize(std::fstream& out) const = 0; // 反序列化:从文件读取数据并创建对应子类对象 static Base* deserialize(std::fstream& in); int b; // 基类数据成员 }; class A : public Base { public: std::string show_string() override { return "this is A"; } void serialize(std::fstream& out) const override; int a; // 子类数据成员 };
2. 实现序列化与反序列化逻辑
// base.cpp #include "base.h" // 子类A的序列化:写入类型标识+基类数据+子类数据 void A::serialize(std::fstream& out) const { ObjectType type = ObjectType::TYPE_A; out.write(reinterpret_cast<const char*>(&type), sizeof(type)); out.write(reinterpret_cast<const char*>(&b), sizeof(b)); out.write(reinterpret_cast<const char*>(&a), sizeof(a)); } // 基类的反序列化工厂:根据类型标识创建对应子类并读取数据 Base* Base::deserialize(std::fstream& in) { ObjectType type; in.read(reinterpret_cast<char*>(&type), sizeof(type)); Base* obj = nullptr; if (type == ObjectType::TYPE_A) { obj = new A(); in.read(reinterpret_cast<char*>(&obj->b), sizeof(obj->b)); in.read(reinterpret_cast<char*>(&static_cast<A*>(obj)->a), sizeof(static_cast<A*>(obj)->a)); } // 后续扩展其他子类时,在此添加对应的分支即可 return obj; }
3. 修改数据生成端代码
#include <fstream> #include <iostream> #include "./base.h" using namespace std; int main() { std::fstream f("a.bin", std::ios::out | 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; }
4. 修改数据加载端代码
#include "./base.h" #include <fstream> #include <iostream> using namespace std; int main() { std::fstream f("a.bin", std::ios::in | std::ios::binary); for (int i = 0; i < 10; ++i) { Base* obj = Base::deserialize(f); if (obj) { cout << obj->show_string() << endl; cout << "a: " << static_cast<A*>(obj)->a << ", b: " << obj->b << endl; delete obj; // 记得释放内存 } } f.close(); return 0; }
性能优化建议
针对高性能需求,可做以下优化:
- 使用固定大小的标准类型(如
int32_t代替int),避免平台间类型大小差异,同时提升读写效率。 - 批量IO:将多个对象的数据先写入内存缓冲区,再一次性写入文件,减少磁盘IO次数。
- 跳过字节序转换:如果仅在同一平台运行,可忽略字节序处理,进一步提升速度;若需跨平台,统一转换为大端/小端即可。
- 对象池复用:如果频繁创建销毁子类对象,可使用对象池减少动态分配的开销。
内容的提问来源于stack exchange,提问作者nick
相关产品推荐
相关产品推荐

