Multiple Inheritance Problem
There was a bug that stumped me for a while and I'd like to know the exact reason why this is happening.
I'm going to simplify the problem here.
Code:
class Base1 - Concrete class
{
virtual void DoSomething1(){}
}
class Base2 - Abstract class
{
virtual void DoSomething2()=0;
}
class Derived : Base1, Base2
{
void DoSomething1(){}
void DoSomething2(){}
}
When I create a new instance of Derived and call DoSomething2(), the function that gets invoked is DoSomething1()
However, here is what the root cause appears to be.
EDIT: Example was slightly off we are casting back to Base2.
Code:
Derived *d = new Derived();
DWORD dPtr = (DWORD)d;
Base2 *b2 = (Base2*) dPtr;
b2->DoSomething2(); //DoSomething1() is called
How come casting to a DWORD and then casting back to Derived* screws things up?
Re: Multiple Inheritance Problem
Quote:
Originally Posted by Rigel
There was a bug that stumped me for a while and I'd like to know the exact reason why this is happening.
Obviously DWORDs are not pointers to Derived*, so any type of this back-and-forth C-style casting like this is not guaranteed to work correctly.
Regards,
Paul McKenzie
Re: Multiple Inheritance Problem
Quote:
Originally Posted by Paul McKenzie
Obviously DWORDs are not pointers to Derived*, so any type of this back-and-forth C-style casting like this is not guaranteed to work correctly.
Regards,
Paul McKenzie
Thanks, but I thought a pointer was just a variable that held a 4 byte integer memory address. I don't see how casting this back and forth can cause so much problem.
Re: Multiple Inheritance Problem
No!
To prove it.
Create an instance of Derived and then assign that one instance to two pointers, one of type Base1 and another of type Base2. Look at the results in a debugger.
You will instantly see two different addresses, but they are clearly point to the same instances.
Re: Multiple Inheritance Problem
Quote:
Originally Posted by Rigel
Thanks, but I thought a pointer was just a variable that held a 4 byte integer memory address. I don't see how casting this back and forth can cause so much problem.
In C++, and especially multiple inherited classes, casting must be done correctly and carefully. This means casting from compatible types, and using C++ style (not C style) casts to make sure this occurs. You can't just blindly cast to an integer, and then cast back from the integer.
Without the cast, what error did the compiler give you? Anytime you try and make the compiler shut-up an error by casting, you're digging yourself a big hole (i.e. what you're doing is most likely incorrect).
Regards,
Paul McKenzie
Re: Multiple Inheritance Problem
Quote:
Originally Posted by Paul McKenzie
...Anytime you try and make the compiler shut-up an error by casting, you're digging yourself a big hole (i.e. what you're doing is most likely incorrect).
I often tell beginners that a C style cast in a C+ program is equivilant to the following statement:
Quote:
Hey you stupid miserable little compiler, I know what should be done because I am great and infallible. Even though you ae tracking every byte of memory, all of the registers, etc you are still incapable of making any good decisions. Therefore you will listen to me without question and do exactly what I say regardless of the consequences.
Now THAT is a pretty arrogant statement.
Consider how you would feel if someone talked like that to YOU. It is no wonder that the end result is the compiler getting angry and generating destructive code in retailation. :eek: :eek: :eek: ;)
Re: Multiple Inheritance Problem
Quote:
Originally Posted by TheCPUWizard
I often tell beginners that a C style cast in a C+ program is equivilant to the following statement:
Now THAT is a pretty arrogant statement.
Consider how you would feel if someone talked like that to YOU. It is no wonder that the end result is the compiler getting angry and generating destructive code in retailation. :eek: :eek: :eek: ;)
The most egregious example is trying to cast a two dimenisonal array of T to a T**.
There has to be at least 20 or 30 threads on this same error, where the poster casts the two dimenisonal array to keep the compiler quiet. Even seasoned programmers, or at least supposed seasoned programmers, get fooled into doing this cast, just because it "feels natural" and the compiler is being too harsh.
If you did a search on CodeGuru, you would be surprised at the names that thought previously that doing this cast was safe.
Regards,
Paul McKenzie
Re: Multiple Inheritance Problem
Quote:
Originally Posted by Rigel
How come casting to a DWORD and then casting back to Derived* screws things up?
Well, you aren't exactly casting back the DWORD to Derived*. You're upcasting from DWORD to Base2*. This upcast is problematic because the compiler lacks information about what type DWORD is. By doing it I would say you're entering the realms of undefined behaviour.
To fix this you should first restore the actual type of the typeless DWORD, namely Derived*. Then you can perform the upcast to Base2*, like
Base2 *b2 = (Base2*) ((Derived*)dPtr);
Re: Multiple Inheritance Problem
Quote:
Originally Posted by _uj
Well, you aren't exactly casting back the DWORD to Derived*. You're upcasting from DWORD to Base2*. This upcast is problematic because the compiler lacks information about what type DWORD is. By doing it I would say you're entering the realms of undefined behaviour.
To fix this you should first restore the actual type of the typeless DWORD, namely Derived*. Then you can perform the upcast to Base2*, like
Base2 *b2 = (Base2*) ((Derived*)dPtr);
I believe that is still undefined behaviour (99.9998%), but much more likely to "randomly" work on 32 Bit Intel boxes at least...
Re: Multiple Inheritance Problem
Quote:
Originally Posted by TheCPUWizard
I believe that is still undefined behaviour (99.9998%), but much more likely to "randomly" work on 32 Bit Intel boxes at least...
You mean assigning a pointer to DWORD (an integer type) is undefined? That's true but if DWORD is replaced by void* it's correct C++ and should be portable.
Still the problem the OP reports persists also with void*. It has to be fixed the way I suggested, by restoring the typeless pointer to its actual type before upcasting.
So it's okay to make a pointer typeless by casting it to void*, but be sure to cast it back to its proper type before doing something with it. If not you get undefined behaviour
This is discussed in 5.6 Pointer to Void in Stroustrups C++ book.
Re: Multiple Inheritance Problem
Quote:
Originally Posted by _uj
You mean assigning a pointer to DWORD (an integer type) is undefined? That's true but if DWORD is replaced by void* it's correct C++ and should be portable.
Still the problem the OP reports persists also with void*. It has to be fixed the way I suggested, by restoring the typeless pointer to it's actual type before upcasting.
So it's okay to make a pointer typeless by casting it to void*, but be sure to cast it back to its proper type before doing something with it. If not you get undefined behaviour
It's discussed in 5.6 Pointer to Void in Stroustrups C++ book.
To/From a "void *" done properly is 100% defined and your are 100% correct.
On the other hand, there is NO requirement that DWORD (typically a representation of an integer of a given width) is in any way shape for form compatabile with a pointer. Even on an x64,m I64 machine I would expect it to fail..
Re: Multiple Inheritance Problem
Quote:
Originally Posted by _uj
Well, you aren't exactly casting back the DWORD to Derived*. You're upcasting from DWORD to Base2*. This upcast is problematic because the compiler lacks information about what type DWORD is. By doing it I would say you're entering the realms of undefined behaviour.
To fix this you should first restore the actual type of the typeless DWORD, namely Derived*. Then you can perform the upcast to Base2*, like
Base2 *b2 = (Base2*) ((Derived*)dPtr);
Thanks that's the answer I was looking for.
Re: Multiple Inheritance Problem
Quote:
To/From a "void *" done properly is 100% defined and your are 100% correct.
Though IMO it should ring a small alarm bell that something could probably be improved in your design. The only reason I've ever had to cast to an intermediate generic type was when passing pointers to data using Windows messages.
Re: Multiple Inheritance Problem
Quote:
Originally Posted by JohnW@Wessex
Though IMO it should ring a small alarm bell that something could probably be improved in your design.
Hey I nevr said that it was "good", only that it was well defined. I would most likely "slap someone upside the head [NCIS] unless there was an invariant external constraint.
Re: Multiple Inheritance Problem
Quote:
Originally Posted by JohnW@Wessex
The only reason I've ever had
When many "only reasons" are added the total reason can be quite substantial. :)
Re: Multiple Inheritance Problem
Quote:
Originally Posted by TheCPUWizard
Hey I nevr said that it was "good",
That was taken as read :thumb: