c++: following piece of code crashes
#include <iostream>
using namespace std;
class B
{
public:
B() { cout << "Base B()" << endl; }
~B() { cout << "Base ~B()" << endl; }
private:
int x;
};
class D : public B
{
public:
D() { cout << "Derived D()" << endl; }
virtual ~D() { cout << "Derived ~D()" << endl; }
};
int
main ( void )
{
B* b = new D;
开发者_C百科 delete b;
}
---- output----------
Base B()
Derived D()
Base ~B()
*** glibc detected *** ./a.out: free(): invalid pointer: 0x0930500c ***
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6[0xb7d41604]
/lib/tls/i686/cmov/libc.so.6(cfree+0x96)[0xb7d435b6]
/usr/lib/libstdc++.so.6(_ZdlPv+0x21)[0xb7f24231]
./a.out[0x8048948]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb7ce8775]
./a.out[0x80487c1]
Aborted
If i remove the private member "int x" from the base class, it works fine
The destructor for the base class B must be virtual too.
class B
doesn't have a virtual destructor and you try to delete
an instance of class D
derived from class B
through a pointer to class B
- that's undefined behavior. You have to make class B
destructor virtual to make your code work.
What you are doing is UB, but for the specific compiler you are using behavior can be described as follows. To ease the ASCII-graphics below, modifying the example, adding a y
member to D
and modifying main
.
#include <iostream>
using namespace std;
class B
{
public:
B() { cout << "Base B()" << endl; }
~B() { cout << "Base ~B()" << endl; }
private:
int x;
};
class D : public B
{
public:
D() { cout << "Derived D()" << endl; }
virtual ~D() { cout << "Derived ~D()" << endl; }
private:
int y; // added.
};
int
main ( void )
{
D* d = new D; // modified.
B* b = d;
delete b;
}
In the compiler you are using, the vtable, if any, is placed in the beginning of the memory block. In the compiler you are using the memory layout for this is as follows:
+--------+
| vtable | <--- d points here, at the start of the memory block.
+--------+
| x | <--- b points here, in the middle of the memory block.
+--------+
| y |
+--------+
Later when calling delete b
the program will try to free
the memory block using the b
pointer, which points to the middle of the memory block.
This will in turn result in the crash due to the invalid pointer error.
An alternative answer may be to use boost::shared_ptr : its templated constructors will remember that your object is of type D.
#include <boost/shared_ptr.hpp>
int
main ( void )
{
boost::shared_ptr<B> b( new D );
}
The above modification of your code will work fine, even without a virtual destructor.
By the way, unless you want to store pointers to D, there is no use in making D's destructor virtual.
Well, if you don't want a virtual destructor, then you must delete the object with a pointer to it's actual type:
int
main ( void )
{
D* as_d = new D;
B *as_b = as_d;
// you can use the object via either as_b or as_d but
// you must delete it via as_d
delete as_d;
}
That said, if you are not careful, it can be easy to delete the object through the wrong pointer.
So I know you don't want it, but for your own sanity, just make the B destructor virtual.
When you destroy derived class object using base class pointer then , it will lead to partial destruction of the object( ( only base class constructor is invoked ) if base class ctor is not virtual.
so you must make base class desrtuctor as virtual.
When a class is purely non virtual, it does not have an entry for the VPTR. Therefore B is exactly 4 bytes.
Here is an illustration of the memory. VPTR is at the smallest memory location. This is so that all derived classes know where to find the VPTR. Hence, D is 8 bytes, the first 4 is VPTR, and the next 4 for x.
But, isn't D is-a B? No, it works in the same way as multiple inheritance. When you assign a D address into a B pointer, the compiler knows that, and instead of giving you the REAL address of D, it gives you the address offsetted so that it works like a B. In this case, its really offset by 4 bytes. So when you try to B->x, you get the right value.
When you pass this offset address back to the heap in free, everything goes crazy, because what it needs is the original address. This behavior is not undefined. It happens when in multiple inheritance as well.
I think the fact of being the integer member causing a memory crash, it's just a matter of "luck". I mean, if your destructor in the base class isn't virtual, the "destruction" of D isn't called.
So, in memory, your object D in the heaps could look like:
Object D
+-----------+
| B subobj. |
+-----------+
| D members |
+-----------+
If the B's destructor isn't virtual and if you delete a pointer base to B
, the D
part isn't destructed. In your case, the D
part has size 0, and the B
part a size of sizeof(int)
(4 bytes), and that makes the situation a little more complicated to "guess", but perhaps you compiler is adding additional information for any reason to your objects in memory.
So, after the deletion of b
, but before the end of the application, perhaps some piece of code adding by the compiler at exit time is causing the crash because of your unpropertly deletion of b
(reusing of this part of memory for example or something like that).
Since your code is very short, you could inspect the behaviour of your code with 'gdb', at assembly level, in the interval between the deletion of b
and the termination of your code.
精彩评论