Inheritance & Polymorphism¶
Inheritance and polymorphism are the two pillars of object-oriented code reuse and abstraction. Inheritance allows a new class to inherit attributes and behaviors from an existing class, while polymorphism enables treated objects of different types to be interacted with through a unified interface.
1. Inheritance Basics¶
In C++, you inherit from a base class using a colon syntax. The inheritance mode (public, protected, or private) determines how the inherited members are exposed to the outside world.
Access Control in Inheritance¶
| Base Member Access | public Inheritance |
protected Inheritance |
private Inheritance |
|---|---|---|---|
public |
Remains public |
Becomes protected |
Becomes private |
protected |
Remains protected |
Becomes protected |
Becomes private |
private |
Hidden (accessible only via base class functions) | Hidden | Hidden |
publicinheritance: Models an "IS-A" relationship (e.g. a Camera is a Device). This is by far the most common type of inheritance.private/protectedinheritance: Models "implemented in terms of." It is rarely used, as composition (having a member variable of the class type) is usually preferred.
2. Polymorphism and Virtual Functions¶
Polymorphism ("many forms") allows a base class pointer or reference to call member functions of a derived class at runtime. This dynamic behavior is triggered by marking functions with the virtual keyword.
The Under the Hood Mechanism: Vtables and Vptrs¶
Dynamic polymorphism is implemented using a Virtual Table (vtable). - Every class that declares or inherits a virtual function has a hidden vtable, which is an array of function pointers pointing to the virtual methods. - Every object instance of that class contains a hidden pointer called vptr (virtual pointer), which points to the class's vtable.
When calling shape.draw(), the program dereferences the object's vptr, locates the draw() entry in the vtable, and jumps to that function pointer. This adds a slight pointer-chasing overhead, which is the trade-off for dynamic flexibility.
3. Abstract Classes and Pure Virtual Functions¶
An Abstract Class defines an interface (contract) that derived classes must implement. You cannot instantiate an abstract class directly.
A function is declared pure virtual by appending = 0 to its declaration.
4. The Critical Rule of Virtual Destructors¶
If a class has at least one virtual function, its destructor must be declared virtual.
If you delete a derived object using a pointer to its base class, and the base class has a non-virtual destructor, only the base class destructor is executed. The derived class's destructor is bypassed, leading to Undefined Behavior and major memory/resource leaks.
The Fix:
5. Controlling Overriding: final¶
You can restrict inheritance and overrides using the final keyword:
- Final Class: Prevents other classes from inheriting from it.
- Final Function: Prevents derived classes from overriding that specific virtual function further down the hierarchy.
6. Casting in Inherited Hierarchies¶
C++ provides explicit cast operators to traverse class hierarchies safely.
static_cast: Performs fast compile-time casts up or down a hierarchy. It does not check types at runtime. Using it to downcast to an incorrect derived class leads to undefined behavior.dynamic_cast: Performs safe runtime checks (downcasting). It checks the runtime type information (RTTI) of the object.- If casting a pointer fails, it returns
nullptr. - If casting a reference fails, it throws a
std::bad_castexception. - Note:
dynamic_castonly works on polymorphic classes (classes containing at least one virtual function).
- If casting a pointer fails, it returns
Related Guides¶
- Classes and Objects — Encapsulation, constructors, and access specifiers.
- Templates — Compile-time polymorphism vs runtime polymorphism.