As others have pointed out, you delete an object whose static type is different from its dynamic type, and since the static type does not have a virtual destructor, you get undefined behavior. This includes the behavior of sometimes working and sometimes not working, as you see. However, I think you are interested in a deeper understanding of what is happening with your specific compiler.
Class A has no members, so its data layout is as follows:
struct A { };
Since class B comes from class A , class A becomes embedded in B. When class B has no virtual functions, the layout ends as follows:
struct B { A __a_part; int x; };
The compiler can convert B* to A* by simply taking the address __a_part , as if the compiler had this function:
A * convertToAPointer (B * bp) {return & bp β __ a_part; }
Since __a_part is the first member of B , B* and A* point to the same address.
Code like this:
A* bptr = new B; delete bptr;
Effectively doing something like this:
In this case, vp2 and vp1 same. The system allocates and frees the same memory address, so the program starts without errors.
If class B has a virtual member function (destructor in this case). The compiler adds a pointer to the virtual table, so class B looks like this:
struct B { B_vtable* __vptr; A __a_part; };
The problem here is that __a_part is not the first member, and the convertToAPointer operation convertToAPointer now change the address of the pointer, so vp2 and vp1 no longer point to the same address. Since a different memory location is freed than the allocated one, you get an error.