A C++ programmer strives for readable and reusable code. Easy-to-read code saves time and effort when making changes. Modifying such code will be easier for another developer jumping in.
C++ programmers use multiple inheritance to structure their code. Multiple inheritance lets you minimize copy-and-paste and improve overall conciseness. In this article, we’ll look at how multiple inheritance works, as well as what issues can arise and how to solve them.
What Is Multiple Inheritance in C++?
Inheritance is a fundamental programming concept and a critical feature of C++ polymorphism, which refers to a function or object’s ability to perform in multiple ways. The ability of one class to inherit the properties of another is referred to as inheritance.
There is a “parent” class and a “child” class in a simple inheritance relationship. During the declaration of the child class, the parent class’s methods and variables are automatically propagated to the child class. This eliminates the need for copy-paste and rewriting code.
Multiple inheritance is where a child class inherits from more than one parent. The goal of multiple inheritance is to allow multiple parent classes to share their characteristics with a single child.
How Does Multiple Inheritance Work?
Let’s say we want to create a program that provides information about a car. We’ll have two parent classes, SportsCar
and CollectableObject
, and a child class, called Ferrari
, which will inherit the parents’ characteristics.
First, we create the two classes SportsCar
and CollectableObject
. Each contains a method that confirms that outputs a bit of text.
...class SportsCar // this class contains a method that confirms it is a sports car
{
public:
void infoSportsCar(string carModel)
{
cout << "The " << carModel << " is a sports car." << endl;
}
};
class CollectableObject // this class contains a method that confirms it is a collectable object
{
public:
void infoCollectableObject(string carModel)
{
cout << "The " << carModel << " is a collectable object." << endl;
}
};...
Then, we create the class Ferrari
that inherits from both the classes SportsCar
and CollectableObject
:
class Ferrari: public SportsCar, public CollectableObject //This class inherits the methods of the previous classes
{
public:
string model;
void infoFerrari(string carModel)
{
cout << "This " << carModel << " is an amazing car!" << endl;
}
};
We want to display information about a certain Ferrari model. First, we create an object called myCar
from the Ferrari
class. We then call the methods of the Ferrari
class and its parent classes, passing a model name as a parameter:
int main()
{
Ferrari myCar; //This object, created from the class Ferrari has got all the characteristics of CollectableObject and SportsCar
myCar.model = "1954 Ferrari 250 Monza";
myCar.infoSportsCar(myCar.model);
myCar.infoCollectableObject(myCar.model);
myCar.infoFerrari(myCar.model);
}
When we run the program, we get the following output:
The 1954 Ferrari 250 Monza is a sports car.
The 1954 Ferrari 250 Monza is a collectable object.
This 1954 Ferrari 250 Monza is an amazing car!
The program returned the values using the methods initially declared in the CollectableObject
and SportsCar
classes.
If you wanted to create a Bugatti
class in the future, you could easily reuse the CollectableObject
and SportsCar
classes. They’d be parents to the new Bugatti
class which would inherit all their characteristics.
Visibility Mode Implementation
In our example, you probably saw that we used the keyword public
to define the methods within the classes. In C++, class members possess three visibility modes: public, private, and protected. We use these to assign a specific level of protection to class members and prevent misuse where needed.
- The
public
attribute: objects and methods from the same class and from the outside, including the main function and other classes can call a public member.
- The
private
attribute: only methods within the same class and a derived object can use a private element.
- The
protected
attribute: only members within its class or child classes can call it.
By default C++ defines class members as private if no attribute is specified. So, if you want a child class to use its parent’s methods, you need to declare them as public
or protected
.
Let’s say our program now deals with a certain type of car, which we’ll call the “F” type. Each “F” type car has a unique engine number and a unique body number. You would not want these numbers to be altered, and you would only want a child class of engine
or body
to use them. Here’s how we would implement these requirements:
...class Engine
{
private: // The variable below can't be altered from outside the class
int engineNumber = 9215;
protected: // The method below can only be accessed by a child of Engine
int vehicleEngineNumber()
{
return engineNumber;
}
};
class Body
{
private: // The variable below can't be altered from outside the class
int bodyNumber = 37265;
protected: // The method below can only be accessed by a child of Body
int vehicleBodyNumber()
{
return bodyNumber;
}
};...
First, we’ll create two classes similar to the previous ones, but with a private member (a serial number) and a protected member to be accessed by the Car
class:
...class Car: public Engine, public Body
{
public:
string carCode; string model;
int getBodyNb()
{
int carBodyNb = vehicleBodyNumber();
return carBodyNb;
}
int getEngineNb()
{
int carEngineNb = vehicle_engine_number();
return carEngineNb;
}
};...
The Car class declares a car code and two functions to get the engine and body numbers. The members of the Car
class remain public so that we can call its functions from anywhere else:
...int main()
{
Car ferrari;
ferrari.model = "Ferrari F430";
ferrari.carCode = "fri";
cout << "This " << ferrari.model << " id number is: " <<
ferrari.getEngineNb() << endl <<
ferrari.getBodyNb() << ferrari.carCode;
}
The main function creates a ferrari
object of the Car
class. The ferrari
object can now only call the functions in the Car
class, as the functions from Engine
and Body
are not accessible to the ferrari
object.
This Ferrari F430 id number is: 921537265fri
This example demonstrates how visibility mode works together with multiple inheritance. Combined, these features prevent unwanted modifications in the code.
If someone wanted to change the engine number for a given “F” type car, they would have to change it in the Engine
class. But this would impact the child classes of Engine
. The outside developer would think twice before making the change, and try to understand why this visibility mode was implemented in the first place.
The Diamond Problem
While multiple inheritance is useful, you might encounter scenarios where the compiler will throw an ambiguity error. Consider the following program:
...class SteelComponent
{
public:
float steelPrice = 1.5;
};
class MotorPoweredVehicle: public SteelComponent
{
public:
float enginePrice = steelPrice * 380;
};
class WheeledVehicle: public SteelComponent
{
public:
float wheelPrice = steelPrice * 500;
};
class Car: public MotorPoweredVehicle, public WheeledVehicle
{
public:
string model;
float carSteelCost;
};...
Our MotorPoweredVehicle
and WheeledVehicle
classes are still parents of the Car
class, but this time they are also children of the SteelComponent
class because they are made from steel.
In other words, the Car
class inherits the public variable of the SteelComponent
class twice. Here’s what happens when the main function attempts to display the cost of steel:
int main()
{
Car opel;
opel.model = "Opel Corsa";
opel.carSteelCost = opel.wheelPrice + opel.enginePrice;
cout << "Total price for steel was: $" << opel.carSteelCost;
cout << " and the steel price was $" << opel.steelPrice << "/kg";
}
We get the following error:
main.cpp:37:50: error: request for member ‘steelPrice’ is ambiguous cout << " and the steel price was $" << opel.steelPrice << "/kg"; ^~~~~~~~~~main.cpp:8:24: note: candidates are: float SteelComponent::steelPrice float steelPrice = 1.5; ^~~main.cpp:8:24: note: float SteelComponent::steelPrice
The compiler does not know which steelPrice
candidate to use. To solve this problem, we have two options: the scope resolution operator and visual inheritance.
Scope Resolution Operator
You can use the scope resolution operator to tell the compiler where to look for the right class member. From our previous example, simply replace the last cout
of the main function as follows:
...cout << " and the steel price was $" << opel.MotorPoweredVehicle::steelPrice << "/kg";...
When you add the word body and the double colon to specify where to pick up the information, you get the following output:
Total price for steel was: $1320 and the steel price was $1.5/kg
The compiler no longer throws an error, since it knows where to look for the data you requested.
Virtual Inheritance
Another way of solving the diamond problem is to prevent multiple copies of a base class, in our case, the steel
class. When declaring the engine
and body
classes, simply add the keyword virtual
in front of the parent class name:
...
class MotorPoweredVehicle: virtual public SteelComponent
...
class WheeledVehicle: virtual public SteelComponent...
The keyword virtual
prevents the program from creating multiple copies of the steel
class. You can now use the original syntax of cout
, and the program will run perfectly.
Become a C++ Developer
You can leverage multiple inheritance to write professional, concise code that anyone who jumps into your program can understand. The various visibility modes assist you in limiting modifications and securing your code. You can avoid common issues like the diamond problem by properly implementing the scope resolution operator or virtual inheritance.
To take your C++ game to the next level, check out Udacity’s online program that’s designed to turn you into a C++ developer. All you need is intermediate knowledge of any programming language.
Enroll in our C++ Nanodegree program today!
Complete Code Examples
Example 1: Multiple Inheritance
#include <iostream>
#include <string>
using namespace std;
class SportsCar // this class contains a method that confirms it is a sports car
{
public:
void infoSportsCar(string carModel)
{
cout << "The " << carModel << " is a sports car." << endl;
}
};
class CollectableObject // this class contains a method that confirms it is a collectable object
{
public:
void infoCollectableObject(string carModel)
{
cout << "The " << carModel << " is a collectable object." << endl;
}
};
class Ferrari: public SportsCar, public CollectableObject //This class inherits the methods of the previous classes
{
public:
string model;
void infoFerrari(string carModel)
{
cout << "This " << carModel << " is an amazing car!" << endl;
}
};
int main()
{
Ferrari myCar; //This object, created from the class Ferrari has got all the characteristics of CollectableObject and SportsCar
myCar.model = "1954 Ferrari 250 Monza";
myCar.infoSportsCar(myCar.model);
myCar.infoCollectableObject(myCar.model);
myCar.infoFerrari(myCar.model);
}
Example 2: Visibility Mode
#include <iostream>
#include <string>
using namespace std;
class Engine
{
private: // The variable below can't be altered from outside the class
int engineNumber = 9215;
protected: // The method below can only be accessed by a child of Engine
int vehicleEngineNumber()
{
return engineNumber;
}
};
class Body
{
private: // The variable below can't be altered from outside the class
int bodyNumber = 37265;
protected: // The method below can only be accessed by a child of Body
int vehicleBodyNumber()
{
return bodyNumber;
}
};
class Car: public Engine, public Body
{
public:
string carCode;
string model;
int getBodyNb()
{
int carBodyNb = vehicleBodyNumber();
return carBodyNb;
}
int getEngineNb()
{
int carEngineNb = vehicleEngineNumber();
return carEngineNb;
}
};
int main()
{
Car ferrari;
ferrari.model = "Ferrari F430";
ferrari.carCode = "fri";
cout << "This " << ferrari.model << " id number is: " <<
ferrari.getEngineNb() << endl <<
ferrari.getBodyNb() << ferrari.carCode;
}
Exemple 3 : Diamond Problem
#include <iostream>
#include <string>
using namespace std;
class SteelComponent
{
public:
float steelPrice = 1.5;
};
class MotorPoweredVehicle: public SteelComponent
{
public:
float enginePrice = steelPrice * 380;
};
class WheeledVehicle: public SteelComponent
{
public:
float wheelPrice = steelPrice * 500;
};
class Car: public MotorPoweredVehicle, public WheeledVehicle
{
public:
string model;
float carSteelCost;
};
int main()
{
Car opel;
opel.model = "Opel Corsa";
opel.carSteelCost = opel.wheelPrice + opel.enginePrice;
cout << "Total price for steel was: $" << opel.carSteelCost;
cout << " and the steel price was $" << opel.steelPrice << "/kg";
}