调用虚拟方法时出现段错误

问题描述 投票:-1回答:6

这是我的代码,我将缓冲区强制转换为不同类型的对象,这是导致失败的原因吗?我真的很想知道为什么[[FromBase :: find2(int键),但不是FromBase :: find(int键)?

class Base { public: virtual int find(int key)=0; int keys[4]; }; class FromBase:public Base { public: FromBase(); int find(int key); int find2(int key); }; FromBase::FromBase() { for(int i=0;i<4;i++) keys[i]=-1; } int FromBase::find(int key) { for(int i=0;i<4;i++){ if(keys[i]==key) return i; } return i; }; int FromBase::find2(int key) { for(int i=0;i<4;i++){ if(keys[i]==key) return i; } return i; }; int main() { FromBase frombase; FILE* fptr=fopen("object.dat","w"); fwrite((void*)&frombase,48,1,fptr); fclose(fptr); char object[48]; fptr=fopen("object.dat","r"); fread((void*)object,48,1,fptr); // looks like this works (FromBase*)object->find2(7); //These two do not work, I got segmentation fault! (FromBase*)object->find(7); (Base*)object->find(7); }
之所以这样做,是因为我需要从文件中读取对象,因此我需要将缓冲区转换为特定类型,然后才能调用该方法。
c++ virtual
6个回答
4
投票
调用该方法时,您的代码很有可能会覆盖虚拟函数表,从而导致地址错误。您不能仅将对象保存到文件中并期望通过仅在保存对象时还原内存内容来还原它们。

[有些不错的库,例如boost::serialization,用于保存和还原对象。我敦促您阅读有关此内容或将您的对象转换为不包含引用或地址的普通旧数据类型(结构)。


1
投票
有几个原因不能保证此代码有效。我认为最大的担心是这里的代码:

char object[48];

这里的数字48是一个魔术数字,绝对不能保证您要写出的对象的大小为48字节。如果您想要一个足够大的缓冲区来容纳一个对象,请使用sizeof查看所需的字节数:

char object[sizeof(FromBase)];

此外,由于对齐问题,不能保证此方法可以正常工作。 C ++中的每个对象都有一个

alignment,它的地址必须是它的倍数。声明变量时,C ++确保其类型具有正确的对齐方式,尽管不能保证最终它具有任何其他类型的对齐方式。这意味着,当您声明char数组时,不能保证它与真实FromBase对象的对齐方式相同,并且将缓冲区用作该类型的对象会导致未定义的行为。

但是,正如其他人指出的那样,您也遇到了问题,因为此行:

fopen("object.dat","r");

不会更新用于跟踪文件指针的局部变量,因此,您几乎要回读的内容肯定是垃圾(如果您回读了所有内容)。 segfault可能来自未正确读回虚拟调度表的字节。

1
投票
// will these two methods work? I got segmentation fault! (FromBase*)object->find(7); (Base*)object->find(7);
不,它们将无法工作。分割错误可能是一个提示;)

object是堆栈上的一种类型,这很好,但是您需要调用类构造函数。如果这是有效的c ++,则可以将任何内存强制转换为任何类。

我首先在堆栈上创建类,然后在其上调用一些Load()方法,例如

FromBase object; object.Load("object.dat");

然后让Load()方法从文件中读取数据并在内部数据上设置值。

1
投票
除了人们指出的所有其他问题。

我绝对震惊,没有人提到:

(FromBase*)object->find2(7);

只是

NOT保证有效。您取决于大量的实现细节。 objectarray of char!不是FromBase,因此编译器没有机会初始化其任何与实现相关的细节。

即使我们假设实现使用vtable(因此也使用类中的vtable指针)。实现是使用相对指针还是绝对指针。假设您要保存一次运行,然后在下一次重新加载?您是否假设vtable实际上位于不同运行之间的同一位置(从动态库加载应用程序的这一部分会发生什么情况!)

这太可怕了。您

永远不要做这件事

如果要从存储中序列化和反序列化对象。然后,该类必须知道如何自己进行序列化。因此,所有正确的构造函数/析构函数都会在正确的时间被调用。

0
投票
第二次使用fopen时我看到的第一个问题:

fopen("object.dat","r"); //problem - your code

应该是这个:

fptr = fopen("object.dat","r"); //fix (atleast one fix)

这意味着,在您的代码中,您试图使用已经关闭的fptr读取数据!

0
投票
一个问题是字符数组没有名为find的方法。强制转换不会将数组转换为FromBase或Base。它仅告诉编译器忽略该错误。
© www.soinside.com 2019 - 2024. All rights reserved.