Inheritance is one of the four fundamental pillars of Object-Oriented Programming (OOP), alongside encapsulation, abstraction, and polymorphism. It allows a new class to acquire the properties and behaviors of an existing class. This powerful concept helps developers create hierarchical relationships among classes and promotes code reusability, modularity, and maintainability in C++ programs.
In simple terms, inheritance allows you to define a class in terms of another class. The existing class is known as the base class (or parent class), while the newly derived class is called the derived class (or child class). The derived class inherits attributes and methods of the base class and can also have additional members of its own.
Introduction to Inheritance
When building software systems, you often encounter situations where several entities share common characteristics or behaviors. Instead of redefining the same data members and functions multiple times, you can define them once in a base class and then create specialized classes that inherit those features. This approach avoids redundancy and makes code cleaner and easier to maintain.
For example, consider a system that models different types of vehicles. All vehicles share certain properties such as speed, fuel capacity, and model number. You can define a base class named Vehicle that contains these common attributes. Then you can create derived classes such as Car, Bike, and Truck that inherit the common features of the Vehicle class but also define their own specific behaviors or properties.
Example
#include <iostream>
using namespace std;
class Vehicle {
public:
int speed;
int fuelCapacity;
void displayInfo() {
cout << "Speed: " << speed << " km/h" << endl;
cout << "Fuel Capacity: " << fuelCapacity << " liters" << endl;
}
};
class Car : public Vehicle {
public:
int numberOfDoors;
void showDetails() {
cout << "Number of doors: " << numberOfDoors << endl;
}
};
int main() {
Car car1;
car1.speed = 180;
car1.fuelCapacity = 50;
car1.numberOfDoors = 4;
car1.displayInfo();
car1.showDetails();
return 0;
}
Output
Speed: 180 km/h
Fuel Capacity: 50 liters
Number of doors: 4
In this example, the Car class inherits all the members of the Vehicle class. The displayInfo() function and the variables speed and fuelCapacity are inherited by the Car class. This demonstrates that inheritance allows you to reuse code from one class in another.
Terminology Used in Inheritance
Before exploring deeper, it is important to understand the terminology associated with inheritance:
- Base Class: The class whose properties and methods are inherited by another class. Also called the parent or superclass.
- Derived Class: The class that inherits members from another class. Also called the child or subclass.
- Access Specifiers: These determine how the members of a base class are accessible in a derived class. The main access specifiers are
public
,protected
, andprivate
.
The syntax of inheritance in C++ is straightforward.
Syntax
class DerivedClassName : access-specifier BaseClassName {
// Members of the derived class
};
Here, the access-specifier
determines how the base class members are inherited.
Access Control in Inheritance
C++ provides three main types of inheritance access control: public, protected, and private. These control how the members of the base class are treated inside the derived class.
1. Public Inheritance
When a class is publicly inherited, the public members of the base class remain public in the derived class, and the protected members remain protected. The private members of the base class are not directly accessible but can still be used through public or protected member functions.
Public inheritance represents an “is-a” relationship, meaning the derived class is a specialized version of the base class.
Example
class Vehicle {
public:
int speed;
void start() {
cout << "Vehicle started" << endl;
}
};
class Car : public Vehicle {
public:
void honk() {
cout << "Car horn" << endl;
}
};
Here, Car publicly inherits from Vehicle, meaning a Car is a Vehicle.
2. Protected Inheritance
When a class inherits another class protectedly, the public and protected members of the base class become protected members of the derived class. This type of inheritance is rarely used because it restricts the accessibility of base class members.
3. Private Inheritance
When a class privately inherits from another class, all the public and protected members of the base class become private members of the derived class. Private inheritance implies a “has-a” or “implemented-in-terms-of” relationship rather than an “is-a” relationship.
Types of Inheritance in C++
C++ supports several types of inheritance to accommodate different design requirements. These types define how classes relate to one another and how properties flow through the class hierarchy.
The five major types of inheritance are:
- Single Inheritance
- Multiple Inheritance
- Multilevel Inheritance
- Hierarchical Inheritance
- Hybrid Inheritance
Let us discuss each type in detail.
Single Inheritance
Single inheritance is the simplest form of inheritance where a single derived class inherits from a single base class. It establishes a one-to-one relationship between the parent and child classes.
Example
#include <iostream>
using namespace std;
class Vehicle {
public:
void message() {
cout << "This is a vehicle." << endl;
}
};
class Car : public Vehicle {
public:
void display() {
cout << "This is a car." << endl;
}
};
int main() {
Car c;
c.message();
c.display();
return 0;
}
Output
This is a vehicle.
This is a car.
This is an example of single inheritance where one derived class inherits from a single base class.
Multiple Inheritance
Multiple inheritance allows a class to inherit from more than one base class. This means that the derived class inherits the properties and behaviors of multiple parent classes.
While multiple inheritance provides flexibility, it can also lead to ambiguity if two or more base classes contain members with the same name. This problem is commonly referred to as the diamond problem.
Example
#include <iostream>
using namespace std;
class Engine {
public:
void startEngine() {
cout << "Engine started." << endl;
}
};
class Wheels {
public:
void rotate() {
cout << "Wheels are rotating." << endl;
}
};
class Car : public Engine, public Wheels {
public:
void drive() {
cout << "Car is driving." << endl;
}
};
int main() {
Car myCar;
myCar.startEngine();
myCar.rotate();
myCar.drive();
return 0;
}
Output
Engine started.
Wheels are rotating.
Car is driving.
Here, the Car class inherits from both the Engine and Wheels classes, demonstrating multiple inheritance.
Multilevel Inheritance
In multilevel inheritance, a class is derived from another derived class. In other words, the inheritance chain goes through multiple levels.
For example, a Vehicle class may be inherited by a Car class, which in turn is inherited by a SportsCar class. Each level in the hierarchy builds upon the previous one.
Example
#include <iostream>
using namespace std;
class Vehicle {
public:
void info() {
cout << "This is a vehicle." << endl;
}
};
class Car : public Vehicle {
public:
void type() {
cout << "This is a car." << endl;
}
};
class SportsCar : public Car {
public:
void category() {
cout << "This is a sports car." << endl;
}
};
int main() {
SportsCar s;
s.info();
s.type();
s.category();
return 0;
}
Output
This is a vehicle.
This is a car.
This is a sports car.
In this example, SportsCar inherits from Car, which itself inherits from Vehicle, forming a multilevel inheritance chain.
Hierarchical Inheritance
In hierarchical inheritance, multiple derived classes inherit from a single base class. This allows several subclasses to share the common functionality of the base class.
Example
#include <iostream>
using namespace std;
class Vehicle {
public:
void start() {
cout << "Vehicle started." << endl;
}
};
class Car : public Vehicle {
public:
void carFeature() {
cout << "Car feature: Air Conditioning." << endl;
}
};
class Bike : public Vehicle {
public:
void bikeFeature() {
cout << "Bike feature: Two wheels." << endl;
}
};
int main() {
Car c;
Bike b;
c.start();
c.carFeature();
b.start();
b.bikeFeature();
return 0;
}
Output
Vehicle started.
Car feature: Air Conditioning.
Vehicle started.
Bike feature: Two wheels.
Here, both Car and Bike inherit from the same base class Vehicle. This demonstrates hierarchical inheritance.
Hybrid Inheritance
Hybrid inheritance is a combination of two or more types of inheritance. It is often used in complex class hierarchies but must be used carefully because it can lead to ambiguity issues, especially in the presence of multiple inheritance.
The virtual base class mechanism in C++ helps resolve such ambiguities. When two derived classes inherit the same base class and a third class inherits both derived classes, the virtual keyword ensures only one instance of the common base class is shared.
Example
#include <iostream>
using namespace std;
class Vehicle {
public:
void info() {
cout << "This is a vehicle." << endl;
}
};
class Engine : virtual public Vehicle {
public:
void engineInfo() {
cout << "Engine capacity: 2000cc." << endl;
}
};
class Wheels : virtual public Vehicle {
public:
void wheelInfo() {
cout << "Wheels: 4." << endl;
}
};
class Car : public Engine, public Wheels {
public:
void display() {
cout << "This is a car derived from Vehicle, Engine, and Wheels." << endl;
}
};
int main() {
Car obj;
obj.info();
obj.engineInfo();
obj.wheelInfo();
obj.display();
return 0;
}
Output
This is a vehicle.
Engine capacity: 2000cc.
Wheels: 4.
This is a car derived from Vehicle, Engine, and Wheels.
The virtual inheritance ensures that only one copy of the base class Vehicle is shared among all derived classes, preventing the diamond problem.
Constructors and Inheritance
When a derived class object is created, the base class constructor is automatically called first, followed by the derived class constructor. This ensures that the base part of the object is properly initialized before the derived part.
Similarly, destructors are called in reverse order — first the derived class destructor, then the base class destructor.
Example
#include <iostream>
using namespace std;
class Vehicle {
public:
Vehicle() {
cout << "Vehicle constructor called." << endl;
}
~Vehicle() {
cout << "Vehicle destructor called." << endl;
}
};
class Car : public Vehicle {
public:
Car() {
cout << "Car constructor called." << endl;
}
~Car() {
cout << "Car destructor called." << endl;
}
};
int main() {
Car obj;
return 0;
}
Output
Vehicle constructor called.
Car constructor called.
Car destructor called.
Vehicle destructor called.
This order ensures proper initialization and destruction of objects.
Advantages of Inheritance
- Code Reusability
Inheritance enables you to reuse existing code. You can define common functionality once in a base class and reuse it across multiple derived classes. - Simplicity
The hierarchical structure makes the code simpler and easier to understand. - Extensibility
It is easy to extend existing functionality by creating new derived classes. - Maintainability
Changes made in the base class automatically reflect in all derived classes, reducing maintenance effort. - Polymorphism Support
Inheritance lays the foundation for runtime polymorphism through virtual functions.
Disadvantages of Inheritance
Although inheritance is powerful, it must be used carefully. Improper use can lead to tightly coupled and difficult-to-maintain code.
- Increased Complexity
Deep inheritance hierarchies can become difficult to manage. - Tight Coupling
Changes in the base class can unintentionally affect derived classes. - Ambiguity in Multiple Inheritance
When the same member exists in multiple base classes, ambiguity arises, which needs to be resolved using scope resolution or virtual inheritance.
Real-Life Analogy of Inheritance
Think of inheritance like a family tree. A child inherits characteristics from the parents but also has traits of its own. For example, all cars and bikes are types of vehicles. They share some features, like the ability to move, but each has unique features such as the number of wheels or the type of engine.
Similarly, in programming, inheritance allows new classes to reuse and extend the behavior of existing ones.
When to Use Inheritance
- When several classes share common properties and behaviors.
- When a hierarchical relationship exists between entities.
- When you want to implement polymorphism and reusability.
- When existing code can serve as a foundation for new functionalities.
When Not to Use Inheritance
- When there is no true “is-a” relationship between the base and derived classes.
- When the inheritance leads to overly complex hierarchies.
- When composition (using objects within other objects) is a better fit.
Leave a Reply