调用虚方法时,奇怪的分段错误,HELP

时间:2011-03-20 18:30:51

标签: c++

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

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);     
}

我想这样做的原因是因为我需要从文件中读取对象,因此我需要将缓冲区转换为特定类型然后我可以调用mothod。

6 个答案:

答案 0 :(得分:3)

当您调用方法时,很可能使用代码覆盖虚拟功能表,从而导致地址错误。您不仅可以将对象保存到文件中,还可以通过在保存时恢复内存内容来恢复它们。

有一些很好的库,比如boost::serialization来保存和恢复对象。我建议您阅读此内容或将您的对象转换为不包含引用或地址的普通旧数据类型(结构)。

答案 1 :(得分: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() - 方法从文件中读取数据并在内部数据上设置值。

答案 2 :(得分:1)

有几个原因可以保证此代码不起作用。我认为最关心的是这里的代码:

char object[48];

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

char object[sizeof(FromBase)];

此外,由于对齐问题,无法保证此功能。 C ++中的每个对象都有一个 alignment ,其中一些数字的地址必须是其倍数。声明变量时,C ++确保它的类型具有正确的对齐方式,但不能保证它最终具有任何其他类型的对齐方式。这意味着当您声明char数组时,无法保证它的对齐方式与实际FromBase对象相同,并且使用缓冲区作为该类型的对象会导致未定义的行为。

正如其他人所指出的那样,你也有一个问题,因为这一行:

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

不更新你用来跟踪文件指针的局部变量,所以你读回来的几乎肯定是垃圾(如果你回读任何东西)。段错误可能来自虚拟调度表的字节,而无法正确读回。

答案 3 :(得分:1)

除了人们指出的所有其他问题外。

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

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

NOT 保证可以正常工作 您依赖于大量的实施细节。 objectarray of char!不是FromBase因此编译器没有机会初始化其任何依赖于实现的细节。

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

这太可怕了。你不应该这样做

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

答案 4 :(得分:0)

第二次使用fopen时我可以看到第一个问题:

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

应该是这样的:

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

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

答案 5 :(得分:0)

一个问题是字符数组没有名为find的方法。 强制转换不会将数组转换为FromBase或Base。它只告诉编译器忽略错误。