Multiple inheritance concepts

Multiple inheritance seems like such a powerful and useful tool for many situations. I understand the problems associated with the dreaded diamond and an easy solution is to always inherit with virtual base classes.

Meanwhile, I've also read this from "Thinking in C++":
http://www.mindview.net/Books/TICPP/ThinkingInCPP2e.html

Vol2Chap6 wrote:
The reason MI exists in C++ and not in other OOP languages is that C++ is a hybrid language and couldn’t enforce a single monolithic class hierarchy the way Smalltalk does. Instead, C++ allows many inheritance trees to be formed, so sometimes you may need to combine the interfaces from two or more trees into a new class. If no “diamonds” appear in your class hierarchy, MI is fairly simple (although identical function signatures in base classes must be resolved). If a diamond appears, then you must deal with the problems of duplicate subobjects by introducing virtual base classes. This not only adds confusion, but the underlying representation becomes more complex and less efficient.
Multiple inheritance has been called the “goto of the 90’s”.22 This seems appropriate because, like a goto, MI is best avoided in normal programming, but can occasionally be very useful. It’s a “minor” but more advanced feature of C++, designed to solve problems that arise in special situations. If you find yourself using it often, you may want to take a look at your reasoning. A good Occam’s Razor is to ask, “Must I upcast to all of the base classes?” If not,
your life will be easier if you embed instances of all the classes you don’t need to upcast to.


This quote tells me that MI is not appropriate in most situations where I would think to use it. In what situations is it designed for?

In the examples below, are there better techniques?
1
2
3
class Herbivore : Animal { void gather(); };
class Carnivore : Animal { void hunt(); };
class Omnivore : virtual public Herbivore, virtual public Carnivore {};


or
1
2
3
4
5
class Janitor { void Clean(); };
class Father { void Play(); };
class Mother { void Nurse(); };
class Steve : virtual public Janitor, virtual public Father {} steve;
class Cindy : virtual public Janitor, virtual public Mother {} cindy;


Edit: I was just thinking about the second case. Steve "IS A" janitor and "IS A" father. That's why I inherited from both. But perhaps I should have said: Steve "HAS A" job. A Janitor "IS A" Job. Steve "IS A" father. That would look like:

1
2
3
4
5
6
7
8
9
class Janitor : Job { void Clean(); };
class Father : { void Play(); };

class Steve : public Father 
{ 
  Job job; 
public:
  Steve() : job( Janitor() ) {}
} steve;


I guess the main difference is that I have to create methods manually to interface with Job now.
Last edited on
Usually you should only use MI when dealing with interface classes (aka pure virtual classes). Sometimes MI can be used with bormal classes, but you should try hard to avoid "diamond inheritance" — it will lead to all sort of problems. But even this shouldn't be the absolute rule: i.e. in standart library iostream class inherits from istream and ostream, each of whom inherits from stream.
Typically you should only use MI when dealing with User Interface classes.

Both of your examples are bad because lets say you add a method void eat(); to the parents. How do you decide which way it gets implemented in the children etc?

MI is usually the sign of a bad design that should be rethought.
> Steve "IS A" father.
Steve `behaves' as a father to Johnny, but as a son to Mary.
A DNA test (followed by divorce) dismiss him from the first relationship.

Devastated, he makes a trip around the world.
Sadly, gets a parasite that produces hormonal changes, causing rejuvenation and sex change.

Now she's called Jane and in the third month of pregnancy. ¡Congratulations!
I'd say that "IS A" and "HAS A" are way too restrictive.

When I say "Janitor Willy". Does it mean the job Willy?

Your example specifys traits.

You cannot say that Willy "HAS A" janitor, but you can say Willy "HAS A JOB" janitor.
A cow has the trait being a herbivore.
etc.

Usually with breaking down a class in smaller part you can avoid the multiple inheritance. What i'd recommend
> This quote tells me that MI is not appropriate in most situations where I would think to use it.
> In what situations is it designed for?

http://www.parashift.com/c++-faq/mi-not-evil.html

And the other FAQs in section 25.
Thanks guys. I'm still trying to wrap my head around OOP.

ne555, you made my morning.

Edit: I've realized that you've actually destroyed my thoughts that OOP makes things easier. If I seriously wanted to support all of those statements, I'd be in a tangled mess.
Last edited on
Topic archived. No new replies allowed.