Subclass casting, and pointer address changes
I've been using multiple inheritance in c++ for quite a long time, but only realised today that this could imply that the pointer addresses could be different when referencing them as one of the subclasses.
For example, if I have:
class ClassA{
public:
int x;
int y;
ClassA(){
cout << "ClassA : " << (unsigned int)this << endl;
}
};
class ClassC{
public:
int cc;
int xx;
ClassC(){
cout << "ClassC : " << (unsigned int)this << endl;
}
};
class ClassB : public ClassC, public ClassA{
public:
int z;
int v;
ClassB(){
cout << "ClassB : " << (unsigned int)this << endl;
}
};
int main(){
ClassB * b = new ClassB();
}
class A and class C have different addresses when printed on the constructor.
Yet, when I try to cast them back to each other, it just works automagically:
ClassA * the_a = (ClassA*)b;
cout << "The A, casted : " << (unsigned int)the_a << endl;
ClassB * the_b = (ClassB*)the_a;
cout << "The B, casted back : " << (unsigned int)the_b << endl;
I suppose this kind of information can be derived by the compiler from the code, but is it safe to assume that this works on all compilers?
Additional Question : is it possible to force the order in which the subclass locations go? For example, if I need classA to be located first (essentially, share the same pointer location) as ClassC which subclasses it, do I 开发者_如何学Pythonjust need to put it first in the declaration of subclasses? Update Okay, looks like it's not possible to force the order. Is it still possible to find out the "root" address of the structure, the start of the address allocated to the subclass, at the superclass level? For example, getting classB's address from ClassA.
That's a perfectly standard use of multiple inheritance, and it will work on any compliant compiler. You should however note that an explicit cast in unnecessary in the first case, and that the 'C style cast' could be replaced by a static_cast
in the second.
Is it possible to force the order in which the subclass locations go.
No : the layout is implementation defined, and your code should not depend on this ordering.
Yes, you can ssume that this is safe. A pointer typecast in C++ is guaranteed to correctly adjust the pointer to account for base-to-derived or vice-versa conversions.
That said, you have to be careful not to push the system too far. For example, the compiler won't get this conversion right:
ClassB* b = new ClassB;
ClassC* c = (ClassC*)(void*)b;
This breaks because the cast from C to A get funneled through a void*, and so the information about where the pointer is inside the B object is lost.
Another case where a straight cast won't work is with virtual inheritance. If you want to cast from a derived class to a virtual base or vice-versa, I believe you have to use the dynamic_cast operator to ensure that the cast succeeds.
Yea, a naive implementation for virtual bases is to place at a known location in the derived object a pointer to the virtual base subobject.
If you have multiple virtual bases that's a bit expensive. A better representation (patented by Microsoft I think) uses self-relative offsets. Since these are invariant for each subobject (that is, they don't depend on the object address), they can be stored once in static memory, and all you need is a single pointer to them in each subobject.
Without some such data structure, cross casts would not work. You can cross cast from a virtual base A to a virtual base B inside the virtual base A even though B subobject isn't visible (provided only the two bases have a shared virtual base X if I recall correctly). That's pretty hairy navigation when you think about it: the navigation goes via the shape descriptor of the most derived class (which can see both A and B).
What's even more hairy is that the structure so represented is required "by law of the Standard" to change dynamically during construction and destruction, which is why construction and destruction of complex objects is slow. Once they're made, however, method calls, even cross calls, are quite fast.
精彩评论