This article describes logical aspects of multiple inheritance and should clear your doubts about whether multiple inheritance (especially in C++) can be logically consistent.
Multiple inheritance, another buzzword in object-oriented programming, is currently treated as a “dire” feature. One meaning of it is that it shouldn’t be supported (and this is what Smalltalk, Java, C# or Vala do), the other is that it’s “not for kids, but for professionals”, so it’s supported together with conflict resolution, which leads to stupid and unmaintainable code (Lisp, Eiffel, Python), and effectively to ban multiple inheritance. I would add C++ to this last list, however it at least does not do this stupid conflict resolution and additionally features virtual inheritance that allows to prevent “diamonds”. None of the languages does the right thing, though, that is, strictly disallow diamonds and implicit common base method overrides.
The main problem isn’t the multiple inheritance. The main problem is that people seem not to understand the OO inheritance itself.
1. What is inheritance?
Yes, well, let’s start from the beginning because there’s too much people who have their own idea what inheritance is and, moreover, what inheritance is not.
I should first define, what class is, of course, but let’s not get to such a low level. As everyone, who is familiar with OO, knows, class is an entity with statements, which defines what a potential object of this class should do and which way. Having that, inheritance is a relationship between two classes, which means that the “source” class (called “base class” or “superclass”) plays a partial role in the “destination” class (called “derived class” or “subclass”).
Please pay attention that this relationship, despite being similar in result, is realized different way in dynamic languages (like Smalltalk) and in static languages (like C++). Let’s say, universally, we have a class A and we create class B that inherits A, then finally we make an object O of class B.
In Smalltalk this relationship means a kind of “fallback delegation”: whenever we call method M for object O, and the B class does not define M, the A class is tried to do M. And so on towards the bases, until Object class is reached, and if M is still not found, it ends up with exception.
In C++ inheritance really means derivation – if we define B this way, then all methods of A are meant that they also are members of B (every method identified as A::M is also identifiable as B::M). There is no such thing as “fallback” – all methods of A are incorporated into B, and when there is a method M called on object O, the method is “searched” in the B class only and exclusively.
In Smalltalk then, as the only type in this language is a reference to object, you can just “try” to call some method you think this object handles. The class of which this object is is stated to provide some features, so when you define your own class, you usually would like to provide another implementation for what the object’s class has already provided. The main treat then seems to be the possibility to define my own methods with the same name as those defined in base class and this way change the behavior of some method from the base class, which was calling this method in its body. That’s probably why the fact that B inherits A is called “subclassing” in this language.
The perspective of C++, despite that is exactly the same in effect, makes different impression. In this language the main treat of inheritance seems to be “extending” classes, that is, making classes import contents of another class and take it as their own. Or, in other words, add new things towards the base class. That’s why probably it’s usually called “derivation”.
However it’s maybe not explicitly visible that both these things result in the same, that is, both aspects are important: “subclassing”, when you partially change the behavior of the base class, and “extending” when you add new features towards the base class. Inheritance is both these things together. However in Smalltalk it’s not obvious that “extending” occurs, especially that in Smalltalk, as it’s a dynamic language, it’s hard to extract partial contents of the object, that is “subobjects”, which is normal in case of static languages. But anyway, in any of these languages you can “override” methods from base classes, as well as add new features to a class.
Stating now that Java is a Smalltalk descendant with added static types and C++-like syntax, it looks moreover stupid that the “extends” keyword is used to define a feature that by OO purists is called “subclassing” (in Smalltalk it is “subclass:”!). Moreover, the “implements” keyword would also be treated as subclassing (especially when an interface can be a type of reference, so it can be used for “classification” as well!). It was then much better that C# followed the way of C++ by not using any keyword, since any keyword there would look stupid.
OO purists (would say “mind purists”, who have their minds pure of common sense) usually say that extending is only a “side effect” and the most important thing is subclassing. They just don’t realize that there is also “extending”. The majority of people simply do not understand that inheritance in general is these two aspects.
Some people say, for example, that you should not derive, if you are not going to subclass. But why not? I want to have an object that will do things I need, and partially these things are fulfilled by some existing class. Why then, shouldn’t I derive?
Or, what should I do instead? Create another class from scratch (oh, maybe I should copy-paste the code from the class that partially does what I need)? Should I delegate, that is, place this “base class” as a field and define method forwarders? This will only result in the same thing and will cost me more effort. Or maybe I should create external functions with the code I meant to put into the new methods? That’s a good alternative solution and has some advantages (for example that multiple such solutions can coexist without conflicts), but also it can’t be used when such extending requires additional state particles in the object (implemented by fields).
In Smalltalk the problem is also that once you defined a class, its fields cannot be reused. In Smalltalk the matter is very simple – there is no syntax for exposing fields (even for derived class). In languages, where fields can be public and there is also a “protected” section, which makes parts of the class available for deriving class, it’s possible to create such an “extension” towards the base class that makes use of some fields from the base class. Well, I can understand that “protected” can be treated as a “wtf kind of encapsulation”, but I don’t understand why exposing fields has to be “non OO”. In particular, I don’t understand why exposing or hiding fields has to be different thing than exposing or hiding methods. Especially when “properties” is growing to a rank of feature, either “logically implemented and partially supported by some tools” (in Java) or even built into the language (C#, Vala, also Qt extensions to C++).
If you explain the things as if inheritance is only subclassing, and this usual inheritance is the only available inheritance in the language, then it’s very easy to explain, why multiple inheritance is “not true object oriented technique” – this will be covered later.
2. What is virtual inheritance?
The usual inheritance relies on the statement that the base class is a direct base of a class that declared this derivation, that is, if this class is derived “through” some other class intermediately, it is only its indirect base and rather not interesting part of the base class. The virtual inheritance (the only language I know providing this feature is C++) is a bit different – every virtually derived class that occurs in the class hierarchy will always become a kind-of direct base of every its deriving class (even indirectly) and also this class will become a “shared superclass” for all classes in this hierarchy. It’s something like only the derivation procedures undergo derivation, while the (virtual base) class itself is provided as a base class of each class separately, although as common for them.
For example: in the following hierarchy (== marks virtual inheritance):
V ==> A --> B
not the class V itself is intermediately derived by deriving A, but the delegation procedures for class V, and in consequence, class V becomes a direct virtual base of class B, too (and of any other class that derives, even indirectly, from A).
The subobject layout is also different. If V was a normal base class of A, then in an object of class B there is a subobject of class A, which also contains a subobject of class V. If V is virtual base, then subobject of class A (in an object of class B) still contains a subobject of class V, however it’s also a subobject of B. A subobject of class, which is a virtual base, becomes a common subobject of the derived class and the base class. However from the class B perspective, the A subobject does not contain V – class B just took it over as its own. Despite the fact that V is still a subobject of A, the fact that it’s a subobject of B overlaps this first fact. In other words, both object of class B and subobject of class A think that the subobject of class V is their direct subobject.
The practical consequence of this fact isn’t seen as long as we use only single inheritance.
3. What is multiple inheritance?
It’s theoretically simple – one class derives more than just one.
That is, we are extending many classes together: we use multiple other classes in unison to combine a base for another class. We use particular classes as contents so that the object of our class will contain multiple subobjects belonging to multiple classes.
But wait! This is quite OK as long as we’re talking about extending. Moreover, as long as we are talking about independent extending (methods of derived class do not interfere with methods of base class, which means, among others, that methods of derived class do not mess with fields of base class). What about subclassing?
Of course, we still have problems like naming conflicts, but as long as it concerns sibling classes, it’s very simple to be solved. If the name of called method can’t be unanimously resolved as one of base class’s method, the compiler will complain, and this way you should either refer to method specifying from which class, or – better – create a method in the derived class that will redirect to this one that you meant, or even “rename” this method (that is, provide method forwarders with different names).
Multiple subclassing, on the other hand, is something that simply doesn’t fit in object-oriented programming. There’s simply no such thing. It’s not possible to define it logically.
In particular, what does it mean that a class named B “subclasses” a class named A? It’s simple: a class named B specializes or precises the term defined by class A. But what if we are defining class C that “subclasses” A and B simultaneously? If “subclassing” is defined as making “more specific”, “narrower range”, “specialization” – you can quickly come to a simple conclusion that this may concern only and exclusively one class. The only possibility to have one class that effectively subclasses two others is to have something like a “glued class”, that is, a composite of several classes, where each component subclasses only one class. And of course, none of these “components” have a thing to do with each other.
If we look at it from Smalltalk perspective, it’s not possible to subclass multiply by two reasons. The most characteristic reason for Smalltalk is that in this language the base class is used as a “fallback” when the method is not defined in the object’s class. Where to “fallback” then, if the class doesn’t define the method? The first one? Each base one by one? Find the first class (in the derivation order) that “understands” it and throw an exception if none does?
Of course, this problem is soluble – technically of course, not logically. For example, Python solves it such a way that it just tries to resolve the method by searching the base class, then starts to search the next base sibling if the previous search failed. In particular, it does not even regard naming conflicts – if there are two methods of the same name in both base classes, the first one takes precedence anyway. It means that derivation order also defines priority, or significance – a method from the more significant class overrides the method from the less significant one.
But being soluble doesn’t mean that this is a good reason to use this solution. The problem isn’t that this is some hack or something. The problem is that this solves the technical problem of searching for a method by name, but does not solve the problem of impossible multiple subclassing in the OO theory. Of course, this conflict problem relies on method names, not on provided features (so it would be said that this shouldn’t have anything to do with the OO theory). The problem, though, is in the impossible subclassing in case when base classes form a diamond. Providing technical solution for this problem leads to accepting a logically wrong code.
In the light of this, there’s no wonder why even the official documentation of Python strongly discourages using multiple inheritance.
If you are lemming, you can now quickly realize that the multiple inheritance is bad and shouldn’t be used. If you are engineer, you know that the real world will quickly surprise you.
4. Classes in the real world
Let’s reconsider then, whether multiple subclassing is something that is acceptable in the real world. Let’s reconsider first, why there is something called class hierarchy.
Class hierarchy (classification) is something that came from – well known from many other domains – “categorizing”. This functions like multiple boxes to which we can put many small things. Depending on the category we assign to particular thing, we put this thing to the particular box. As you can see, one thing cannot be in more than one box at a time. This is the main treat of categorizing: there can be only one category to qualify particular thing.
However this is a very strong (and very humane!) simplification in describing the world. This is because categories exist on some specified “level”, or there can be “categories of categories”, or whatever we can call it, in other words there are “category types”. When we talk about these boxes, to which we put things, these boxes describe categories of only one type. While there can be multiple types of categories and each object can be categorized from multiple perspectives. This is normal thing because an object cannot be just one treat – usually objects combine many different treats in one. However every object has always only one treat of specified type (if we think some object has multiple treats of one type, it means that the category type is incorrectly defined).
It simply means that an object may belong to multiple categories, but each category must be of different category type. If we call the category “class”, so that we “classify” these objects, we may have something like “class types” that group classes, and this way an object may belong to multiple classes at a time. There is only one condition: every class, on whatever level of derivation, must appear in the hierarchy tree only once.
5. Amphibia, as a subclass of car and boat
I take this example because it’s the best known example of multiple inheritance and simultaneously an example that multiple inheritance is possible, correct, and does not require any hacks – it just works in the real world. Simultaneously it introduces a big confusion for object-oriented languages. Especially because it proves that languages that do not feature multiple inheritance are stupid.
The amphibia class is then a subclass of car (because it rides as a car on roads) and of boat (because it can swim as a boat). Why is it possible that one class can unify these things in one? Simply because boat and car do not have any common parts.
No? What about engine, steering wheel, controls? We’re still talking about an engine-powered boat, as this amphibia can be powered by its engine also when swimming.
Pay attention that boat and car are totally different things. A car can ride on roads, but it’s not possible that it ride on deep water. Also a boat is “riding” only on water, while it cannot ride on roads. Even though we’d say that they both move, their definitions of movement do not interfere with each other. They are just moving in separate environments, so they are different “types of moving”. Moreover, now depending on this environment our amphibia behaves only like a boat or only like a car at a time. It is, then, something like two different objects combined into one.
However, they have some common parts. Yes. But this common part can cover different functionalities (not dependent on the fact that this machine is running on water or land), which cooperate with the additional treats. So this one part is common (so in OO it should undergo virtual inheritance) and the other parts provides separate features.
Well, so engine, steering wheel, or controls, are just parts of it. This is just another class category. How does it work then?
Very simply. Usually if you define some class, which is deriving from another class, you need to define a level of significance, that is, whether we are limiting the use of object (which is said to be the main thing that inheritance does, although it’s not true), or we are extending the use of object, towards some base class.
Referring to this example, if we define EnginePowered class, we define something that can be powered by engine in general. We can have also two classes, Car and Boat. These two classes don’t have any common parts – they are quite separate. Now let’s consider that we create new classes – EnginePoweredCar and EnginePoweredBoat (please note that Car can also be a part of train and does not require to be powered independently). By defining such classes we bind two different treats in one, that is, treats belonging to different categories: EnginePowered is a class of totally different category than Car or Boat. Moreover, EnginePowered can be treated as a technical detail that may be used for something else, while Car or Boat is the main treat. So, EnginePoweredBoat should be defined as a class that derives Boat and virtually derives EnginePowered.
This way, if you now define Amphibia as a class that derives EnginePoweredCar and EnginePoweredBoat, it will be composed (practically) from Car, Boat, and – virtually – EnginePowered.
But how does it work that despite that EnginePoweredCar and EnginePoweredBoat override the same functions from EnginePowered and do not “conflict”? It’s because they are linked special way – it’s not much important whether they are automatically switching between road mode and water mode, or whether both are operated simultaneously. The main reason why this is possible is that it’s possible to define what engine and steering wheel and all other controls operate with details of Car and Boat. This is because the control elements of car (wheels) and control elements of boat (rudder) do not conflict with each other.
6. Virtual inheritance as a solution for multiple inheritance
So, again, in which case the virtual inheritance should be used and when it is reasonable to use multiple inheritance?
Let’s imagine that we have some class A that defines whatever things can belong to it as a category – there is a large variety of objects that can be classified as A. Now you define B that derives A but limits it to some narrower range, so much less objects belong to B. You also define C and D. Please note that B, C and D are sibling classes, and as such, they should belong to the same class category. In which case then, in similar situation, should A be derived virtually?
In such a case, when defining B makes the range enough narrow that it is not physically possible that there can be any object that would belong to C or D (that is, to any imaginable other classes that would become siblings to B). In other words, beside the fact that these classes subclass A, they do not have any least common treats.
It’s very simple. As I said, in virtual inheritance the subobject of virtual base is common to everyone in the hierarchy from this class down. It should then express the exceptional common treat of all its subclasses, and there are no other common treats.
An example of virtual inheritance may be also a mule. As mule is a mix of horse and donkey, you may say that horse is animal and donkey is animal. However mule isn’t animal just because both donkey and horse are animals. Mule is animal because it just IS animal, independently on the fact that horse and donkey are both animals, too. It’s just yet another case (subclass) of animal, despite the fact of coming from horse and donkey.
I express this because there can be classes that derive from some other class and may implicitly have some common treats. In this case there’s no point in deriving virtually their base class, as these classes cannot be separated anyway.
I even tend to think that the virtual inheritance isn’t a required feature to make it possible to have a well working multiple inheritance – it would be enough that the “diamond” forms are disallowed, but you can derive from the “conflicting” class in the class, in which you derived these two classes. That is, if you have A deriving from V and B deriving from V, and C that derives from A and B, you should derive it also from V to make this code accepted. Such a solution would do the same as virtual inheritance. The virtual inheritance is needed in C++ mainly because this keyword changes the object layout to a bit less efficient, but the only possible to implement a shared subobject. But this solution is able to be implemented in languages like Python.
Diamonds do not exist.
Ok, let’s start from a different place. Centaurs do not exist.
Why do not centaurs exist, or simpler, why cannot they exist? Because they are composed of a horse that was cut off its head and a man that was cut off his… well, say, loins. You cannot compose objects (or their classes) by taking a damaged part of the one and a damaged part of the other, this way composing something that cannot even co-function. For example: where should this creature have lounges and stomach, or even more funny, how could the backbone of such a creature be constructed? Try to find a centaur’s skeleton drawings on the network and you’ll see that the backbone is bent in the place where human body meets the horse body – a very strong bending, which cannot be withstood by an average man’s backbone and not break. Such a creature does not exist, but not because the nature did not happen to evolve anything to it (there would be no such problems with Shokans), but because it is not possible to define such a creature – neither technically, nor logically.
Centaur is an example of combining two classes that have common parts, or IOW, two classes that partially play the same roles. We can extract parts of horse and man on the lowest level, as mammals. But then, there are more specific treats that make them two different classes. However they are still not so different that the common parts can be disappeared or extracted as a “virtual base”. In case of man and horse, the “mammal” is the virtual base, however next both classes add new features of the same category (but working completely different way), so there are still many treats that are in conflicts with each other.
So, if we have two classes that manage different sorts of terms (riding in case of car and swimming in case of boat), we can safely create a class that derives from them both, because these classes cover different features. We can’t do the same with horse and man because these classes both cover features of the same categories, they only do it different way.
There is also similar problem with widgets. Note that despite that C++ is such an old language now and there is a lot of GUI libraries, and moreover, some of them are now being created with regard of standard C++ (!), none of them allows for multiple inheritance for widget classes, in particular, a new widget class cannot derive from more than one widget class (but of course, there’s no problem in having a class that derives, say, a widget and a stream). It could be theoretically possible that the Widget class itself be virtually inherited in every basic widget, so you can compose widgets.
That’s not true. It’s different in different libraries, but it’s very well seen in Qt. In this library, there is a virtual method in QWidget class, called ‘paintEvent’. You just override this method and then inside it “do whatever you want to make this widget look as you wish”. What would you then do in your ‘paintEvent’ method in a class that derives from two widget classes, while in both base widget classes you have ‘paintEvent’ methods that do totally different things? If you just allow them to paint (that is, you call them both one after another), they’ll collide (the second one will scratch the result of the first one). If you want that they paint at different coordinates you should either fake the widget different sizes during repainting (which is really a “tuff” hack!), or you can fake some margins around so that you can move the fake widget’s sizes and position to force underlying paint-s to draw at correct coordinates. Moreover, you’ll have to regard these faked coordinates in many other methods, like those of sizing, also you need to “route” the mouse clicks to appropriate underlying class, etc. etc. – the widget class just isn’t predicted to manage “glued two widgets”. While it’s very simple how to create a composite widget: just use a container widget that can lay out the widget parts on itself. This is what you should do when you want that one subwidget stand beside the other. Your class may at most derive from the layout widget (in particular, this is a solution for Gtk; in Qt you should derive from QWidget and use QBoxLayout for organizing the layout).
But no matter what a combination of two (or more) widgets you’d imagine, there’s no possible combination that would excuse using multiple inheritance to implement it. You’d say it should be possible to combine Label and Button, this way creating button with label. No, that’s wrong – this is just another combination of widgets; in Gtk the GtkButton class is a container widget, so you can put anything inside it; similarly this should be done in other libraries. Whatever you’d imagine as a combination of widgets, it will always result in the same: you should at most derive from just ONE widget class and if you need any subwidgets as components, you just manage them as independent ingredients.
Remember: when you have two classes that even partially play the same role, you just can’t derive from them both simultaneously. If this “role” is defined by the base class, this class is never derived virtually in case when the extension of the derived class is tightly bound to the implementation of this role. So this would form a diamond.
Do not look for diamonds. Diamonds do not exist.
8. The correct multiple inheritance
From the technical point of view, the correct multiple inheritance is when you adhere to the rules of the language. From the logical point of view, you can use multiple inheritance, but there are also some logical rules. I’ll show now, how the multiple inheritance should be used in a language that features multiple inheritance with virtual inheritance on request (although the only such language I know is C++).
If your hierarchy of objects is not polymorphic at all, there’s no problem with having multiple inheritance (case #0). In this case there is no, even potential, subclassing:
If you inherit multiple classes, but only one of them is polymorphic, there’s still no multiple subclassing. It’s then multiple inheritance, but only single subclassing (case #1). In this picture, * marks the polymorphic class:
If you inherit multiple polymorphic classes, only two cases are allowed for object-oriented programming:
- These two inherited classes come from totally different hierarchies and they do not have any common polymorphic classes in the inheritance tree (case #2). This is a bit spoofing of the OO theory (the resulting class is rather a kind of “class composite” than a class), but at least it does not form a “diamond”:
- Both classes are derived from the same abstract class, but this abstract class is virtually inherited by both of them (case #3). In the below picture, double line shows the virtual inheritance. Virtual inheritance means that every virtual base will always become a direct superclass of any class that derives it indirectly. Please note that in this case you have partially “separate features” derived, plus common feature of both classes, but derived individually and separately:
In other words: there is only one case of multiple inheritance which is a disallowed multiple subclassing: when two classes derived by some class are subclasses (even indirect) of some other class (case #4). As you can see, this is the only case when you can see the well known “diamond”, which causes such a confusion in OO theory:
Please note that in a language, in which every user defined type that may be fully derived is class, and every class has implicit root base class (for example Java, C#, Smalltalk and practically a vast majority of OO languages), particular cases of multiple inheritance are impossible or limited:
- case #0 and case #1: there is no such thing as “non polymorphic class” (in C# you can derive between structs, but there is no derivation between structs and classes)
- case #2: It’s limited, in such a sense that only one of such polymorphic bases can be class; others must be interfaces (it results from a simple rule that there is a root class for all classes, so every case of two or more classes being derived will always form case #4)
- case #3: It would be possible in such languages, if they supported virtual inheritance (they usually don’t; only some Smalltalk dialects support delegation as a simple replacement for multiple inheritance)
Virtual inheritance could be a solution for languages that do not feature multiple inheritance, however it must be allowed that the root object class be a virtual base (not always a direct base); otherwise any double inheritance will form a diamond. Such a solution is possible in languages like Java or C# (possibly also in Vala – there should be no obstacles for this in GObject system).
Note that virtual inheritance means that no matter how all base classes have implemented some methods from this virtually derived class, your class must implement them anew. Although unfortunately C++ still allows that if only one of derived classes (e.g. BaseB1 in the above picture) reimplemented a method from the base class (BaseAn) then the derived class (X) will take it as its own. The conflict may be only in case when at least two of derived (or intermediately derived) classes have overridden some methods from BaseAn (for example, both BaseA1 and B). This is unfortunate and the compiler should issue a warning about this because such a behavior is rarely expected and usually unobvious. But usually what you expect is that BaseAn is also a direct base of X, so X should reimplement all methods from BaseAn. Even stating that it can call implementations of these methods in its base classes. Because this is what you usually expect when you derive multiply and have some common base class.
Languages that allow for multiple inheritance, but do not feature virtual inheritance (nor the forced class rebasing, as I have mentioned), practically support the worst coding practices because instead of making diamonds disallowed, or at least making reimplementation conflicts disallowed (as C++ does), they try to solve the technical problem, that is, to accept a counter-logical code. To use multiple inheritance correctly in these languages, you should always manually find the common base class and override all overridable methods in this class, even as simple redirection to some other method, because otherwise your class’s behavior will quickly go out of control. In particular, you should not allow that the automatic conflict resolver resolve your method call, but you should rather manually define which method should be called. Otherwise your class will form a “Frankenstein”, which partially behaves as one of classes that you have derived.
9. Lessons learned
Of course, a usual programmer has no influence on that the language they have to use to make software have some features implemented stupid way. But at least they are able to keep some rules that will, simply, rely on just not doing stupid things even if a language allows for it.
If you heard that multiple inheritance is bad because it breaks the OO theory, and this theory has been created by observing the real world, you can always respond that amphibia is a case of virtual inheritance existing in the real world, so it’s maybe this theory what is broken. On the other hand, don’t treat multiple inheritance as a method to combine everything into one for any purpose. You should research it logically – whether it should be derivation, but maybe containment, wrapping, composition, proxies etc. And first of all, keep the rules that define what kinds of inheritance are allowed.
Multiple inheritance can be considered nice – as long as you don’t look for diamonds.