Click to See Complete Forum and Search --> : Casting b/w Base Derived Class
usman999_1
December 2nd, 2002, 05:34 AM
Hi *!
Lets say i have a base class & derived classes like
class main {
public:
virtual void foo(){}
};
class derive1 { void foo(){}};
class derive2 : public main, public derive1 { void foo(){}};
Now if i do something like that
DWORD ret = (DWORD)static_cast<main*>((derive2*)1) - 1 ; // now ret = 0 O.K
ret = (DWORD)static_cast<derive1*>((derive2*)1) - 1 ; // now ret = 4 WHY????????? & HOW??????
What I dont understand is (it has to do something with virtual functions i think) But what exactly is it?????????
and secondly why am i getting 4 just by casting as far i understand casting is a hint that the var that is being casted to be treated of a different type then it is now after the cast but doenst have anything to do with the value.
Thanks for your time,
Regards,
Usman.
zdf
December 2nd, 2002, 09:15 AM
I believe your problem is the pointer arithmetic not the virtual functions. Consider the following example
#include <iostream>
using namespace std;
int main()
{
int* pi0 = (int*)0; // pointer to element 0 at address 0
int* pi1 = (int*)1; // does not point to element 1; points to the byte at addres 1
pi0++; // increments the pointer; not equivalent to (int*)1
if ( pi0 == pi1 )
cout << "never reached" << endl;
else
cout << "oops!" << endl;
return 0;
}
I hope I correctly understood your problem.
Regards,
usman999_1
December 2nd, 2002, 09:32 AM
Thanks for the reply. I dont think it is pointer arithmatic :) coz I am casting 1 that I think is constant so I dont think its possible to change that value & in your case you did pi0++; i.e a variable not a constant & what I posted in my post except of ret, there isn't any variable. Secondly If you take out the virtual keyword from the base class then the ret is equal to 1. Strange... :confused:
Regards,
Usman.
P.S: I dont understand much whats happening in that line of code, So dont know how to explain it better :( Sorry for that.
zdf
December 2nd, 2002, 10:04 AM
I do not understand. You expect ret to be zero in both cases?
TheCPUWizard
December 2nd, 2002, 10:23 AM
The problem is one of the root issues with why multiple inheritance is complicated (many say bad...)
A Derived2 object has multiple base addresses...
It has a Main1 object that resides at the starting address of the Derived2 object. If also has a Derived1 object that begins at:
Derived2(base) + sizeof(Main1)
Since main 1 has a vtbl, it has a size of 4 (for this compiler at least). If it had NO virtual functions, but had member variables, the diffeerence between your two results would be the net size of the member variables.
Worse consider the following code (which does not involve casts to DWORD)
Derived2 realObject
Main1 *m = &realObject;
Derive1 *d = &realObject;
if (m == d)
cout << "Same Object";
else
cout << "Different Object";
the above code WILL print out DIFFERENT OBJECT!!!!!!
See Item #43 in Scott Meyers "Effective C++" for in depth information.
omri
December 3rd, 2002, 01:46 AM
Hi,
I compiled assembly code of this example. Looks like is supports TheCPUWizard's reply.
// option 1
class A
{
virtual void func(){}
};
class B
{
};
class C : public A, public B
{
};
int main(int argc, char* argv[])
{
DWORD ret = (DWORD)(A*)(C*)1;
ret = (DWORD)(B*)((C*)1);
return ret;
}
// assebly
; 24 : DWORD ret = (DWORD)(A*)(C*)1;
mov DWORD PTR _ret$[ebp], 1
; 25 : ret = (DWORD)(B*)((C*)1);
mov eax, 1
test eax, eax
je SHORT $L17059
mov ecx, 4 // look at this line
add ecx, 1
mov DWORD PTR -8+[ebp], ecx
jmp SHORT $L17060
$L17059:
mov DWORD PTR -8+[ebp], 0
$L17060:
mov edx, DWORD PTR -8+[ebp]
mov DWORD PTR _ret$[ebp], edx
// option 2 - add member to class A
class A
{
virtual void func(){}
int a;
};
class B
{
};
class C : public A, public B
{
};
int main(int argc, char* argv[])
{
DWORD ret = (DWORD)(A*)(C*)1;
ret = (DWORD)(B*)((C*)1);
return ret;
}
// the assemly
; 25 : DWORD ret = (DWORD)(A*)(C*)1;
mov DWORD PTR _ret$[ebp], 1
; 26 : ret = (DWORD)(B*)((C*)1);
mov eax, 1
test eax, eax
je SHORT $L17060
mov ecx, 8 // look at this line
add ecx, 1
mov DWORD PTR -8+[ebp], ecx
jmp SHORT $L17061
$L17060:
mov DWORD PTR -8+[ebp], 0
$L17061:
mov edx, DWORD PTR -8+[ebp]
mov DWORD PTR _ret$[ebp], edx
regards
omri
zdf
December 3rd, 2002, 03:30 AM
Hi Usman,
As TheCPUWizard pointed out it is not possible to get the same value for ret. I do not see any practical use for the code you posted. So, is there any practical use for the code you posted or is just curiosity?
Regards,
usman999_1
December 3rd, 2002, 05:43 AM
Thank you all for your reply!
What i understand now correct me if I am wrong.
class main {
public:
virtual void foo(){}
}
};
class derive1 { void foo(){}
};
class derive2 : public main, public derive1 { void foo(){}};
now the Object for derive2 should looklike
Address 0 : main Obj Starts + vtable
Address 4 : derive 1 Starts // No var & no virtual function so I guess no vtable
Address 4 : derive 2 starts // Is correct that both starts at the same address??????? d1 & d2
So if i now cast derive2 pointer to main lets say it's value is 4. So it should be reduced by 4 to point to main.
I think i am missing something here. coz the answer is not that. Actually there's a macro in ATL for that sortof thing, I didnt understand why & whats happening when I saw that, thats why I posted it here.
Thanks for your time,
Regards,
Usman.
P.S: Sorry I am not good at assembly :(
TheCPUWizard
December 3rd, 2002, 06:10 AM
Close but not quite. Remember Derived2 CONTAINS Main and Derived1. Therefore
Address 0 : Derived2 and Main
Address n : Derived1 (n=4 because Main has Vtable and no data)
Address m: Derived2 Members (m=-n+sizeof(Derived1)) [0 in your case]
-------------------------
Please note that if you changed your class to:
Derived2 : public Derived1, public main
This would change to
Address 0: Derived2 and Derived1 and Main
This is because Derived1 has no size.
----------------------
Multiple inheritance causes many issues such as this. Even neglecting the "diamond" problem, multiple inheritance (except for interfaces) is usually more trouble than it is worth (consider using aggregation instead).
To see a workable method os supporting multiple inheritance look at the COM function QueryInterface. This is ONE method that is commonly used for casting between different base classes.
Hope this helps.
usman999_1
December 3rd, 2002, 06:28 AM
One more question :).
I think every derived class has a sub object/s of the super/base class/es & that sub object starts before the derived class object starts. So if I instentiate the derive2 class it will hav two sub-objects main & derive1 and the derived derive2 class object starts at the end of these sub objects. So at address 0, the main object should start and when that ends derive1 should start then the last class which is instaniated.
But in your reply d2 & m start at 0 should it be only main at 0 then after main derive1 (which is 0) so after that derive 2 at Address 5.
Thanks for your time.
Regards,
Usman.
I am not very much into COM & stuff, but just wanted to know bit more bout the C++ object Model.
TheCPUWizard
December 3rd, 2002, 06:48 AM
The reason "d2" STARTS at 0 (relative to everything else) is that the definition of "d2" CONTAINS "m" and "d1".
You are correct that unique data members added by "d2" start after the memory taken by "m" and "d1", but the COMPLETE "d2" object also consists of all of the members of "m" and "d1".
Hope this clears thing up regarding class layouts.
Regarding the order of calls. Technically the constructor of "d2" if the first thing (and only thing) called by the creator of the d2 object. Hidden from view (before the opening brace of the constructor) are calls to "m" and "d1". Therefore if you put breakpoints on the first line of the constructor of each class, it LOOKS like the order of call is "m", "d1", "d2". On the other hand if you put the breakpoint on the method definition itself (e.g. "d1::d1()") you will see the true order.
usman999_1
December 3rd, 2002, 06:52 AM
Thanks a lot! I got it :).
Regards,
Usman.
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.