tags:

views:

200

answers:

7
#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;
    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

+6  A: 

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.

sharptooth
+12  A: 

The destructor for the base class B must be virtual too.

João da Silva
I dont want that
Ganesh Kundapur
@Ganesh Kundapur: You don't want it to crash either...
dalle
Unfortunately you have to, if you want to release the object as you do in main(). Otherwise, you have to delete it as a D object: D *d = (D *) b; delete d;
João da Silva
@Ganesh Kundapur: Your code has UB. Please read FAQ 20.7 http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.7, particularly, the last para.
dirkgently
@dirkgently: Or better yet quote a reputable source. Like let us say the C++ standard. n2521: 5.3.5-3: <qoute>In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined.</quote>
Martin York
@Ganesh: You want that. You just don't know yet that you want it if you do C++. Please read the FAQ.
Thorsten79
@Martin York: Since when did _the_ FAQ fall into ill repute? :) Of course, the standard is better, but the FAQ is considered a gem of a resource by everyone I know who has perused through it.
dirkgently
@Thorsten79: Yeap, in fact that's what virtual destructors are for.
sharptooth
@Ganesh Kundapur: Why don't you want make B::~B() virtual?
sharptooth
As i mentioned, if i remove the private member and without B::~B(), virtual, it works fine.It crashes with the integer member in base class
Ganesh Kundapur
@Ganesh Kundapur: That's how UB behaves - you change something, now it looks working. How do you know it doesn't overwrite some memory or corrupt heap? Why don't you want a virtual destructor in the first place?
sharptooth
@dirkgently: When has it ever been considered a standard source. I have read it he makes a lot of good points, but it is not something I would quote because of the errors and the __strong__ opinion (rather than just facts) of the author. Not that I have anything against opinion but when it is the antipathy of the leading lights (Meyers/Alexandrescu/Sutter) of C++ you begin to ask questions. Remember this is not a boost type publication (reviewed and collaborative), it is the opinion of a single author who is not keen on taking updates or reconsidering his opinion to move with standard practice
Martin York
@Martin York: "[...] antipathy of the leading lights" -- source? Opinionated -- yes, I totally agree. I do see updates every now and then though (when I go back; they are usually marked with dates).
dirkgently
@Ganesh Kundapur: No, it doesn't "work fine". It simply gets broken in some other way. Your code has undefined behavior. There's no way (and no point) to predict how UB will manifest itself. It might even seem to "work fine".
AndreyT
@AndreyT: Actually it might really work fine in that very case. UB is not obliged to be problematic - many cases attributed as UB really work just fine under certain conditions.
sharptooth
@sharptooth: Working fine in certain conditions is code for: its broken but I don't care because I am not going to maintain it and the unlucky pleb that gets the bug next year (when the compiler is upgraded) is going to spend weeks fixing a problem that could have been avoided.
Martin York
@Martin York: Yes, it might stop working if anything changes. So it's broken and should be fixed. It doesn't mean that it doesn't work fine right now under these specific conditions.
sharptooth
Thanks for all the responses. But still i didn't get what is exact reason for the crash. Many responded with the base class with the virtual destructor is the remedy. Yes, that is the way normal code is written. Why in this case it crashes? What's exatly happening on deleting the instance of derived class from the base class pointer without base class destructor as virtual. Moreover how the private member is affecting this. What happens with the private member and without the private member
Ganesh Kundapur
@Ganesh Kundapur: If you're so curious you might want to look into the disassembly being executed. That's the most reliable way to see the actual cause of the crash.
sharptooth
Part 1/2: Each object in memory has an extra pointer to a vtable. Each virtual function has an entry in this table, and when you invoke a virtual method the table for the object is searched for the method to invoke. Thus, when you invoke foo() on a B*, it goes to foo() on D because the entry for "foo" on D's vtable points there. Now, a destructor is similar to a method. So "delete b;" will invoke B's destructor, because it's _not_ virtual. If it were virtual, it would invoke D's destructor, which is correct. The crash is because D is a bit bigger than B (because of the vtable pointer).
João da Silva
Part 2/2: So B's destructor will try to free() the wrong size. Having the "x" field makes B 4 bytes bigger, which happens to be the size of a pointer too (on x86 and many other architectures). *Just because having "x" prevents the crash doesn't mean it's correct code*. Also, the layout in memory varies from compiler to compiler, so it might be slightly different that what was described here. Read a bit on vtables to understand the problem. As a solution, just make B::~B() virtual. If you can't (because it comes from a library, for instance), then don't subclass it; have a B field in D instead.
João da Silva
@João da Silva: Actually the error message at the crash point says "invalid pointer", so I guess it's something with a pointer adjustment, not wrong size.
sharptooth
@Ganesh Kundapur: The exact answer to "why?" question depends on tens (if not hundreds) implementation details. There's no "C++" answer to that question, besides the one that has already been given to you - UB. Ther rest is not C++ anymore, but rather features, quirks and bugs of a particular version of your particual compiler with some particular set of compilation settings in some particular environment. No one can answer the question from here, without resorting to random guessing. Only you can. Inspect the generated assembly code, if you care to know the exact reason.
AndreyT
+1  A: 

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.

Benoît
sizeof(B) = 4 bytes whereas sizeof(D) = 8 bytes. While freeing the base part from the derived instance, tries to free 8 bytes, whereas it has 4 bytes allocated. Am i right?
Ganesh Kundapur
@Ganesh Kundapur: The problem is not the wrong number of bytes being freed, the problem is the implementation is not required to do anything reasonable in the case you describe in the question. That's called UB - maybe it works, maybe it crashes, maybe it causes some delayed devastating bug.
sharptooth
Why did someone -1 my answer ?
Benoît
+1  A: 

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.

R Samuel Klatchko
A: 

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.

Ashish
There's nothing to destroy here - class B has an integer member variable with a trivial destructor, class D has none, nothing to destroy. The problem is his code triggers undefined behavior. Speculating about partial destruction makes no sense here.
sharptooth
A: 

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.

dalle
A: 

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.

sep