Multiple Inheritance in C++ is a powerful yet intricate feature that allows a class to inherit properties and behaviors from more than one base class. In traditional single inheritance, a class can have only one immediate parent, but with multiple inheritance, a derived class can inherit from multiple base classes. This flexibility provides developers with the capability to create complex class hierarchies and share features from different sources. However, it also introduces challenges related to ambiguity resolution and the potential for the diamond problem.

Several companies have C++ compilers available in the marketplace, and many others are sure to follow. Because the example programs in this tutorial are designed to be as generic as possible, most should be compile able with any good quality C++ compiler provided it follows C++ 11 or newer version.

Table of Contents

Multiple Inheritance in Java and Python

In object-oriented programming, languages like Java and Python take distinct approaches to inheritance. Java, for instance, supports single inheritance only, while Python allows both single and multiple inheritance. C++ stands out by supporting both and gives developers a unique range of choices when designing class structures. Understanding how multiple inheritance is handled in C++ as opposed to other languages is crucial for effective utilization of this feature and address its associated complexities.

Multiple Inheritance in C++

C++ Language’s most powerful feature is multiple inheritance which makes it more powerful than Java. It is possible for one class to inherit the attributes of two or more classes.

The general form is  class DerivedClass : BaseClassList //seperated with commas { };

Here is a simple example.

Here Z class has the access to both the members of X & Y class.

The biggest problem with multiple inheritance involves the inheritance of variables or methods from two or more parent classes with the same name. Which variable or method should be chosen as the inherited variable or method if two or more have the same name? This will be illustrated in the next few example programs.

Simple C++ Multiple Inheritance Example

Let’s consider a scenario where we have two base classes, Shape and Color, and we want to create a derived class ColoredShape that inherits from both. This simple example illustrates the basic syntax of multiple inheritance in C++:

Practical Multiple Inheritance

Consider a scenario where you have a class DatabaseConnection handling database connectivity and another class Logger managing logging functionality. Creating a class DatabaseLogger through multiple inheritance allows you to seamlessly integrate both functionalities into a single class:

In this example, the DatabaseLogger class includes the performDatabaseOperation method, which shows the sequence of connecting to the database, executing a query, disconnecting, and logging the completion. Additionally, the performSecureDatabaseOperation method demonstrates a more complex scenario by incorporating authentication logic before executing a database operation. The authenticate method checks whether the provided username and password match a predefined set of credentials. This shows how multiple inheritance allows us to seamlessly integrate functionalities from both the base classes.

Duplicated Method Names

Take a look at the following C++ Program. You will notice that both of the parent classes have a method named initialize(), and both of these are inherited into the subclass with no difficulty. However, if we attempt to send a message to one of these methods, we will have a problem, because the system does not know which we are referring to. This problem will be solved and illustrated in the next example program.

Be sure to compile and execute this program after you understand its operation completely.

If you study the code, you will find that a new method has been added to all three of the classes named cost_per_full_day(). This was done intentionally to illustrate how the same method name can be used in all three classes. The class definitions are no problem at all, the methods are simply named and defined as shown. The problem comes when we wish to use one of the methods since they are all the same name and they have the same numbers and types of parameters and identical return types. This prevents some sort of an overloading rule to disambiguate the message sent to one or more of the methods.

The method used to disambiguate the method calls are illustrated in lines 60, 64, and 68 of the main program. The solution is to prepend the class name to the method name with the double colon as used in the method implementation definition. This is referred to as qualifying the method name. Qualification is not necessary in line 68 since it is the method in the derived class and it will take precedence over the other method names. Actually, you could qualify all method calls, but if the names are unique, the compiler can do it for you and make your code easier to write and read.

Duplicated Variable Names

Now examine the following C++ Program, you will notice that each base class has a variable with the same name.

Following the principles of inheritance, a driven_truck object inherently has two variables named weight. While this might pose an issue, C++ offers a well-defined approach to access each variable. Lines 38 and 45 in the code exemplify the application of these variables and requires qualification for distinct identification. It is crucial to note that the subclass itself can have a variable sharing the same name as those inherited from parent classes, eliminating the need for qualification when accessing it.

Understanding single inheritance lays the foundation for comprehending multiple inheritance as merely an extension of the same principles. If two inherited methods or variables share identical names, qualification becomes imperative to guide the compiler in selecting the appropriate one.

Member Initializers

In C++, we can utilize member initializers to conveniently initialize class members. For instance, if we have a class member named member_var of type int, we can set its initial value by specifying the member’s name followed by the desired value in parentheses. For instance, to initialize it to 13, we would use the following line of code in the member initializer list:

After all member initialization is complete, the regular constructor code is executed, as seen in line 16.

Order of Member Initializers

While the order of member initialization might appear peculiar, it adheres to simple rules. The order is not dictated by the initialization list but follows a strict sequence within your control. Inherited classes are initialized first, listed in the order specified in the class header. Even if lines 14 and 15 were reversed, new_date would still be initialized first because it’s mentioned first in line 8. This follows the principle that C++ respects its elders and initializes its parents before itself—a helpful memory aid for working with member initializers.

Subsequently, local class members are initialized in the order of declaration within the class, not according to the initialization list. While using member initializers for class members is possible, it’s often considered good practice to initialize them in the regular constructor code.

After executing all member initializers in the proper order, the main body of the constructor proceeds in the usual manner.

Parameterized Types

In programming, there are often situations where you need to perform a operation on different data types. For instance, you might want to sort lists of integers, floating-point numbers, and strings in a similar manner. It becomes impractical to write separate sorting functions for each type when the logic is essentially the same. Parameterized types, also known as templates or generics, offer a solution by enabling the creation of a single sorting routine capable of handling multiple data types concurrently.

While this capability is already present in the Ada language through the use of generic packages or procedures, its absence in C++ has limited the availability of prewritten, thoroughly debugged software routines that work with various types. Bjarne Stroustrup, the creator of C++, has announced that parameterized types will be introduced in a future version of C++, opening the door for a components industry to provide readily available, pre-coded, and efficient source code for standard operations like sorting, queues, stacks, and lists.

The Template Example

In the following C++ we introduce the concept of templates with a simple example. The template, starting from line 4, defines a function called maximum that can compare and return the greater of two values of any data type. This parameterized type, denoted by ANY_TYPE, allows flexibility in handling various data types without writing separate functions for each.

The template allows for concise and reusable code, handling different data types effortlessly. The diligent student should recognize that this approach is more versatile than using macros, which lack strict type checking.

A Class Template

In the following program, we delve deeper into templates by creating a class template named stack. This template, from line 6 to 16, defines a simple stack class capable of storing various data types. The main program demonstrates the usage of this template by creating instances of the stack for integers, floats, and strings.

This class template demonstrates the flexibility of parameterized types, enabling the creation of a stack for various data types. The diligent student will recognize the simplicity of the class and understand its primary purpose: to illustrate the use of parameterized types. The code, though basic, serves as a fundamental example for novice C++ programmers.

Exception Handling

The C++ language now supports exception handling. This valuable feature allows programmers to intercept errors and prevent abrupt program termination when encountering fatal errors. Bjarne Stroustrup, working in collaboration with the ANSI-C++ committee, has ensured the incorporation of exception handling into the language. The support for exception handling is available in modern versions of C++, enhancing the robustness of error management and providing more reliable program execution.