Reputation: 21351
I have never used multiple inheritance but while reading about it recently I started to think about how I could use it practically within my code. When I use polymorphism normally I usually use it by creating new derived instances declared as base class pointers such as
BaseClass* pObject = new DerivedClass();
so that I get the correct polymorphic behaviour when calling virtual functions on the derived class. In this way I can have collections of different polymorphic types that manage themselves with regards to behaviour through their virtual functions.
When considering using multiple inheritance, I was thinking about the same approach but how would I do this if I had the following hierarchy
class A {
virtual void foo() = 0;
};
class B : public A {
virtual void foo() {
// implementation
}
};
class C {
virtual void foo2() = 0;
};
class D : public C {
virtual void foo2() {
// implementation
}
};
class E : public C, public B {
virtual void foo() {
// implementation
}
virtual void foo2() {
// implementation
}
};
with this hierarchy, I could create a new instance of class E as
A* myObject = new E();
or
C* myObject = new E();
or
E* myObject = new E();
but if I declare it as a A*
then I will lose the polymorphism of the class C and D inheritance hierarchy. Similarly if I declare it as C*
then I lose the class A and B polymorphism. If I declare it as E*
then I cannot get the polymorphic behaviour in the way I usually do as the objects are not accessed through base class pointers.
So my question is what is the solution to this? Does C++ provide a mechanism that can get around these problems, or must the pointer types be cast back and forth between the base classes? Surely this is quite cumbersome as I could not directly do the following
A* myA = new E();
C* myC = dynamic_cast<C*>(myA);
because the cast would return a NULL pointer.
Upvotes: 5
Views: 1586
Reputation: 154007
In this case, classes A
and C
are interfaces, and E
implements two
interfaces. (Typically, you wouldn't have intermediate classes C
and
D
in such a case.) There are several ways of dealing with this.
The most frequent is probably to define a new interface, which is a sum
of A
and C
:
class AandC : public A, public C {};
and have E
derive from this. You'd then normally manage E
through a
AandC*
, passing it indifferently to functions taking an A*
or a
C*
. Functions that need both interfaces in the same object will deal
with AandC*
.
If the interfaces A
and C
are somehow related, say C
offers
additional facilities which some A
(but not all) might want to
support, then it might make sense for A
to have a getB()
function,
which returns the C*
(or a null pointer, if the object doesn't support
the C
interface).
Finally, if you have mixins and multiple interfaces, the cleanest solution is to maintain two independent hierarchies, one for the interfaces, and another with the implementation parts:
// Interface...
class AandC : public virtual A, public virtual C {};
class B : public virtual A
{
// implement A...
};
class D : public virtual C
{
// implement C...
};
class E : public AandC, private B, private D
{
// may not need any additional implementation!
};
(I'm tempted to say that from a design point of view, inheritance of interface should always be virtual, to allow this sort of thing in the future, even if it isn't needed now. In practice, however, it seems fairly rare to not be able to predict this sort of use in advance.)
If you want more information about this sort of thing, you might want to read Barton and Nackman. There book is fairly dated now (it describes pre C++98), but most of the information is still valid.
Upvotes: 3
Reputation: 490408
With multiple inheritance, you have a single object that you can view any of multiple different ways. Consider, for example:
class door {
virtual void open();
virtual void close();
};
class wood {
virtual void burn();
virtual void warp();
};
class wooden_door : public wood, public door {
void open() { /* ... */ }
void close() { /* ... */ }
void burn() { /* ... */ }
void warp() { /* ... */ }
};
Now, if we create a wooden_door object, we can pass it to a function that expects to work with (a reference or pointer to) a door object, or a function that expects to work with (again, a pointer or reference to) a wood
object.
It's certainly true that multiple inheritance will not suddenly give functions that work with door
s any new capability to work with wood
(or vice versa) -- but we don't really expect that. What we expect is to be able to treat our wooden door as either a door than can open and close, or as a piece of wood that can burn or warp -- and that's exactly what we get.
Upvotes: 8
Reputation: 5431
C
can't cast to A
because it isn't an A
; it can only cast down to D
or E
.
Using A*
you can make an E*
and through that you can always explicitly say things like C::foo()
but yes, there is no way for A
to implicitly call functions in C
that might have overrides or might not.
In weird cases like this, templates are often a good solution because they can allow classes to act as if they have common inheritance even if they don't. For instance, you might write a template that works with anything that can have foo2()
invoked on it.
Upvotes: 0
Reputation: 4663
This should work
A* myA = new E();
E* myC = dynamic_cast<E*>(myA);
myC->Foo2();
Upvotes: 1