Inheritance is a fundamental concept in object-oriented programming (OOP) that allows a class to inherit properties and behavior from another class. In C++, inheritance enables the creation of hierarchical relationships between classes, promoting code reuse and enhancing code organization.
In basic inheritance, a derived class (also known as a subclass or child class) inherits properties and methods from a base class (also known as a superclass or parent class). This means that the derived class can access the members of the base class.
#include
// Base class
class Animal {
public:
void eat() {
std::cout << "Animal is eating." << std::endl;
}
};
// Derived class
class Dog : public Animal {
public:
void bark() {
std::cout << "Dog is barking." << std::endl;
}
};
int main() {
Dog dog;
dog.eat(); // Accessing base class method
dog.bark(); // Accessing derived class method
return 0;
}
// output //
Animal is eating.
Dog is barking.
In this example, we have a base class Animal
with a method eat()
. We also have a derived class Dog
which inherits from Animal
. The Dog
class has its own method bark()
. In the main()
function, we create an instance of Dog
and demonstrate accessing both base class and derived class methods.
#include
// Base class
class Shape {
public:
void draw() {
std::cout << "Drawing shape." << std::endl;
}
};
// Derived class
class Circle : public Shape {
public:
void drawCircle() {
std::cout << "Drawing circle." << std::endl;
}
};
int main() {
Circle circle;
circle.draw(); // Accessing base class method
circle.drawCircle(); // Accessing derived class method
return 0;
}
// output //
Drawing shape.
Drawing circle.
In this example, the Circle
class inherits publicly from the Shape
class, demonstrating single inheritance. The Circle
class can access the draw()
method from the Shape
class and define its own methods.
#include
// Base class 1
class Animal {
public:
void breathe() {
std::cout << "Animal breathes." << std::endl;
}
};
// Base class 2
class Mammal {
public:
void eat() {
std::cout << "Mammal eats." << std::endl;
}
};
// Derived class inheriting from multiple base classes
class Dog : public Animal, public Mammal {
public:
void bark() {
std::cout << "Dog barks." << std::endl;
}
};
int main() {
Dog dog;
dog.breathe(); // Accessing method from Animal
dog.eat(); // Accessing method from Mammal
dog.bark(); // Accessing method from Dog
return 0;
}
// output //
Animal breathes.
Mammal eats.
Dog barks.
In this example, the Dog
class inherits from both the Animal
and Mammal
classes, demonstrating multiple inheritance. The Dog
class can access methods from both base classes along with its own methods.
#include
// Base class
class Vehicle {
public:
void start() {
std::cout << "Vehicle started." << std::endl;
}
};
// Derived class
class Car : public Vehicle {
public:
void drive() {
std::cout << "Car is being driven." << std::endl;
}
};
// Further derived class
class Sedan : public Car {
public:
void park() {
std::cout << "Sedan is parked." << std::endl;
}
};
int main() {
Sedan sedan;
sedan.start(); // Accessing method from Vehicle
sedan.drive(); // Accessing method from Car
sedan.park(); // Accessing method from Sedan
return 0;
}
// output //
Vehicle started.
Car is being driven.
Sedan is parked.
In this example, the Sedan
class inherits from the Car
class, which in turn inherits from the Vehicle
class, demonstrating multilevel inheritance. The Sedan
class can access methods from both Car
and Vehicle
classes.
#include
// Base class
class Shape {
public:
void draw() {
std::cout << "Drawing shape." << std::endl;
}
};
// Derived classes
class Circle : public Shape {
public:
void drawCircle() {
std::cout << "Drawing circle." << std::endl;
}
};
class Rectangle : public Shape {
public:
void drawRectangle() {
std::cout << "Drawing rectangle." << std::endl;
}
};
int main() {
Circle circle;
circle.draw(); // Accessing method from Shape
circle.drawCircle(); // Accessing method from Circle
Rectangle rectangle;
rectangle.draw(); // Accessing method from Shape
rectangle.drawRectangle(); // Accessing method from Rectangle
return 0;
}
// output //
Drawing shape.
Drawing circle.
Drawing shape.
Drawing rectangle.
In this example, both the Circle
and Rectangle
classes inherit from the Shape
class, demonstrating hierarchical inheritance. Each derived class specializes in drawing a specific shape while sharing the common drawing functionality from the base class.
Hybrid inheritance is a combination of multiple inheritance and multilevel inheritance. It involves a mix of inheriting from multiple base classes and deriving classes from other derived classes. This inheritance model provides a high level of flexibility but can also introduce complexity and potential ambiguity.
#include
// Base class 1
class Animal {
public:
void breathe() {
std::cout << "Animal breathes." << std::endl;
}
};
// Base class 2
class Vehicle {
public:
void start() {
std::cout << "Vehicle started." << std::endl;
}
};
// Intermediate derived class
class Mammal : public Animal {
public:
void eat() {
std::cout << "Mammal eats." << std::endl;
}
};
// Derived class inheriting from multiple base classes and intermediate derived class
class Dog : public Mammal, public Vehicle {
public:
void bark() {
std::cout << "Dog barks." << std::endl;
}
};
int main() {
Dog dog;
dog.breathe(); // Accessing method from Animal (through Mammal)
dog.start(); // Accessing method from Vehicle
dog.eat(); // Accessing method from Mammal
dog.bark(); // Accessing method from Dog
return 0;
}
// output //
Animal breathes.
Vehicle started.
Mammal eats.
Dog barks.
In this example, the Dog
class inherits from both the Mammal
class (which in turn inherits from the Animal
class) and the Vehicle
class, demonstrating hybrid inheritance. The Dog
class can access methods from multiple base classes and an intermediate derived class.
Access specifiers play a crucial role in controlling the visibility of base class members in the derived class when using inheritance in C++. There are three access specifiers: public
, protected
, and private
. Each specifier determines how the members of the base class are accessible in the derived class.
#include
// Base class
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
// Derived class
class Derived : public Base {
public:
void accessBaseMembers() {
publicMember = 10; // Accessible
protectedMember = 20; // Accessible
// privateMember = 30; // Not accessible
}
};
int main() {
Derived derivedObj;
derivedObj.accessBaseMembers();
return 0;
}
In this example, the Derived
class inherits publicly from the Base
class. The publicMember
and protectedMember
of the Base
class are accessible within the Derived
class. However, the privateMember
of the Base
class is not accessible directly in the Derived
class.
#include
// Base class
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
// Derived class
class Derived : protected Base {
public:
void accessBaseMembers() {
publicMember = 10; // Accessible
protectedMember = 20; // Accessible
// privateMember = 30; // Not accessible
}
};
int main() {
Derived derivedObj;
derivedObj.accessBaseMembers();
return 0;
}
In this example, the Derived
class inherits protectedly from the Base
class. Both publicMember
and protectedMember
of the Base
class become protected members of the Derived
class. The privateMember
of the Base
class remains inaccessible in the Derived
class.
#include
// Base class
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
// Derived class
class Derived : private Base {
public:
void accessBaseMembers() {
// publicMember = 10; // Not accessible
// protectedMember = 20; // Not accessible
// privateMember = 30; // Not accessible
}
};
int main() {
Derived derivedObj;
// derivedObj.accessBaseMembers(); // Not accessible
return 0;
}
In this example, the Derived
class inherits privately from the Base
class. All members of the Base
class become private in the Derived
class, meaning they are not directly accessible outside the class.
Beyond basic inheritance, C++ offers several advanced concepts to enhance the flexibility and functionality of inheritance:
Virtual Inheritance: When a class inherits virtually from a base class, it ensures that only one instance of the base class exists in the derived class hierarchy, resolving issues such as the “diamond problem” in multiple inheritance.
Abstract Base Classes: Abstract classes are classes that cannot be instantiated and contain one or more pure virtual functions. They serve as interfaces, defining a common protocol for derived classes to implement.
Interface Inheritance: C++ does not have a native interface keyword like some other languages, but interface-like behavior can be achieved through abstract base classes containing only pure virtual functions.
Final Keyword: In C++11 and later, the final
keyword can be used to prevent a class from being inherited further or a virtual function from being overridden in derived classes.
The “diamond problem” is a common issue that arises in multiple inheritance scenarios, particularly in languages like C++ that support it. It occurs when a class inherits from two or more classes that have a common ancestor. This common ancestor can lead to ambiguity in the derived class due to the presence of duplicate inherited members.
#include
// Base class
class Animal {
public:
void breathe() {
std::cout << "Animal breathes." << std::endl;
}
};
// Intermediate base class 1
class Mammal : public Animal {
public:
void eat() {
std::cout << "Mammal eats." << std::endl;
}
};
// Intermediate base class 2
class Bird : public Animal {
public:
void fly() {
std::cout << "Bird flies." << std::endl;
}
};
// Derived class inheriting from multiple base classes
class Bat : public Mammal, public Bird {
public:
void nocturnal() {
std::cout << "Bat is nocturnal." << std::endl;
}
};
int main() {
Bat bat;
bat.breathe(); // Accessing method from which base class? Ambiguity!
return 0;
}
In this example, the Bat
class inherits from both the Mammal
class and the Bird
class, which both inherit from the Animal
class. When we create an instance of the Bat
class and try to call the breathe()
method, it’s ambiguous to the compiler which breathe()
method it should call, from the Mammal
class or the Bird
class.
In Above example this Animal class comes two times in Bat , For example
Animal – breathe()
Mammal – breathe (), eat()
Bird – breathe(), fly()
Bat – breathe() , eat() , breathe(), fly()
So here two times breathe() function got duplicated so now compiler is confused which function to be called.
To resolve the diamond problem, C++ provides a concept called virtual inheritance. By using virtual inheritance, we can ensure that there is only one instance of the common base class in the hierarchy, avoiding the duplication of inherited members.
#include
// Base class with virtual inheritance
class Animal {
public:
void breathe() {
std::cout << "Animal breathes." << std::endl;
}
};
// Intermediate base classes with virtual inheritance
class Mammal : virtual public Animal {
public:
void eat() {
std::cout << "Mammal eats." << std::endl;
}
};
class Bird : virtual public Animal {
public:
void fly() {
std::cout << "Bird flies." << std::endl;
}
};
// Derived class inheriting from multiple base classes with virtual inheritance
class Bat : public Mammal, public Bird {
public:
void nocturnal() {
std::cout << "Bat is nocturnal." << std::endl;
}
};
int main() {
Bat bat;
bat.breathe(); // Calls the breathe() method from the common ancestor Animal
return 0;
}
// output //
Animal breathes.
In this modified example, we use virtual inheritance for both the Mammal
and Bird
classes. This ensures that there is only one instance of the Animal
class in the hierarchy, resolving the ambiguity. Now, when we call the breathe()
method on an instance of the Bat
class, it calls the breathe()
method from the common ancestor Animal
class.
Virtual inheritance is a mechanism provided by C++ to resolve the “diamond problem” that occurs in multiple inheritance scenarios. The diamond problem arises when a class inherits from two or more classes that have a common ancestor. This common ancestor can lead to ambiguity in the derived class due to the presence of duplicate inherited members.
Virtual inheritance allows us to ensure that there is only one instance of a common base class in the inheritance hierarchy, thus resolving the ambiguity and preventing issues such as duplicate member inheritance.
Declaration of Virtual Inheritance: To declare virtual inheritance, we use the virtual
keyword before the base class name in the derived class declaration.
class Derived : virtual public Base {
// Derived class definition
};
In this example, Derived
virtually inherits from Base
.
Single Instance of the Base Class: With virtual inheritance, there is only one instance of the virtual base class in the entire inheritance hierarchy, regardless of how many times it appears in the inheritance graph.
Resolving Ambiguity: When a derived class has multiple paths to access the same base class (e.g., through multiple inheritance), the compiler resolves any potential ambiguity by ensuring that all paths lead to the single instance of the virtual base class.
Construction and Destruction Order: Virtual inheritance affects the construction and destruction order of objects. The virtual base class is initialized by the most derived class in the inheritance hierarchy and is constructed before any non-virtual base classes. Similarly, the virtual base class is destroyed after all other non-virtual base classes.
class Base {
// Base class definition
};
class Derived1 : virtual public Base {
// Derived1 class definition
};
class Derived2 : virtual public Base {
// Derived2 class definition
};
class MostDerived : public Derived1, public Derived2 {
// MostDerived class definition
};
In this example, Base
is constructed before Derived1
and Derived2
in the MostDerived
class constructor.
Use Cases: Virtual inheritance is typically used in situations where we have a diamond-shaped inheritance hierarchy and want to avoid duplication of inherited members and resolve ambiguity. It is commonly employed in design patterns such as the “Composite” and “Visitor” patterns.
Inheritance is a versatile mechanism in C++ that allows for the creation of complex class hierarchies, enabling code reuse, modularity, and polymorphism. By understanding both the basic and advanced concepts of inheritance, developers can design elegant and maintainable software solutions that leverage the full power of object-oriented programming. Happy coding !❤️