Home Icon Home Resource Centre Multiple Inheritance In C++ & Ambiguity Problems (+Code Examples)

Multiple Inheritance In C++ & Ambiguity Problems (+Code Examples)

Multiple inheritance is when a single class can inherit from more than one base class. While it helps model complex scenarios, it can also lead to issues like ambiguity and diamond problems.
Shivani Goyal
Schedule Icon 0 min read
Multiple Inheritance In C++ & Ambiguity Problems (+Code Examples)
Schedule Icon 0 min read

Table of content: 

  • What Is Multiple Inheritance In C++?
  • Examples Of Multiple Inheritance In C++
  • Ambiguity Problem In Multiple Inheritance In C++
  • Ambiguity Resolution In Multiple Inheritance In C++
  • The Diamond Problem In Multiple Inheritance In C++
  • Visibility Modes In Multiple Inheritance In C++
  • Advantages & Disadvantages Of Multiple Inheritance In C++
  • Multiple Inheritance Vs. Multilevel Inheritance In C++
  • Conclusion
  • Frequently Asked Questions
expand icon

The concept of inheritance is a fundamental feature of object-oriented programming languages. It allows us to create a new class (i.e., derived or child class) based on an existing class or classes (i.e., base or parent class). The derived class inherits properties (data members) and behaviors (member functions) from the base class, which is known as inheritance.

In this article, we will focus on multiple inheritance in C++ programming, its syntax, implementation, problems/ challenges, modes, and more with the help of detailed code examples.

What Is Multiple Inheritance In C++?

Multiple inheritance in C++ language allows a class to inherit members (data members and member functions) from multiple base classes. It's like a child learning different skills from different teachers.

  • Multiple inheritance means that a derived class can have more than one parent class, and it inherits properties and behaviors from all of them.
  • This is a powerful but complex concept of object-oriented programming that must be used with caution to avoid ambiguities.

How Does Multiple Inheritance In C++ Work?

We create a derived class/ child class that can inherit all public and protected members from multiple base classes. When an object of the derived class is initialized, the derived class constructor calls the base class constructors in the order they are listed in the declaration. And the destructor calls the base class destructors in the reverse order.

Graphical representation of multiple in heritance in C++ from n number of base classes.

The logical diagram in the image above depicts what a multiple inheritance structure would look like. We have a single class named a child class, which inherits from n other classes denoted by Base Class 1, Base Class 2… and so on.

Syntax For Implementing Multiple Inheritance In C++

class Derived : access_specifier1 Base1, access_specifier2 Base2, ... {
// Derived class members and methods
};

Here,

  • Derived: The name of the derived class that inherits from multiple base classes.
  • access_specifier: It is a specifier that indicates if the base class members are accessible for the derived class. We can use public, protected, or private access specifiers.
  • Base1, Base2, ...: The names of the base classes from which the derived class will inherit ( separated by commas).

Let’s see the implementation syntax of multiple inheritance for two-parent classes and one child class.

class Base1 {
public:
void func_Base1() {
// Base1 function implementation
}
};

class Base2 {
public:
void func_Base2() {// Base2 function implementation
}
};

class Derived : access_specifier1 Base1, access_specifier2 Base2 {
public:
void func_Derived() {// Derived class function implementation
}
};

Multiple inheritance can be used to model real-life scenarios where an entity possesses characteristics and behaviors from multiple sources. 

Examples Of Multiple Inheritance In C++

We know the syntax and workings of multiple inheritance in C++. Let's look at practical examples to showcase how it actually works.

Example 1: Program Demonstrating Use Of Multiple Inheritance In C++

Imagine creating a class for a vehicle that inherits features from both a Car class and a GPS class. The Car class provides information about speed and fuel consumption, while the GPS class offers location and navigation capabilities. We can create a SmartCar class that combines the functionalities of a car and a GPS system using multiple inheritance.

Code Example:

Output:

Car is driving.
GPS is navigating.
SmartCar is auto-driving.

Explanation:

