|
-
May 2nd, 2008, 06:57 PM
#1
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?
Last edited by Rigel; May 2nd, 2008 at 08:56 PM.
-
May 2nd, 2008, 07:19 PM
#2
Re: Multiple Inheritance Problem
 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
-
May 2nd, 2008, 08:40 PM
#3
Re: Multiple Inheritance Problem
 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.
-
May 2nd, 2008, 10:04 PM
#4
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.
TheCPUWizard is a registered trademark, all rights reserved. (If this post was helpful, please RATE it!)
2008, 2009,2010
In theory, there is no difference between theory and practice; in practice there is.
* Join the fight, refuse to respond to posts that contain code outside of [code] ... [/code] tags. See here for instructions 
* How NOT to post a question here
* Of course you read this carefully before you posted
* Need homework help? Read this first
-
May 3rd, 2008, 04:39 AM
#5
Re: Multiple Inheritance Problem
 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
-
May 3rd, 2008, 08:47 AM
#6
Re: Multiple Inheritance Problem
 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:
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.
TheCPUWizard is a registered trademark, all rights reserved. (If this post was helpful, please RATE it!)
2008, 2009,2010
In theory, there is no difference between theory and practice; in practice there is.
* Join the fight, refuse to respond to posts that contain code outside of [code] ... [/code] tags. See here for instructions 
* How NOT to post a question here
* Of course you read this carefully before you posted
* Need homework help? Read this first
-
May 3rd, 2008, 09:38 AM
#7
Re: Multiple Inheritance Problem
 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. 
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
-
May 3rd, 2008, 01:48 PM
#8
Re: Multiple Inheritance Problem
 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);
Last edited by _uj; May 3rd, 2008 at 02:00 PM.
-
May 3rd, 2008, 02:13 PM
#9
Re: Multiple Inheritance Problem
 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...
TheCPUWizard is a registered trademark, all rights reserved. (If this post was helpful, please RATE it!)
2008, 2009,2010
In theory, there is no difference between theory and practice; in practice there is.
* Join the fight, refuse to respond to posts that contain code outside of [code] ... [/code] tags. See here for instructions 
* How NOT to post a question here
* Of course you read this carefully before you posted
* Need homework help? Read this first
-
May 3rd, 2008, 03:17 PM
#10
Re: Multiple Inheritance Problem
 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.
Last edited by _uj; May 4th, 2008 at 10:17 AM.
-
May 4th, 2008, 08:43 AM
#11
Re: Multiple Inheritance Problem
 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..
TheCPUWizard is a registered trademark, all rights reserved. (If this post was helpful, please RATE it!)
2008, 2009,2010
In theory, there is no difference between theory and practice; in practice there is.
* Join the fight, refuse to respond to posts that contain code outside of [code] ... [/code] tags. See here for instructions 
* How NOT to post a question here
* Of course you read this carefully before you posted
* Need homework help? Read this first
-
May 5th, 2008, 11:45 AM
#12
Re: Multiple Inheritance Problem
 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.
-
May 6th, 2008, 08:15 AM
#13
Re: Multiple Inheritance Problem
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.
-
May 6th, 2008, 07:20 PM
#14
Re: Multiple Inheritance Problem
 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.
TheCPUWizard is a registered trademark, all rights reserved. (If this post was helpful, please RATE it!)
2008, 2009,2010
In theory, there is no difference between theory and practice; in practice there is.
* Join the fight, refuse to respond to posts that contain code outside of [code] ... [/code] tags. See here for instructions 
* How NOT to post a question here
* Of course you read this carefully before you posted
* Need homework help? Read this first
-
May 7th, 2008, 12:37 AM
#15
Re: Multiple Inheritance Problem
 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.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|