Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Code crashes when derived class' destructor is virtual and base class' dtor is not

I tried the following code on gcc 4.4.5.

If the member 'data' is not present, the code executes fine, but in its presence, it crashes. It also doesn't crash when the derived class' dtor is not virtual.

I'm aware that the behavior would be Undefined as listed in C++03 (5.3.5 / 3) in both the cases, but still could someone provide me with some explanation why does it crash in the latter case?

And yes, I know UB means that anything can happen, but still I'd like to know implementation-specific details.

#include<iostream>    
using std::cout;

struct base{
int data;
   base(){
      cout << "ctor of base\n";
   }
   ~base(){
      cout << "dtor of base\n";
   }
};

struct derived :  base{
   derived(){
      cout << "ctor of derived\n";
   }
   virtual ~derived(){
      cout << "dtor of derived\n";
   }
};

int main(){
   base *p = new derived;
   delete p;
}
like image 265
Saurabh Manchanda Avatar asked May 27 '11 16:05

Saurabh Manchanda


Video Answer


2 Answers

Assuming what happens on my system (gcc 4.6.0, linux x86_64) is the same as what happens on yours (it also crashes with data and runs without), the implementation detail is that p does not point at the beginning of the memory block allocated for the object of type derived.

As valgrind told me,

Address 0x595c048 is 8 bytes inside a block of size 16 alloc'd

You can see that for yourself if you print the values of the pointers:

derived * d = new derived;
std::cout << d << '\n';
base *p = d;
std::cout << p << '\n';

And the reason for that is that object layout in gcc is {vtable, base, derived}

When base is empty, the size of {vtable, base, derived} and {base} happen to be the same because allocating an object of empty class occupies nonzero number of bytes, which happens to be equal in both cases.

When derived has no virtual functions, vtable is not present, the addresses are again the same and delete succeeds.

like image 106
Cubbi Avatar answered Sep 20 '22 11:09

Cubbi


the size of the two types does not match and the layout in your example should differ.

you are comparing pod types versus a type with a vtable (the layout and offsets are implementation defined). when the destructor is called, the address of implicit this is assumed to have the layout of base, but this is actually derived. what's executed is equivalent to writing to/reading from an invalid address.

like image 23
justin Avatar answered Sep 21 '22 11:09

justin