开发者

Segmentation fault when calling virtual method

Here is my code, I casted the buffer to different type of objects, is this what causes the failure? I really want to know why the FromBase::find2(int key) works, but not 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);

       // lo开发者_JAVA百科oks like this works
       (FromBase*)object->find2(7);

       //These two do not work, I got segmentation fault!
       (FromBase*)object->find(7); 
       (Base*)object->find(7);     
}

The reason I want to do this is because I need to read the object from a file, thus I need to cast the buffer to an particular type then I can call the mothod.


There is a high chance that you are overwriting the virtual function table with your code leading to a bad address when you call the method. You cannot just save objects into a file and expect to restore them by just restoring the memory content at the time they were saved.

There are some nice libraries like boost::serialization to save and restore objects. I would urge you to read about this or to turn your objects into plain old data types (structs) containing no references or addresses.


There are several reasons why this code is not guaranteed to work. I think the biggest concern is this code here:

char object[48];

The number 48 here is a magic number and there's absolutely no guarantee that the size of the object you're writing out is 48 bytes. If you want to have a buffer large enough to hold an object, use sizeof to see how many bytes you need:

char object[sizeof(FromBase)];

Moreover, this is not guaranteed to work due to alignment issues. Every object in C++ has an alignment, some number that its address must be a multiple of. When you declare a variable, C++ ensures that it has the proper alignment for its type, though there's no guarantee that it ends up having the alignment of any other type. This means that when you declare a char array, there's no guarantee that it's aligned the same way as a real FromBase object would be, and using the buffer as an object of that type results in undefined behavior.

As others have pointed out, though, you also have a problem because this line:

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

Doesn't update the local variable you're using to keep track of the file pointer, so what you're reading back is almost certainly going to be garbage (if you read back anything at all). The segfault is probably coming from the bytes for the virtual dispatch table not being read back in correctly.


// will these two methods work? I got segmentation fault!
(FromBase*)object->find(7); 
(Base*)object->find(7);     

No they will not work. The segmentation fault might be a hint ;)

object is a type on the stack, which is fine, but you need to call the class constructor. If this was valid c++, ANY memory could be casted to any class.

I'd start off by creating the class on the stack and call some Load()-method on it, e.g.

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

And let the Load()-method read the data from file and set values on the internal data.


Apart from all the other problems that people have pointed out.

I absolutely shocked that nobody has mentioned that:

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

Is just NOT guaranteed to work.
You are depending on a raft of implementation details. object is an array of char! Not a FromBase thus the compiler has not had the chance to initialize any of its implementation dependent details.

Even if we assume that the implementation uses a vtable (and thus a vtable pointer in the class). Does the implementation use a relative pointer or an absolute pointer. Assuming you want to save with one run and then reload the next time? Are you assuming the vtable is actually located in the same location between different runs (what happens when you load this part of the application from a dynamic library)!

This is just horrible. You SHOULD NOT DO THIS EVER.

If you want to serialize and de-serialize the object from storage. Then the class has to know how to do the serialization itself. Thus all the correct constructors/destructors get called at the correct time.


First problem I can see when you use fopen second time:

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

which should be this:

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

That means, in your code you're trying to read data using fptr which is already closed!


One problem is that the array of characters do not have a method called find. The cast do not convert the array to FromBase or Base. It only tells the compiler to ignore the error.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