We begin the C++ program by including the <iostream> header file. Then-

  1. We create a base class called Car, which contains a public member function drive() that uses std::cout to print a phrase to the console.
  2. Then, we define a second base class called GPS, containing a public member function, navigate(), to print a phrase when called.
  3. Next, we define a derived class, SmartCar, using multiple inheritance. It publicly inherits from both the Car and GPS classes.
    • This means that SmartCar inherits all the public member functions and attributes of both base classes.
    • It also has a member function, autoDrive(), which prints a phrase using std::cout.
  4. Inside the main() function, we create an object of SmartCar class called myCar.
  5. We then use this object with dot operator to call the drive() function inherited from the Car class and call the navigate() function inherited from the GPS class.
  6. The functions print the string messages, 'Car is driving' and 'GPS is navigating', respectively.
  7. Lastly, we call the autoDrive() member function of the SmartCar class using the dot operator. It prints the phrase 'SmartCar is auto-driving' to the console.
  8. Finally, the main() function returns 0, indicating successful program execution.

Example 2: Using Multiple Inheritance In C++ To Perform Arithmetic Operations

In this example, we'll create a calculator class that inherits functionality from both an addition class and a subtraction class to perform arithmetic operations like addition and subtraction.

Code Example:

Output:

Calculator is ready.
Addition result: 15
Subtraction result: 5

Explanation:

  1. We create a base class, Addition, containing the public member function add(). It takes two integers, a and b, and returns their sum using the addition arithmetic operator.
  2. Then, we define a second base class, Subtraction with public member function subtract(), that takes two integers and returns their difference.
  3. Next, we create a derived class, Calculator, which publically inherits from both the Addition and Subtraction classes. Inside:
    • We define a constructor, Calculator(), which prints the phrase 'Calculator is ready' when an object of class is created.
    • Next, we define a member function, performOperations(), which performs both addition and subtraction using the inherited add() and subtract() methods.
    • It takes two integer numbers as input and calculates their addition (stored in resultAddition) and subtraction (stored in resultSubtraction). The result is printed to the console using std::cout and std::endl, which moves the cursor to the next line.
  4. In the main() function, we create an instance of the Calculator class, object myCalculator.
  5. Next, we declare and initialize integer variables, num1 and num2, with values 10 and 5, respectively.
  6. Then, we call the performOperations() function on myCalculator object and pass num1 and num2 as parameters.
  7. The function uses the methods from derived classes to perform the operations and displays the results of addition and subtraction to the console.

Example 3: Getting Average Marks Of Two Subjects Using Multiple Inheritance In C++

Here, we will define two base classes, i.e., Maths and Science, representing marks obtained in math and science subjects, respectively. Then, we'll create a derived class Student that inherits from both Math and Science to calculate and display the average marks.

Code Example:

Output:

Math Marks: 90.5
Science Marks: 88
Average Marks: 89.25

Explanation:

In this example C++ code,

  1. We first define base class, Maths containing:
    • Public data member, mathMarks, of double data type to store the marks in the Maths subject.
    • Next, we have a constructor, mathMarks(math), which initializes the mathMarks data member.
    • Finally, we have a public member function displayMathMarks(), which prints the marks using std::cout.
  2. Then, we create a second base class, Science, containing:
    • A public data member called scienceMarks of type double to store marks in the Science subject.
    • A constructor, scienceMarks(science) to initialize the scienceMarks data member, and a displayScienceMarks() member function to print the marks.
  3. After that, we create a derived class, Student, which publically inherits from both the parent classes using multiple inheritance. Inside:
    • We have a constructor Student(), which calls the constructors of the base classes to initialize the respective data members. 
    • Then, we define a member function, calculateAverage(), that calculates the average of the math and science marks and prints it to the console using std::cout.
  4. In the main() function, we first initialize two double type variables mathMarks and scienceMarks, with values 90.5 and 88.0, respectively.
  5. Next, we create an instance student of the Student class, passing the above variables to the constructor, which initializes the mathMarks and scienceMarks data members.
  6. We then use this object to call the displayMathMarks() and displayScienceMarks() functions to print the values to the console.
  7. Next, we call the calculateAverage() function on the student object using the dot operator to calculate the average and print it to the console.

Check out this amazing course to become the best version of the C++ programmer you can be.

Ambiguity Problem In Multiple Inheritance In C++

Ambiguity in multiple inheritance arises when a derived class inherits members (either data members or member functions) with the same name from multiple base classes. When this happens, the compiler cannot determine which version of the member to use in the derived class, resulting in an ambiguity error.

Let's illustrate this with a real-life example:

Suppose we have two base classes, Teacher and Musician, and a derived class, MusicTeacher, that inherits from both classes. Both Teacher and Musician have a member function called teach(). Since the derived class doesn't specify which teach() function to use, it leads to ambiguity.

