Inheritance in C++ is an OOP mechanism that allows a new class (called the derived or child class) to acquire the properties (data members) and behaviors (member functions) of an existing class (called the base or parent class), establishing an 'is-a' relationship between them. Inheritance promotes code reuse by letting common functionality be defined once in a base class and automatically shared by all derived classes, which can also add their own unique members or override inherited behavior.
Inheritance in C++
Think of inheritance like genetic traits passed from parent to child — a child inherits characteristics like eye color and height from their parents automatically, without needing to redefine them, but the child can also have their own unique traits the parents didn't have. In C++, if you have a general 'Vehicle' class with common traits like speed and fuel, a 'Car' class can 'inherit' from 'Vehicle' to automatically get those same traits and behaviors, while also adding car-specific features like 'numberOfDoors' — without rewriting the shared logic that already exists in 'Vehicle'.
In a company's HR software, a general 'Employee' base class defines shared attributes like name, ID, and salary, and common behavior like calculatePay(). Specific derived classes like 'Manager' and 'Engineer' inherit all of this automatically, while each adds its own unique attributes — 'Manager' might add 'teamSize', and 'Engineer' might add 'programmingLanguage' — without duplicating the shared employee logic in each class. In a game like Fortnite, a base 'Weapon' class defines shared behavior like reload() and fire(), while derived classes like 'AssaultRifle', 'Shotgun', and 'SniperRifle' each inherit that shared framework but override fire() with their own unique firing behavior and add their own specific stats like scope zoom or spread pattern.
Without inheritance, every class that shares common attributes and behavior with another would need to duplicate that shared code entirely, leading to massive code repetition — if a bug is found in the shared logic, it would need to be fixed in every single duplicated copy across the codebase. Inheritance lets developers define shared functionality exactly once in a base class, automatically propagate it to every derived class, and update it in a single place when changes are needed, while still allowing each derived class the flexibility to specialize or override specific behaviors — a critical feature for building large, extensible, and maintainable class hierarchies that mirror real-world categorization (like Animal → Mammal → Dog).
- Single Inheritance: The simplest and most common form, where a derived class inherits from exactly one base class (e.g., 'Dog' inherits from 'Animal'), establishing a straightforward one-to-one parent-child relationship.
- Multiple Inheritance: A derived class inherits from two or more base classes simultaneously (e.g., 'FlyingCar' inheriting from both 'Car' and 'Airplane'), a feature C++ supports (unlike many other OOP languages like Java) but which requires careful handling to avoid ambiguity issues like the 'diamond problem'.
- Multilevel Inheritance: A chain of inheritance where a class derives from another derived class, forming a hierarchy of more than two levels (e.g., 'Animal' → 'Mammal' → 'Dog'), with each level inheriting all accumulated members from every class above it in the chain.
- Hierarchical Inheritance: Multiple derived classes all inherit from the SAME single base class (e.g., 'Car', 'Truck', and 'Motorcycle' all inheriting from a common 'Vehicle' base class), allowing multiple related but distinct classes to share common functionality.
- Hybrid Inheritance: A combination of two or more types of inheritance (e.g., combining hierarchical and multiple inheritance) within the same class hierarchy, which can introduce complex ambiguity issues like the diamond problem, typically resolved in C++ using 'virtual inheritance'.
- Using private Inheritance When public Inheritance Was Intended: Writing 'class Derived : Base' without specifying an inheritance access specifier defaults to 'private' inheritance for a 'class' (unlike 'struct', which defaults to 'public'), which makes all inherited public/protected members of the base class become private in the derived class — often not what beginners intend, breaking expected access patterns like calling inherited methods from outside the derived class.
- Trying to Access Private Base Class Members Directly from a Derived Class: Members declared 'private' in the base class are NOT accessible directly in derived classes, even with 'public' inheritance — only 'protected' and 'public' base class members are accessible in derived classes. Beginners often mistakenly make base class members 'private' when they actually need derived classes to access them directly, and should use 'protected' instead.
- Forgetting That Base Class Constructors Are Not Automatically Inherited: A derived class does not automatically inherit its base class's constructors (unless explicitly using 'using BaseClass::BaseClass;' in C++11+) — the derived class must define its own constructor(s), which should explicitly call an appropriate base class constructor via the initializer list if the base class requires specific initialization parameters.
- The Diamond Problem with Multiple Inheritance: When a class inherits from two base classes that both inherit from a common ancestor class, the derived class ends up with two separate copies of the common ancestor's members, causing ambiguity when accessing them (the compiler doesn't know which copy to use). This classic issue, known as the 'diamond problem', requires using 'virtual inheritance' on the intermediate base classes to ensure only a single shared copy of the common ancestor exists.
- Not Understanding the Order of Constructor and Destructor Calls in Inheritance: Beginners sometimes assume the derived class's constructor runs before the base class's constructor, when in fact C++ always calls the BASE class constructor FIRST (before the derived class constructor body executes), and destructors run in the exact opposite order (derived class destructor first, then base class destructor), which matters when there's dependent initialization logic between the two.
- Use protected (Not private) for Base Class Members That Derived Classes Need Direct Access To: If derived classes genuinely need direct access to certain base class data members (not just through public getter/setter methods), declare those members 'protected' rather than 'private', since 'private' members are completely inaccessible to derived classes regardless of the inheritance type used.
- Always Explicitly Specify public Inheritance Unless private/protected Inheritance Is Specifically Intended: Write 'class Derived : public Base' explicitly, since the default (private inheritance for classes) is rarely what's intended and can cause confusing access errors; public inheritance correctly models the common 'is-a' relationship most beginners are trying to express.
- Favor Composition Over Multiple Inheritance When Possible: While C++ supports multiple inheritance, it introduces significant complexity (like the diamond problem) that can make class hierarchies fragile and hard to reason about — where possible, prefer composition (embedding objects of other classes as members) to achieve similar code reuse goals with a simpler, more predictable structure.
- Use Virtual Inheritance to Resolve the Diamond Problem When Multiple Inheritance Is Necessary: When a hybrid inheritance hierarchy genuinely requires multiple base classes that share a common ancestor, declare the shared base class as 'virtual' in the intermediate classes (e.g., 'class B : virtual public A') to ensure only one shared instance of the common ancestor's members exists in the final derived class, eliminating ambiguity.
Inheritance allows C++ classes to reuse and extend functionality from existing classes, modeling natural 'is-a' relationships and dramatically reducing code duplication across related classes. Understanding the different inheritance types (single, multiple, multilevel, hierarchical, hybrid), the effects of access specifiers (public/protected/private) on inherited members, proper constructor chaining, and the correct order of constructor/destructor calls are essential for building clean, extensible class hierarchies. While powerful, features like multiple inheritance require careful handling of issues like the diamond problem, and inheritance itself should always be used to model genuine 'is-a' relationships rather than misapplied wherever code reuse is desired.