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

如何将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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 04:25:33