Code Example:

Output:

Compilation error: request for member ‘teach’ is ambiguous

Explanation:

  1. We create a base class, Teacher, with a public teach() member function, which prints a message using std::cout when called.
  2. Next, we create another base class, Musician, containing a public teach() method that prints a different message when called.
  3. Then, we define a derived class MusicTeacher, which publicly inherits from both Teacher and Musician.
  4. This means that the public members of both base classes will be accessible in the derived class. This leads to ambiguity due to inherited member functions.
  5. Since MusicTeacher class does not have its own teach() function, it inherits two functions with the same name. One from the Teacher class, which teaches general subjects, and another from the Musician class, which teaches music.
  6. Now, inside the main() function, we create an instance myTeacher, of the MusicTeacher class.
  7. We then use this object to call teach() function. This leads to an ambiguity error, because the compiler cannot decide which teach() method to call— the one from the Teacher class or the one from the Musician class.
  8. Since there is no clear instruction on which method to use, the compiler throws an error.

Ambiguity Resolution In Multiple Inheritance In C++

To resolve this ambiguity due to multiple inheritance in C++ classes, we must explicitly specify which base class’s member function or data member we want to use in the derived class. There are two common ways to handle this:

  1. Using the scope resolution operator (::)
  2. Overriding the member function in the derived class.

Scope Resolution Operator (::) To Resolve Ambiguity Of Mulitple Inheritance In C++

In this method, we can explicitly specify the base class from which we want to access the member by using the scope resolution operator along with the base class's name.

Syntax:

base_class::member_name

Here,

  • base_class: The name of the base class from which you want to access a member.
  • member_name: The name of the member (function or data) that could lead to ambiguity.

Let’s solve the ambiguity in our music teacher example for multiple inheritance in C++ above by using the scope resolution operator.

Code Example:

Output:

Teaching general subjects.
Teaching music.

Explanation:

In continuation of previous example, we have the two base classes, Teacher and Musician with a teach() member function each. The derived class MusicTeacher publicly inherits from the two base classes.

  1. In the main() function, we create myTeacher object of MusicTeacher class.
  2. Then, we call the teach() function by specifying the name of the base class (Teacher), the scope resolution operator, and the function name teach(). This lets the compiler know to call teach() from the Teacher class.
  3. Similarly, we call teach() from Musician class by specifying the class name and using the scope resolution operator to specify which member function.

Override The Member Function To Resolve Ambiguity Of Mulitple Inheritance In C++

In C++, another way to resolve the ambiguity in multiple inheritance is by overriding the member function in the derived class. When you provide a custom implementation of a member function within the derived class, this overrides the inherited versions from the base classes. As a result, any potential ambiguity is eliminated from the get-go because the derived class's function always takes precedence.

In other words, the derived class’s version of the function will be called, and there will be no confusion between the functions inherited from the base classes.

How Does It Work?

  • If the derived class defines its own version of a function (with the same name as in the base classes), it effectively replaces or overrides the inherited functions.
  • The compiler will always call the derived class's function, ensuring no ambiguity occurs.

This approach is particularly useful when you want the derived class to have its own specific behavior, distinct from the base classes. Below is an example showcasing how you can give a custom definition of a member function to resolve the ambiguity problem of multiple inheritance in C++ classes.

Code Example:

Output:

Teaching both general subjects and music.

Explanation:

  1. In the code example, we define two base classes,Teacher and Musician, each with a member function named teach().
  2. Next, we define a derived class MusicTeacher that inherits from both Teacher and Musician using public inheritance. This class provides its own implementation of the teach() function.
  3. Inside the MusicTeacher class, we override the teach() function with a custom implementation.
  4. This implementation resolves the ambiguity by providing a unique behavior for the teach() function in the context of the MusicTeacher class. It combines the teaching of general subjects and music.
  5. In the main() function, we create an instance of the MusicTeacher class named myTeacher.
  6. We then use the instance with the dot operator to call the teach() function (i.e. myTeacher.teach()), which invokes the overridden teach() function in the MusicTeacher class, which prints ‘Teaching both general subjects and music.’

When overriding a member function in the derived class, the derived class's implementation takes precedence over the base class implementations, resolving the ambiguity.

Level up your coding skills with the 100-Day Coding Sprint at Unstop and claim the bragging rights, now!

