You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 06:05:20