Interesting crash...

Put the code below in a source file and compile it using Visual Studio 2005 (if possible). Notice that there's first an assert that occurs and after that a heap corruption error message (if you ignore the assert). I'm not really sure why it is acting this way, the code below seems perfectly valid. It should only call Base1's constructor without any problems. Perhaps I'm misunderstanding something?

#include <iostream>

using namespace std;

class Base1
{
public:
~Base1() { cout << "Base1: Destroyed\n"; }
};

class Base2 : public Base1
{
public:
virtual ~Base2() { cout << "Base2: Destroyed\n"; }
};

class Derived : public Base2
{
public:
~Derived() { cout << "Derived: Destroyed\n"; }
};

int main()
{
Base1* foo = new Derived;
delete foo;

system("pause");
return 0;
};
[952 byte] By [MrDoomMaster] at [2007-11-20 11:38:35]
# 1 Re: Interesting crash...
What I can say for sure is that Base1 and Derived differ in size, due to the latter inheriting a vtable pointer from Base2. So you are allocating 4 bytes on the heap but freeing only 1, which means at least a memory heap. Not sure if this per definition results in undefined behaviour, though.
treuss at 2007-11-9 1:25:53 >
# 2 Re: Interesting crash...
The code contains undefined behavior because it's calling delete on a pointer to a base subobject that does not have a virtual destructor.
googler at 2007-11-9 1:26:53 >
# 3 Re: Interesting crash...
The code contains undefined behavior because it's calling delete on a pointer to a base subobject that does not have a virtual destructor.

What you're saying really doesn't make any sense to me. Are you saying that even if I remove the virtual it should still be undefined? Because if I do remove the virtual it functions just fine. I still don't understand how Base2 having a virtual destructor causes any problems, especially since Base2 isn't explicitly involved in anything I'm doing.
MrDoomMaster at 2007-11-9 1:28:00 >
# 4 Re: Interesting crash...
He is saying to ADD the virtual keword to the Base1 destructor.

Also ... add constructors to each class and output a message
like you do with the destructors.
Philip Nicoletti at 2007-11-9 1:28:55 >
# 5 Re: Interesting crash...
He is saying to ADD the virtual keword to the Base1 destructor.

Also ... add constructors to each class and output a message
like you do with the destructors.

I know what I need to do to fix the issue, however that's not my goal. My goal is to understand WHY it is a problem in the first place. There's something about the situation itself which I'm unaware of; I'm simply trying to learn. If someone were to ask me WHY the original code I posted causes a problem, I wouldn't be able to answer that question. Perhaps with some insight from you guys that might change.

I'll add constructors with output messages as well, however I'm not sure what you feel this will accomplish. Perhaps you could emphasize?

Thanks for your response; I appreciate it.
MrDoomMaster at 2007-11-9 1:30:01 >
# 6 Re: Interesting crash...
The code contains undefined behavior because it's calling delete on a pointer to a base subobject that does not have a virtual destructor.What you're saying really doesn't make any sense to me. Are you saying that even if I remove the virtual it should still be undefined? Because if I do remove the virtual it functions just fine.Undefined behaviour does not mean your program will crash. It means that it may crash or may "function just fine" or, in the worst case, may sometimes crash and sometimes work.

If you remove the virtual from ~Base2(), it will still be undefined behaviour. The reason that it works, is just the fact that the size of objects of Derived is then the same as the size of objects of Base1, so allocating space for an object of class Derived and deallocationg for an object of class Base1 just happens to work. No guarantee that it will work with different compilers, on different OSes or even on a future version of the same OS.
treuss at 2007-11-9 1:31:00 >
# 7 Re: Interesting crash...
If someone were to ask me WHY the original code I posted causes a problem, I wouldn't be able to answer that question.The answer is "because it is undefined behaviour". That is the reason why it causes a problem.

If you tried to pinpoint why the code crashes beyond stating the obvious, then you're painting yourself in the corner. Someone else could have the same code, and have it crash for a different underlying reason, or it may run flawlessly. The code may even work when used with different compiler options, as well as different compilers.

So how many permutations of differing answers will you give? Will you have a different answer for Linux gcc version 3.0 and gcc version 4.0? What about why it crashes with all the differing Visual Studio compilers (and compiler options, and that is if it does crash)?

The definitive answer for "why" is that "it is undefined behaviour because the language definition says so" -- nothing more needs to be stated.

Regards,

Paul McKenzie
Paul McKenzie at 2007-11-9 1:32:05 >
# 8 Re: Interesting crash...
At the risk of going on when it's been, rather properly, declared that nothing more need be said (like that ever stopped me)...

I know what I need to do to fix the issue, however that's not my goal

If, and only if, that fix involves placing a virtual keyword in from the destructor in the base1 class, the you don't really know what to do to genuinely fix the problem - it would be just enough to make it go away for now.

Undefined behavior is a pain. It can act like the memory bugs of old (and it's often related under the hood). Like others have said, works sometimes, doesn't work sometimes - frustrating.

Especially if you're trying to understand why.

Let's take the subject from your viewpoint, at the moment that it's crashing, and the virtual keyword isn't in the base1 destructor.

On that compiler, with those settings, 'undefined' takes on a character for that compilation and run sample. For whatever reason, the fact that a virtual destructor hasn't been declared means the destruction process is corrupt. It's corrupt even if it seems to work, but you could get the sense that's ok simply because it seems to work.

When a destructor is virtual, and you delete from the base1 perspective, the virtual call to the destructor causes the execution of the most derived destructor first, then, in order, going toward the root of the hierarchy, AS IF you had deleted from the most derived viewpoint.

That's basically it - the fact is all objects MUST be 'destructred' (I hate that invented word) from the most derived viewpoint. If they're not, it's undefined behavior - generally a crash, but not always, sometimes a memory leak, which is bad enough, sometimes, only occasionally - it seems to work.

Without a virtual destructor, the members of and dynamic allocations that may have performed in the most derived (and, in your example, those classes in between) will not be 'destructed' (well, probably not - undefined behavior, remember - it depends on the compiler). Usually this does mean the most derived won't be destroyed (I like that word a little better). I say that because it is not defined that a destructor would be called in that context - when deleted from the base1 perspective, nothing is defining the fact that the most derived destructor should be called - and so, you have the behavior that results from NOT defining something that must be defined (remember, we MUST destroy an object from the most derived class in the hierarchy).

Let me labor that last point a moment. I'm not sure it is explicated in the 'standard' (I'm a bit remiss on reading that particular, dry document). However, if an object is DELETED from a base perspective, it still must be DESTROYED from the most derived perspective. Unless a destructor is called for each and all levels of the hierarchy, then the destruction of that portion that wasn't called has not been defined (or performed, in all likelyhood). Such an object can't be predictable when destroyed. It can only be predictable, and therefore reliable, when it is clearly explicated by, in this case, the virtual keyword in the most base class of the hierarchy.
JVene at 2007-11-9 1:33:05 >
# 9 Re: Interesting crash...
Thanks for clearing all that up guys. I understand now. It's kind of funny, now that I understand it I find that I'm actually approaching the issue from a different perspective. My original view of the issue was from an inappropriate mind set. It seems rather obvious now, and I probably made it more complicated than it needed to be.
MrDoomMaster at 2007-11-9 1:34:10 >