C++ gives you the power to do great things and multiple inheritance is one of the most powerful tools you’ll have. This doesn’t mean that you should always use it. Use it when it’s the right tool for the job just like any other tool. I’ll show you in this episode how to use multiple inheritance properly.
One of the biggest examples of why multiple inheritance is bad is the dreaded diamond. As long as you’re aware of this problem, it’s not really a problem anymore and you should be fine. That doesn’t mean all your designs should involve base classes that get included multiple times. I tend to have the best results when I avoid the diamond entirely. But there are times when it’s perfectly acceptable as long as you use virtual inheritance.
You also learn about a common application of multiple inheritance that can also lead to problems. This time the problem is just that you end up with a lot of classes. The point I make though is that a lot of classes might be exactly what you need. With multiple inheritance, you can decide. When all you have is single inheritance and some interfaces, then the language has already made the choice for you.
Listen to the full episode or read the full transcript below.
Imagine a carpenter telling you how he broke his leg once falling from the roof of a house when the hammer slipped that he was using to support his weight. Ever since then, he banned the use of hammers from his entire crew. The crew members now have to use wooden blocks to push nails into the boards so that they won’t be tempted to try using hammers to hang upside down.
Ridiculous, right? This is clearly a case of using the wrong tool. It wasn’t even an accident. It’s not like the carpenter hit his thumb while trying to nail something.
Multiple inheritance is often used wrong and because of that, some languages have banned multiple inheritance completely. Developers who only know C# or Java might even think that multiple inheritance is bad, something that should be purged from existence. Here, have these wooden blocks instead. They’re so much easier to use!
The strange thing is that these languages that pretend to ban multiple inheritance still allow you to implement as many interfaces as you want. This is really just multiple inheritance under a new name and with reduced functionality. We’ll talk about interfaces in an upcoming episode.
The example given most of the time for why multiple inheritance is bad is called the dreaded diamond. Let’s go back to the earlier example of a book class, a physicalBook class that inherits from book, and an audioBook class that also inherits from book. So far everything is good. What happens though when you want a new product that combines both the physicalBook and the audioBook? Well, inheritance has worked well for you so far, so you decide to create a new class called physicalAudioBook that inherits from both physicalBook and audioBook.
If you draw these four classes on paper, they resemble a diamond. Use a rectangle outline for each class and put the name of the class inside the rectangle. Draw the book class on the top. Then draw the physicalBook and AudioBook classes below the book class so that the three classes so far form the points of a triangle. Then draw a line from the physicalBook class to the book class and another line from the audioBook class to the book class. These lines show relationships between classes. There should be no line between the physicalBook class and the audioBook class because they share no direct relationship. Then draw the physicalAudioBook class below everything with two lines to the physicalBook and the audioBook. The shape should now look like a diamond.
The problem with this arrangement is that the book class is included twice in the new physicalAudioBook class. You can make a change so that you only have a single book class by going back to the physicalBook and audioBook classes and change their inheritance from public to public virtual. Virtual inheritance will make sure that only a single base class will be included among all the virtual inheritance instances of that class.
The only problem with this solution is that you have to apply the fix at the physicalBook and audioBook classes. These classes themselves have no direct need for virtual inheritance. It’s only when they’re put together with multiple inheritance in the physicalAudioBook class that they need to change how they derive from the book class. Even so, the solution really just requires that you’re aware of the problem and have the ability to modify the other classes. If you can do this, then even the dreaded diamond is nothing to be afraid of.
Another example of where multiple inheritance can cause problems is when using it to combine two or more lists of classes. Let’s look at the adventure game example of helmets again. We’ll simplify the classes involved to just a base class called armor and a couple derived classes called helmet and boots.
Now let’s add another set of classes that describe materials. We’ll have a base class called material and add a couple derived classes called leather and steel.
Now that we have these, we can use multiple inheritance to combine them. We can create a leatherHelmet class that derives from leather and from helmet. Or a steelHelmet class that derives from steel and from helmet. Or leatherBoots that derives from leather and from boots. Or steelBoots that derive from steel and from boots. What we end up with is a lot of classes. Is this bad? Maybe. But the important thing is that with C++, you get to decide if this is what you want. Other languages that ban multiple inheritance have already decided for you that this is bad.
The proposed alternate solution usually says to stick with just a single list of primary classes, say, armor, helmet, and boots. Then make the material a property of the classes so that instead of having a leatherHelmet class that’s both an instance of leather and of helmet, you have just a helmet class that’s made of leather. Is this better? Maybe. It really depends on your situation.
Let’s say that leather helmets don’t really exist. For some reason, in your program they just don’t go together. If you take the multiple inheritance approach, then this is easy to solve. Just don’t define a leatherHelmet class. But with the alternate approach, it’s a bit more complicated. That’s because you’ll probably have the base armor class deal with the material and it will work with the base material class. It won’t know or care what the exact material is. Now you have to add some extra complicated code to try to prevent other code from creating a helmet class and using leather for the material.
Another problem with the alternate solution is that you can no longer refer to all of your armor instances by common materials. Because the armor classes were chosen to be primary, that’s all you get. But with multiple inheritance, you can refer to a group of instances that are all made of leather even if some of them may not be armor. This is because the leather class is a full base class on equal level with the helmet base class.
I’ve almost always had the best results with multiple inheritance when using multiple base classes that either have no other base classes themselves or at least share no common base classes. In other words, you have two or more unrelated classes that your class derives from. You want to make sure that each base class makes sense to be a base class for all the reasons we’ve talked about so far for using single inheritance. Either because you want to make use of polymorphism or you want to enhance or extend a base class through method overriding. If each of the base classes makes sense, then why not use them all?