从文件中读取对象后无法调用虚拟成员函数

Could not call virtual member function after read object from file

问题:

我使用 std::fstream 以二进制模式将对象写入文件。但是,当我将它从那个文件读回到另一个对象,然后调用这个新对象的虚拟成员函数之一时,出现内存访问冲突错误。

代码如下所示:

class A
{
public:
   // data
   virtual void foo() = 0;
}; 

class B: public A
{
public:
   // added data 
   virtual void foo() { ... }
}

int main()
{
  // ...
  A* a = new B();
  A* b = new B();

  file.write((char*)a, sizeof(B));
  // ...
  thatSameFile.read((char*)b, sizeof(B));

  b->foo(); // error here

}

我发现了什么:

经过几个小时的调试,我看到 b__vfptr 成员(据我所知,它是指向虚拟 table 的指针对象)在文件读取语句后发生变化。我猜我不仅将 a 的数据写入文件并将其复制到 b,我还将 a 的虚拟 table 指针复制到 b.

我说的对吗?我该如何解决这个问题?

Is what I said correct?

不,这是不正确的。问题的根源在于您只是将地址写入文件并将它们加载回来(此外,使用了错误的大小)。

file.write((char*)&a, sizeof(B));

前一行将大小为class B的变量a中存储的指针写入文件。

不能从文件中重建指针,因为它们需要内存管理(在您的情况下是动态分配)。

所以声明

thatSameFile.read((char*)&b, sizeof(B));

只是用一些任意的、无意义的值以及堆栈上的一些额外字节覆盖存储在 b 中的指针。这基本上是 未定义的行为

至于,这是一个错字;它不会改变我上面写的内容。无法从文件中重建指针。


How can I fix this problem?

如果您需要编写结构的二进制图像/类。您可以对

这样的普通 POD 类型执行此操作
struct Foo {
    char c;
    int i;
    double d;
    long arrlong[25];
};

仅包含整数类型,或整数类型的固定大小数组。

此类类型可以写入 "safely" 到二进制文件并从二进制文件恢复相同的目标体系结构(参见 Endianness):

Foo a;

file.write((const char*)&a, sizeof(Foo));

// ...

Foo b;
thatSameFile.read((char*)&b, sizeof(Foo));

此外,您不能为此使用具有虚拟多态继承的类型。仅仅重新加载一个 vtable(C++ 标准甚至没有指定)不足以安全地告诉运行时什么是实际的底层类型。


你应该查找 serialization/deserialization 来实现你想要的。有几个库很好地支持二进制格式,例如 boost::serialization or google protocol buffers,它们可以帮助您构建比 POD serialization/deserialization.

更复杂的东西