views:

426

answers:

7

For C++ gurus: Can you please elaborate why this code crashes at the places mentioned? I am a bit stumped on this. I guess that it has got something to do with sizeof(int) but I am not so sure. Can anybody explain?


class Base
{
public:
    virtual void SomeFunction() 
    {
     printf("test base\n");
    }

    int m_j;
};

class Derived : public Base {
public:
   void SomeFunction() 
   {
       printf("test derive\n");
   }

private:
   int m_i;
};

void MyWonderfulCode(Base baseArray[])
{
   baseArray[0].SomeFunction();  //Works fine
   baseArray[1].SomeFunction();  //Crashes because of invalid vfptr
   baseArray[2].SomeFunction();  //Crashes because of invalid vfptr
   baseArray[3].SomeFunction();  //Works fine
   baseArray[4].SomeFunction();  //Crashes because of invalid vfptr
   baseArray[5].SomeFunction();  //Crashes because of invalid vfptr
   baseArray[6].SomeFunction();  //Works fine
   baseArray[7].SomeFunction();  //Crashes because of invalid vfptr
   baseArray[8].SomeFunction();  //Crashes because of invalid vfptr
   baseArray[9].SomeFunction();  //Works fine
}
int _tmain(int argc, TCHAR* argv[])
{
   Derived derivedArray[10];
   MyWonderfulCode(derivedArray);
   return 0;
}


+11  A: 

Never treat arrays polymorphically. The C++ language does not support that. (See also this Related question.)

Daniel Daranas
+8  A: 

Arrays can't be used for storing objects of different types or for treating objects polymorphically - store pointers instead.

Sizes of Base and Derived are different - the code seeing the Base[] array has no means to detect that it's really a Derived[] array and just indexes the array incorrectly producing all sorts of undefined behaviour.

sharptooth
+12  A: 

Check this FAQ: Is array of derived same as as array of base?

Naveen
Accepting your answer because of the excellent link that details the exact problem and its solution.
Aamir
+15  A: 

sizeof(Derived) is greater than sizeof(Base). That's the reason.

Generally, indexing an array of Foo objects at index i works the following way:

element_address = array_base_address + i * sizeof(Foo)

You can see how this indexing breaks if the array elements are not of expected size. Why it works for some indices is because sometimes the calculated element address points to a valid object in memory (but it's not actually the ith object).

laalto
Great answer IMO.
toto
A: 

Make your MyWonderfulCode function take an array of Base pointer instead of Base itself, then you can use that array polymorphically.

Kei
+2  A: 

Adrian Grigore answered:

If you use a std::vector, then there shouldn't be any crash.

then deleted the answer because of a comment:

Doesn't matter in this case. Would crash the same way.

But Adrian was mostly right - if MyWonderFulCode takes a vector<Base> or vector<Base>& or vector<Base>*, and you try to pass it a vector<Derived> or vector<Derived>*, then the calling code doesn't compile. Hence, I suppose, it doesn't crash either. It's not possible to convert from one vector type to the other (at least, not without creating a new vector and slicing the derived objects into it).

So compilation failure is what you'd want in this case too, because it's not possible to convert from one array type to the other either. The problem is that C/C++ can't tell the difference between a pointer and an array when passed as a function parameter. Because of the perfectly sensible implicit upcast from Derived* to Base*, we're lumbered with an implicit upcast from Derived[] to Base[], which is almost always broken. In C++ terms, it's a completely unjustified reinterpret_cast.

This is among the reasons C++ programmers don't use arrays (unless they have to).

Steve Jessop
+3  A: 

This problem is called shearing. its where the inherited part of the object is effectively stripped away, which results in the V table pointing to missing functions.

the solution is to use an array of pointers. Try this:

void MyWonderfulCode(Base* baseArray)
{
   baseArray[0].SomeFunction();  
   baseArray[1].SomeFunction();  
   baseArray[2].SomeFunction();  
   baseArray[3].SomeFunction();  
   baseArray[4].SomeFunction();  
   baseArray[5].SomeFunction();  
   baseArray[6].SomeFunction();  
   baseArray[7].SomeFunction();  
   baseArray[8].SomeFunction();  
   baseArray[9].SomeFunction();  
}
int _tmain(int argc, TCHAR* argv[])
{
   Base* derivedArray[10];

   for( int i = 0; i < 10; ++i ) derivedArray[i] = new Derived;

   MyWonderfulCode(derivedArray);
   return 0;
}
0xC0DEFACE