How to align pointers when dealing with multiple-inheritance?
Say we have a concrete class A, and an abstract class B.
Consider a concrete C, that inherits from both A and B, and implements B:
class C : public A, public B
{
/* implementation of B and specific stuff that belongs to C */
};
Now I define a function which signature is void foo(B* b);
开发者_开发技巧
This is my code, I can assume that every pointers to B are both A and B. In foo's definition, how to get a pointer to A? A nasty but working trick is to align back pointers like so:
void foo(B* b)
{
A* a = reinterpret_cast<A*>(reinterpret_cast<char*>(b) - sizeof(A));
// now I can use all the stuff from A
}
Keep in mind that C does not have a super type and actually, there are many classes akin to C which only are A and B. Feel free to question both my logic and this sample of design as well but the question is only concerning pointers alignment.
void foo(B* b)
{
//A* a = reinterpret_cast<A*>(reinterpret_cast<char*>(b) - sizeof(A)); // undefined behaviour!!!!
A* a = dynamic_cast<A*>(b);
if (a)
{
// now I can use all the stuff from A
}
else
{
// that was something else, not descended from A
}
}
Forgot to say: in order to make work dynamic cast both A and B should have virtual function(s) or at least virtual destructors. Otherwise there is no legal way to do that type conversion.
Having a huge set of unrelated classes that both derive from A
and B
is a very strange design. If there's something that makes A
and B
always be "used together" you could either merge them or introduce a shim class that only derives from them and then only derive from that class:
class Shim : A, B {};
class DerivedX : Shim {};
and in the latter case you just use static_cast
to first downcast from A
or B
to Shim*
and then C++ it will implicitly convert the Shim*
pointer to the other class.
If you want to use the functionality of both class A and Class B in your function then you should modify the function to receive C pointers:
void foo(C* c);
And in general you are wrong with you assumption that "every B is an A as well". You could create classes derived from you B interface and not derived from Class A, that's why the compiler won't know that in your specific case "every B is an A".
Expanding on sharptooth's answer (and entering it as an answer, because I can't get formatted code into a comment), you can still use the shim:
class Shim : public virtual A, public virtual B {};
Then:
class Derived1 : public Shim, public virtual A, public virtual B1
{
};
class Derived2 : public Shim, public virtual A, public virtual B2
{
};
B1
and B2
must derive virtually from B
.
But I suspect that if you always need to implement both A
and
B
, you should create a single interface with both, either by
inheriting, or coalising both into a single class; your B1
and
B2
would inherit from that. (The solution with
dynamic_cast
, of course, is for the case where the derived
class of B
may or may not also derived from A
.)
精彩评论