The Diamond Problem In Multiple Inheritance In C++

Representation of diamond problem that occurs with multiple inheritance in C++

The diamond problem occurs with multiple inheritance in C++ classes when a class inherits from two or more classes that share a common base class. The ambiguity arises because the derived class ends up with multiple instances of the common base class, leading to conflicts when accessing base class members.

Real-world example of the diamond problem with multiple inheritance in C++:

  1. Super Base Class: Say you have a base class, Human, representing a human being with various attributes.
  2. First Inheritance/ Base Class: We derive two classes, Person (representing individuals) and Employee (representing employees), both inherited from Human
  3. Second Inheritance/ Derived Class: Then, we create a Manager class that inherits from both Person and Employee.

This creates two instances of Human class within Manager class: one from Person and one from Employee. When we try to access Human’s members from Manager, the compiler doesn't know which Human instance to refer to, causing ambiguity. This is called the diamond problem because of the structure of inheritance.

Note: The diamond problem differs from the general ambiguity problem with multiple inheritance. The diamond problem involves both multiple and multi-level inheritance, whereas ambiguity can arise in simple multiple inheritance in C++ classes.

Let's look at how this shows across code, after which we will proceed to see how to resolve the diamond problem when using multiple inheritance in C++.

Code Example:

Output:

Human class constructor called
Person class constructor called
Human class constructor called
Employee class constructor called
Hello, I'm a person.
I am working as an employee.

Explanation:

Converting the real-life example above into code,

  1. We define a Human class with a private member variable name and a constructor that initializes the name and prints a message to the console.
  2. The class also contains a public member function introduce() that prints a message introducing the human by name.
  3. Then, we create two derived classes, Person and Employee, that inherit publicly from the Human class. Their respective constructors invoke the constructor of their base class to initialize the name data member.
  4. Each class also has a member function, i.e., greet() and work(), respectively. They print a message to the console using the std::cout statement.
  5. Then, we define a class Manager that inherits from Person and Employee classes. It contains a constructor which calls its base constructors to initialize the name data member. 
  6. In the main() function, we create an instance manager of the Manager class and pass the value "Shivani" to the data member.
  7. This invokes the constructors for Employee and Person, which further invokes the Human class constructor twice.
  8. Then, we use the manager object with the dot operator to call the greet() and work() methods from the Person and Employee classes, respectively. Since the methods are unique, they do not lead to any confusion. 
  9. After that, we have a commented line in the code that shows the diamond problem. Here, we have used the manager object to call the introduce() function from the Human base class. 
    • The Manager class can access introduce() through the Person and Employee classes since both share the base class.
    • But without any further instructions, the compiler can't determine which instance of Human to use (inherited from Person or Employee), resulting in an ambiguity error.
  10. This problem arises because the Manager class inherits from the Person and Employee classes, which both inherit from Human. The structure of inheritance here lends the name– Diamond Problem.

The diamond problem occurs due to hybrid inheritance from both multilevel and multiple inheritance in C++. It can be solved using two main methods, i.e., virtual inheritance and explicit qualification with the scope resolution operator (::). Let's explore these methods in detail.

Method 1: The Scope Resolution Operator To Solve Diamond Problem

Just like in the case of ambiguity problem, the scope resolution operator allows us to explicitly specify which base class's member we want to access in the derived class. This resolves the ambiguity without changing the inheritance structure.

Syntax:

