Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple inheritance and polymorphism questions

Consider this C++ code:

#include <iostream>
using namespace std;

struct B {
    virtual int f() { return 1; }
    int g() { return 2; }
};
struct D1 : public B { // (*)
    int g() { return 3; }
};
struct D2 : public B { // (*)
    virtual int f() { return 4; }
};
struct M : public D1, public D2 {
    int g() { return 5; }
};

int main() {
    M m;
    D1* d1 = &m;
    cout << d1->f()
         << static_cast<D2&>(m).g()
         << static_cast<B*>(d1)->g()
         << m.g();
}

It prints 1225. If we make virtual inheritance, i.e. add virtual before public in lines marked with (*), it prints 4225.

  1. Can you explain why 1 changes to 4?
  2. Can you explain meaning of static_cast<D2&>(m) and static_cast<B*>(d1)?
  3. How you are you not getting lost in this kind of combinations? Are you drawing something?
  4. Is it common to spot such complex settings in normal projects?
like image 830
Adam Stelmaszczyk Avatar asked Nov 11 '12 17:11

Adam Stelmaszczyk


People also ask

Can we use polymorphism in multiple inheritance?

Polymorphism can be carried out through inheritance, with subclasses making use of base class methods or overriding them.

Is polymorphism and multiple inheritance are same?

Inheritance is one in which a new class is created (derived class) that inherits the features from the already existing class(Base class). Whereas polymorphism is that which can be defined in multiple forms.

Can polymorphism work without inheritance?

Real polymorphism in General can not be acheived without inheritance. Languages that are not object-oriented provide forms of polymorphism which do not rely on inheritance (i.e parametric polymorphism). Some of this is possible in Java through generics.

What is the connection between inheritance and polymorphism?

Inheritance is a property pertaining to just classes whereas, polymorphism extends itself into any method and/or function. Inheritance allows the derived class to use all the functions and variables declared in the base class without explicitly defining them again.


2 Answers

Pictures speak louder than words, so before the answers...


Class M hierarchy WITHOUT virtual base inheritance of B for D1 and D2:

    M
   / \
  D1 D2
  |   |
  B   B

Class M hierarchy WITH virtual base inheritance of B for D1 and D2:

    M
   / \
  D1 D2
   \ /
    B

  1. Cross-Delegation, or as I like to call it, sibling-polymorphism with a twist. The virtual base inheritance will fix up the B::f() override to be D2:f(). Hopefully the picture helps explain this when you consider where the virtual functions are implemented, and what they override as a result of the inheritance chains.

  2. the static_cast operator usage in this case drives conversion from derived-to-base class types.

  3. Lots of experience reading really bad code and knowing how the underpinnings of the language 'work'

  4. Thankfully no. It is not common. The original iostream libraries would have given you nightmares, though, if this is at-all confusing.

like image 137
WhozCraig Avatar answered Sep 19 '22 14:09

WhozCraig


Can you explain why 1 changes to 4?

Why does it change to 4? Because of cross-delegation.

Here's the inheritance graph before virtual inheritance:

B   B
|   |
D1  D2
 \ /
  M

d1 is a D1, so it has no idea that D2 even exists, and its parent (B) has no idea that D2 exists. The only possible result is that B::f() is called.

After virtual inheritance is added, the base classes are merged together.

  B
 / \
D1  D2
 \ /
  M

Here, when you ask d1 for f(), it looks to its parent. Now, they share the same B, so B's f() will be overridden by D2::f() and you get 4.

Yes, this is weird because it means that D1 has managed to call a function from D2, which is knows nothing about. This is one of the more odd parts of C++ and it is generally avoided.


Can you explain meaning of static_cast(m) and static_cast(d1)?

What don't you understand? They cast m and d1 to D2& and B* respectively.


How you are you not getting lost in this kind of combinations? Are you drawing something?

Not in this case. It's complicated, but small enough to keep in your head. I've drawn the graph in the above example to make things as clear as possible.


Is it common to spot such complex settings in normal projects?

No. Everyone knows to avoid the dreaded diamond pattern of inheritance because it's simply too complicated, and there's usually a simpler way to do whatever you want to do.

In general, it's better to prefer composition over inheritance.

like image 23
Peter Alexander Avatar answered Sep 21 '22 14:09

Peter Alexander