How do you implement multiple inheritance polymorphic behavior?

I have never used multiple inheritance, but, reading about it recently, I began to think about how I can use it practically in my code. When I usually use polymorphism, I usually use it, creating new derived instances declared as base class pointers, such as

BaseClass* pObject = new DerivedClass(); 

to get the correct polymorphic behavior when calling virtual functions in a derived class. Thus, I can have collections of different polymorphic types that control their behavior through their virtual functions.

When considering the use of multiple inheritance, I thought about the same approach, but how would I do it 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* , then I lose the inheritance hierarchy polymorphism of class C and D. Similarly, if I declare it as C* , then I lose the polymorphism of class A and B. If I declare it as E* , then I don’t I can get polymorphic behavior like I usually do, because objects are not accessed using pointers of the base class.

So my question is, what is the solution to this? Does C ++ provide a mechanism that can work around these problems, or do pointer types need to be thrown back and forth between base classes? Undoubtedly, this is rather cumbersome, as I could not directly do the following

  A* myA = new E(); C* myC = dynamic_cast<C*>(myA); 

because the cast will return a NULL pointer.

+4
source share
4 answers

With multiple inheritance, you have one object that you can view in any of several 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 wood_door object, we can pass it to a function that expects to work with (a link or pointer to) a door object or a function that expects to work (again, with a pointer or link to) a wood .

Of course, multiple inheritance will not produce functions that work with door with any new opportunity to work with wood (or vice versa), but we do not expect this. We expect that we will be able to process our wooden door as a door, than to open and close it, or as a piece of wood that can burn or warp - and this is exactly what we get.

+7
source

In this case, classes A and C are interfaces, and E implements two interfaces. (Typically, you would not have intermediate classes C and D in this case.) There are several ways to deal with this.

The most common is probably the definition of a new interface, which is the sum of A and C :

 class AandC : public A, public C {}; 

and have E Then you usually control E through AandC* , passing it evenly to functions that take A* or C* . Functions that need both interfaces in the same object will be with AandC* .

If the interfaces A and C are somehow connected, say, C offers additional tools that some A (but not all) may want support, then for A might make sense to have a getB() function that returns C* (or a null pointer if object does not support interface C ).

Finally, if you have mixins and several interfaces, the cleanest solution is to support two independent hierarchies, one for the interfaces and the other with 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 am tempted to say that from a design point of view, the inheritance of an interface should always be virtual in order to allow such a thing into the future, even if it is not needed now. In practice, however, it seems rather rare that there is no way to predict this in advance using.)

If you need more information about this, you might want to read Barton and Nakman. Now the book is quite dated (it describes pre C ++ 98), but most of the information remains valid.

+3
source

This should work

  A* myA = new E(); E* myC = dynamic_cast<E*>(myA); myC->Foo2(); 
+1
source

C cannot be used for A because it is not A ; it can only drop D or E

Using A* , you can make E* , and through this you can always explicitly say things like C::foo() , but yes, there is no way for A implicitly call functions in C that can have overrides or cannot.

In such strange cases, templates are often a good solution, because they can allow classes to act as if they had common inheritance, even if they do not. For example, you can write a template that works with everything that foo2() can have on it.

0
source

All Articles