int main() {
derivedobj.Base1::memberName; // Access member from Base1
derivedobj.Base2::memberName; // Access member from Base2

Here,

  • The derivedobj refers to the instance of the Derived class that inherits from the two base classes.
  • Base1 and Base2 are the base classes that inherit from the Super Base class.
  • memberName refers to the data member of the Super Base class that we want to access.
  • The scope resolution operator (::) helps specify which base class to refer to when accessing the Super Base class member.

Let's look at an example showcasing the use of the scope resolution operator to solve the diamond problem of multiple inheritance in C++ classes.

Code Example:

Output:

I am a human named Shivani
Hello, I'm a person.
I am working as an employee.

Explanation:

A version of the previous example, we have a super base class called Human, with a member function introduce()Person and Employee classes inherit from Human, with their own functions, i.e., greet() and work(), respectively. We also have a Manager class that inherits from both Person and Employee, leading to the diamond problem. 

  1. In the main() function, we create an object of the Manager class named manager and initialize the attribute name with the string value- "Shivani".
  2. Using this object, we call the introduce() function from the Person class. Here, we use the scope resolution operator and the name of the class from which we want to call introduce(), i.e., Person class.
  3. This lets the compiler know which route to take to call the introduce() method, avoiding any confusion. We can do the same using the Employee class.
  4. Then, we call the greet() and work() functions from the Person class and the Employee class, respectively. Here, we do not need the scope resolution operator since the compiler will automatically know which functions to invoke.

The example shows how to use the scope resolution operator to void the Diamond Problem by explicitly mentioning the base class we want to access.

Method 2: Virtual Inheritance To Solve Diamond Problem

Virtual inheritance is a feature in C++ that ensures that only one instance of a common base class is shared among multiple derived classes in a diamond-shaped inheritance hierarchy. It eliminates the possibility of repetitive instances.

In this method, we use the virtual keyword during inheritance. When the base class is marked as virtual, it signals the compiler to create a single instance of that base class within the hierarchy.

Let's discuss the solution to the diamond problem using virtual inheritance with the manager class example.

Code Example:

Output:

I am a human named Shivani
Hello, I'm a person.
I am working as an employee.

Explanation:

Just like the previous two examples, we have a super base class Human with a constructor, data member name, and a member function introduce(). 

  1. We then define a Person and Employee class, which is derived virtually from Human using virtual inheritance/ keyword. 
  2. By marking both Person and Employee as virtual public Human, we ensure that they share the Human base class, and Manager has only one Human instance.
  3. Then, we define a class Manager which inherits from both Person and Employee classes.
    • It consists of a constructor, where, by definition, the Human class is explicitly initialized once, along with Person and Employee constructors. This helps avoid the duplication issue seen in regular multiple inheritance.
    • In other words, in the Manager constructor, when we initialize Human(name), it ensures that the single Human instance is properly initialized.
  4. In the main() function, we create an instance of the Manager class named manager and initialize the name attribute with Shivani.
  5. Using this instance, we call the introduce() function (i.e., manager.introduce()). Unlike in the first example, when we call introduce() from Manager, there is no ambiguity, as only one Human instance exists.
  6. We also call the greet() and work() functions from the Person and Employee classes.

Also read: Diamond Problem In C++ & Its Resolutions Explained (+Examples)

Visibility Modes In Multiple Inheritance In C++

The visibility mode in multiple inheritance specifies how the members (variables and functions) of base classes are inherited by the derived class when multiple base classes are involved.

In other words, these modes determine the accessibility of base class members (data members and member functions) in the derived class, offering flexibility in designing class hierarchies to satisfy specific needs. It can be controlled using three distinct visibility modes–public, protected, and private.

Example of public mode for inheritance/ multiple inheritance in C++

Public Visibility Mode For Multiple Inheritance In C++

The public keyword stipulates that public members of the parent class remain public in the derived class, protected members remain protected, and the private members of the base class remain inaccessible in the derived class.

Syntax:

class Base1 {
//data member and member functions
};
class Base2 {
//data member and member functions
};

class Derived : public Base1, public Base2 {
//data member and member functions
};

Here,

  • Base1, Base2, and Derived are the names of the parent class 1, parent class 2, and the child class, respectively.
  • The public keyword signifies that the derived class inherits publically from the parent classes, preserving the original accessibility modes.

Protected Inheritance Mode For Multiple Inheritance In C++

When using protected inheritance, the public and protected members of the base class become protected in the derived class. This means they are accessible within the derived class and any of its subclasses but not to other parts of the program. Private members of the base class remain inaccessible.

Syntax:

class Base1 {
//data member and member functions
};

class Base2 {
//data member and member functions
};

class Derived : protected Base1, protected Base2 {
//data member and member functions
};

Here,

  • Base1, Base2, and Dervied, are the names of the base and derived classes, respectively.
  • The protected keyword used in the inheritance declaration specifies that the inherited members from Base1 and Base2 will be protected in Derived. This means that only the derived class and its subclasses can access the members of the base class.

Private Visibility Mode For Multiple Inheritance In C++

When using private keyword, both public and protected members of the base class become private members in the derived class. This means they are no longer accessible outside the derived class, even to subclasses of the derived class. As always, private members of the base class remain inaccessible for the derived class.

Syntax:

class Base1 {
//data member and member functions
};

class Base2 {
//data member and member functions
};

class Derived : private Base1, private Base2 {
//data member and member functions
};

Here,

  • Base1, Base2 and Dervied are the base classes and derived class, respectively.
  • The private access specifier used when deriving the Derived class indicates that the visibility mode is private. This means all base class members become private and can be accessed by the derived class only.

Let's look at an example showcasing the implementation of different visibility modes with multiple inheritance in C++ classes.

Code Example:

Output:

Name: Shivani
Area: S.Delhi, State: Delhi
Age: 34

Explanation:

In the C++ program example, we have three base classes and a derived class, which inherits from all three with different access specifiers.

  1. We first define class Person with a private data member name (string type), a public constructor and a public member function displayPersonInfo()  which prints the name using std::cout.
  2. Then, we define a class Address with two protected data members area and state (both string type), a public constructor, and a public member function displayAddressInfo() that prints the data members.
  3. Next, we define a class Age with a protected data member age (int type), a public constructor and a public member function displayAgeInfo(), which prints the age.
  4. After that, we define a derived class Contact which inherits publically from Person class, protectedly from Address class, and privately from Age class. Inside, we have three public members:
    • We have a constructor that invokes the constructor of all three base classes to intialise the data members when an object of Contact is created.
    • Then, we define a member function displayAddress(), which includes a function call to the displayAddressInfo() method from Address class.
    • Following this, we define the member function displayAge(), which calls the displayAgeInfo() method from Age class.
  5. In the main() function, we create an object contact, of the Contact class and initialize the members name, area, state and age with values Shivani, S.Delhi, Delhi, and 34.
  6. Next, we use the object with dot operator to call displayPersonInfo() function from Person. This goes off without an error because Contact inherits publicly from Person, making all its members accessible to the derived class directly.
  7. After that we have two sections of comments. In the first section, we use the same method to call the displayAddressInfo() method from Address. This will generate an error because Contact inherits protectly from Address, making it possible for only the derived class (and its subclasses) to access base members directly.
  8. Similarly, if we try to call displayAgeInfo() method directly using the object it will throw an error because Contact inherits privately from Age. This means only the derived class (and not its subclasses) can access base members.
  9. Next, we use the object to call the displayAddress() and displayAge() functions. These work without an error because we are accessing the protected and private (respectively) members from inside the derived class.

The example shows how visibility modes allow you to control the accessibility of base class members in the derived class. The choice of visibility mode in which we want to inherit the base class depends on the design and intended behavior of our derived classes.

Advantages & Disadvantages Of Multiple Inheritance In C++

Multiple inheritance opens the doors to a number of design possibilities. While it offers several advantages, it also brings its own set of complexities, which requires utmost consideration for error avoidance. In the section ahead, we'll discuss various advantages and disadvantages of multiple Inheritance in C++:

Advantages Of Multiple Inheritance In C++

  1. Code Reusability: Multiple inheritance encourages code reuse by enabling a class to inherit attributes and behaviors from several base classes. Instead of rewriting similar code, you can simply extend existing classes, making code more modular and maintainable.
  2. Rich Class Hierarchies: Multiple inheritance in C++ helps create rich, layered class hierarchies that model complex real-world scenarios more accurately.
  3. Flexibility: Multiple inheritance in C++ code provides flexibility in designing class hierarchies, allowing for more granular control over object modelling. You can combine features of different classes to create highly specialized classes tailored to specific needs.

Disadvantages Of Multiple Inheritance In C++

  1. Ambiguity: When a derived class inherits the same member from multiple base classes, the compiler can’t decide which one to use, leading to potential conflicts. Resolving this may require techniques like explicit qualification or virtual inheritance.
  2. Complexity and Readability: Multiple inheritance in C++ classes can make class hierarchies harder to manage and understand. Tracing the source of inherited members can become tricky, especially in large projects or deep inheritance chains.
  3. Diamond Problem: The Diamond Problem is a well-known issue in multiple inheritance in C++, where a class inherits from two or more classes that share a common base class. This can lead to redundancy and ambiguity.

Comparison between multilevel and multiple inheritance in C++

Multiple Inheritance Vs. Multilevel Inheritance In C++

The table below highlights the key differences between multilevel and multiple inheritance in C++ classes.

Basis Multilevel Inheritance Multiple Inheritance
Number of Base Classes A derived class inherits from a single base class, and that derived class serves as a base for another. A derived class can inherit from multiple base classes simultaneously.
Class Hierarchy Creates a hierarchical chain where each class inherits from the previous one. Forms a flat structure where a class directly inherits from multiple base classes, often creating a more complex layout.
Ambiguity Minimal ambiguity as each class has a single base. Higher potential for ambiguity, like the Diamond Problem, when multiple base classes share a common ancestor. Requires virtual inheritance to resolve.
Complexity Less complex with a straightforward linear hierarchy. More complex due to possible conflicts and overlapping features from multiple base classes.
Class Design Best for scenarios with a clear hierarchy, where each level adds or modifies behavior. Ideal for combining features from unrelated base classes.
Example Animal -> Bird -> Sparrow (clear chain of inheritance). Manager inheriting from both Person and Employee, combining traits from both.

Looking for guidance? Find the perfect mentor from select experienced coding & software experts here.

Conclusion

C++ offers three main types of inheritance: single, multilevel, and multiple inheritance, each with its pros and cons. Single inheritance provides a simple, straightforward hierarchy, while multiple inheritance allows a class to inherit from multiple sources, promoting code reuse.

However, multiple inheritance in C++ can introduce complexities and ambiguities, most notably the infamous Diamond structure problem, which demands resolution techniques like virtual inheritance or explicit qualification, the choice of which depends on the specific design goals of our program.

All in all, multiple inheritance offers flexibility but demands responsibility. With proper handling of ambiguities and inheritance conflicts, use of multiple inheritance in C++ code can significantly enhance the design and modularity of your code.

Also read- 51 C++ Interview Questions For Freshers & Experienced (With Answers)

Frequently Asked Questions

Q1. How do C++11 and later standards address issues related to multiple inheritance?

C++11 and later standards introduced several features to handle issues with multiple inheritance:

  • override and final keywords: Help clarify which virtual functions can be overridden in the derived class (override–yes, final–no), reducing ambiguity.
  • Smart pointers like std::shared_ptr and std::unique_ptr manage object lifetimes and ownership when dealing with multiple inheritance in C++ classes.
  • Type traits and SFINAE (Substitution Failure Is Not An Error) allow sophisticated template programming when managing complex base classes with different characteristics.

Q2. What are the differences between public, private, and protected inheritance modes in multiple inheritance in C++?

When dealing with multiple inheritance in C++, the access specifiers determine how the members (attributes and functions) of the base class are inherited in the derived class. The differences between the three are listed in the table below:

Public Inhritance Private Inheritance Protected Inheritance
  • Public members of the base class remain public.
  • Models an "is-a" relationship.
  • Useful for creating derived classes that can be used interchangeably with the base class.
  • All members of the base class become private.
  • Models an "implemented-in-terms-of" relationship.
  • Encapsulates the base class's functionality.
  • Public members of the base class become protected.
  • Allows derived classes to access the base class interface without exposing it to external code.

Q3. Are there any potential drawbacks or challenges associated with multilevel inheritance in C++?

Yes, multilevel inheritance can introduce several challenges:

  • Complexity: As inheritance levels increase, the class hierarchy can become harder to understand and maintain.
  • Inflexibility: Once established, refactoring or reorganizing a deep inheritance hierarchy can be challenging.
  • Compile Time: Deeper hierarchies can increase compile time, particularly when working with online C++ compilers.

Q4. What is the difference between virtual inheritance and normal inheritance in the context of multiple inheritances?

In the context of multiple inheritance in C++, normal inheritance refers to the regular way of one derived class inheriting from multiple base classes. In contrast, virtual inheritance is a special mechanism that ensures there is only one shared instance of a common base class in a multiple inheritance hierarchy.

Aspect Normal Inheritance Virtual Inheritance
Object Creation Each derived class creates its own separate instance of the base class. Ensures only one shared instance of the common base class among all derived classes.
Ambiguity Resolution Ambiguities arise when the same-named member function is inherited from multiple base classes. Resolves ambiguities like the Diamond Problem by ensuring a single shared base class instance.
Memory Overhead May lead to increased memory usage due to multiple copies of base classes. Introduces minor overhead due to vtable management but is more memory-efficient.

Q. How to call the parameterized constructor of the base class?

In multiple inheritance in C++, when a derived class inherits from multiple base classes, each of these base classes may have its own constructors. To call the parameterized constructor of a base class in C++, we need to explicitly invoke it in the initialization list of the derived class's constructor.

Let's consider an example where we have a derived class, Derived that inherits from two base classes, Base1 and Base2, both of which have parameterized constructors.

Code Example:

Output:

Base1 parameterized constructor called with value: 10
Base2 parameterized constructor called with value: 20
Derived constructor called.

Explanation:

In the above code snippet-

  1. When defining its constructor in the Derived class, we use a colon (:) to introduce the initialization list. Within this initialization list, we explicitly call the constructors of the base classes.
  2. In the Derived constructor, we specify Base1(value1) and Base2(value2). This means that we're calling the parameterized constructors of Base1 and Base2 with the provided values value1 and value2.
  3. When we create an object of the Derived class in the main() function, i.e., Derived derivedObject(10, 20), the constructors of Base1 and Base2 are called first with the respective values, followed by the Derived class's constructor.

Q. Are there alternative approaches to multiple inheritance in C++ for achieving code reusability and flexibility?

Some alternatives to multiple inheritance in C++ classes include:

  • Composition: Reuse functionality by including objects of other classes as members, promoting encapsulation and flexibility.
  • Mixin Classes: It is a form of multiple inheritance where base classes define interfaces or traits to mix specific behaviors into a derived class.
  • Abstract Base Classes: Define interfaces using pure virtual functions (abstract classes), allowing derived classes to implement specific behaviors as per their own requirement.

Q. What is the exact order of destructors in the case of virtual/ multiple inheritance in C++?

When dealing with multiple inheritance in C++, it's crucial to understand the exact order in which destructors are called, especially in scenarios involving virtual inheritance. The order of destructor execution is important to ensure proper cleanup of resources in complex class hierarchies. Let's break down the rules for destructor order in multiple and/or virtual keyword situations.

Example: Suppose you have a class hierarchy with a most derived class D, meaning the object originally created was of class D. Additionally, D inherits multiply (non-virtually) from two base classes, B1 and B2. Each of these base classes may further inherit from other classes, creating a more complex inheritance hierarchy.

The order in which destructors are called is as follows:

  • The destructor for the most derived class, D, runs first.
  • The destructors for the non-virtual base classes are called in reverse declaration order. This means that the base class declared last (closest to the derived class) will have its destructor called first, followed by the destructor of the base class declared earlier. In the scenario with classes D, B1, and B2, the destructor order will be D, B2, and B1.
  • This rule is applied recursively for nested inheritance. For example, if B1 inherits from B1a and B1b, and B2 inherits from B2a and B2b, the destructor order becomes D, B2, B2b, B2a, B1, B1b, B1a.
  • After the above steps, virtual base classes are handled. The destructors for virtual base classes are executed in a specific order.
  • The order follows a depth-first left-to-right traversal of the graph of base classes, where left to right refers to the order of appearance of base class names.
  • Virtual base classes are unique, i.e., they are not destructed multiple times if they appear in the hierarchy multiple times.
  • The destructor order for virtual base classes is determined based on the left-to-right order of appearance of their names in the inheritance hierarchy.

This compiles our discussion on multiple inheritance in C++. Here are a few more interesting topics that you should know about:

  1. C++ Type Conversion & Type Casting Demystified (With Examples)
  2. Function Prototype In C++ | Definition, Purpose & More (+Examples)
  3. Friend Function In C++ Classes | Types, Uses & More (+Examples)
  4. Hierarchical Inheritance In C++ Explained With Real-Life Examples
  5. C++ Exception Handling | Use Try, Catch, & Throw (+Examples)
  6. Data Abstraction In C++ | Types, Use-Cases & More (With Examples)
Edited by
Shivani Goyal
Manager, Content

An economics graduate with a passion for storytelling, I thrive on crafting content that blends creativity with technical insight. At Unstop, I create in-depth, SEO-driven content that simplifies complex tech topics and covers a wide array of subjects, all designed to inform, engage, and inspire our readers. My goal is to empower others to truly #BeUnstoppable through content that resonates. When I’m not writing, you’ll find me immersed in art, food, or lost in a good book—constantly drawing inspiration from the world around me.

Tags:
Computer Science Engineering

Comments

Add comment
No comments Image No comments added Add comment
Powered By Unstop Logo
Best Viewed in Chrome, Opera, Mozilla, EDGE & Safari. Copyright © 2024 FLIVE Consulting Pvt Ltd - All rights reserved.