从文件中读取对象后无法调用虚拟成员函数
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.
更复杂的东西
问题:
我使用 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.