C++: OOP, Inheritance

Learn how derived classes reuse and extend base class code, the five types of inheritance, access rules, constructor chaining, function overriding, and the base-class pointer.

Base Class and Derived Class

Inheritance lets one class (the derived class) automatically receive the members of another class (the base class). The derived class can then add new members or replace existing ones. This models the real-world "is-a" relationship: a Dog is an Animal, a Car is a Vehicle.

Base and Derived

The base class defines common state and behavior. The derived class inherits everything and specializes it.

  • Syntax: class Derived : public Base { ... };
  • Derived inherits all public and protected members of Base
  • Private members of Base are not directly accessible in Derived
  • The derived class can add new members and override inherited functions

Animal Base, Dog Derived

C++

Dog inherits name and eat() from Animal, then adds its own bark() method.

Types of Inheritance

C++ supports five forms of inheritance. The most common is single (one base, one derived). The others build on this by adding more base classes or more levels to the hierarchy.

Five Inheritance Types

Choose the type that matches the real-world relationship. Single and multilevel cover most use cases; multiple inheritance requires care to avoid ambiguity.

  • Single: one base, one derived: Dog : Animal
  • Multiple: one derived inherits from two or more bases: FlyingFish : Fish, Bird
  • Multilevel: a chain: Animal → Mammal → Dog
  • Hierarchical: one base, many derived: Animal → Dog, Cat, Bird
  • Hybrid: a combination of the above, e.g. multilevel plus multiple
TypeStructureExample
SingleA : BDog : Animal
MultipleA : B, CFlyingFish : Fish, Bird
MultilevelA : B, B : CElectricCar : Car : Vehicle
HierarchicalB : A and C : ADog : Animal and Cat : Animal
HybridMix of the aboveCombination of multilevel + multiple

Hierarchical Inheritance: Animal, Dog, and Cat

When one base class is inherited by multiple derived classes, that is hierarchical inheritance. Dog and Cat both inherit the common Animal interface but provide their own implementations of speak().

Hierarchical Inheritance

One base, many specialized derived classes. Each derived class adds or overrides what makes it unique.

  • Animal defines the shared interface: name, eat(), speak()
  • Dog and Cat each override speak() with their own sound
  • Both can still call the inherited eat() without redefining it
  • Adding a new animal type means adding a new derived class, not modifying Animal

Dog and Cat Inheriting from Animal

C++

eat() is shared. speak() is overridden by each derived class.

Multilevel Inheritance: Vehicle, Car, ElectricCar

In multilevel inheritance a derived class itself becomes the base for another class, forming a chain. Each level adds more specific behavior while inheriting everything from the levels above it.

Multilevel Chain

Each class in the chain is an increasingly specialized version. ElectricCar is a Car, which is a Vehicle.

  • Vehicle: the most general level, move() behavior
  • Car: adds fuel type and honk()
  • ElectricCar: adds charge() and overrides move() with a silent version
  • ElectricCar objects have access to all members across all three levels

Vehicle to Car to ElectricCar

C++

ElectricCar reaches three levels up the chain. It overrides move() and adds charge().

Multiple Inheritance

A class can inherit from more than one base class at the same time. This is useful when a type genuinely combines two independent roles, but it requires care: if both bases define a member with the same name, you must disambiguate with the scope resolution operator.

Multiple Inheritance

Use multiple inheritance only when the derived class truly is-a of both base types. Ambiguity between identical member names in two bases is a compile error until resolved explicitly.

  • Syntax: class Derived : public Base1, public Base2 { };
  • Derived inherits all public and protected members from both bases
  • If Base1 and Base2 both have void show(), call Base1::show() or Base2::show() explicitly
  • The diamond problem (both bases share a common ancestor) is resolved with virtual inheritance

Multiple Inheritance: Employee and Manager

C++

TeamLead combines both roles. It inherits work() and manage() independently.

Access Specifiers in Inheritance

The access specifier used in the inheritance declaration controls how the base class's members appear inside the derived class. public inheritance is by far the most common and preserves the original access levels.

Inheritance Access Rules

With public inheritance, the base class interface is preserved as-is. With protected or private inheritance, that interface is hidden from further derived classes or from the outside world entirely.

  • public inheritance: public stays public, protected stays protected in Derived
  • protected inheritance: public and protected both become protected in Derived
  • private inheritance: public and protected both become private in Derived
  • Base class private members are never directly accessible in any derived class, regardless of inheritance type
Base memberpublic inheritanceprotected inheritanceprivate inheritance
publicpublicprotectedprivate
protectedprotectedprotectedprivate
privatehiddenhiddenhidden

Constructor and Destructor in Inheritance

When a derived object is created, the base class constructor runs first, then the derived class constructor. When the object is destroyed, the order reverses: derived destructor first, then base destructor. Pass arguments to the base constructor using the initializer list.

Constructor Chaining

Base is always built before Derived, and torn down after. Use the initializer list to forward constructor arguments up the chain.

  • Syntax to call base constructor: Derived(args) : Base(subset_of_args) {}
  • If no base constructor is specified, the default base constructor is called automatically
  • Construction order: Base → Derived (for three levels: grandBase → Base → Derived)
  • Destruction order is the exact reverse: Derived → Base

Constructor and Destructor Chain

C++

Employee constructor runs first, then Manager. On exit, Manager destructor runs first, then Employee.

Function Overriding

Function overriding occurs when a derived class defines a function with the same name and parameters as one in the base class. The derived version replaces the base version when called on a derived object. Use BaseClass::functionName() to explicitly call the base version from inside the derived class.

Function Overriding

The derived function hides the base function for calls on derived objects. The base version is still reachable through the scope resolution operator.

  • Same name, same parameters as the base function
  • Called on a derived object: the derived version wins
  • Call base version explicitly: Base::speak() inside the derived function
  • Without the virtual keyword, this is static (compile-time) overriding, not polymorphism

Function Overriding in a Shape Hierarchy

C++

Each derived class overrides describe() and optionally calls the base version with Shape::describe().

Base Class Pointer to Derived Object

A pointer of base class type can legally point to a derived class object. This is called upcasting. Through a base pointer, only the base class interface is visible. This is the foundation of polymorphism, the same pointer type can refer to different kinds of objects at runtime.

Upcasting

Assigning a derived object's address to a base pointer is always safe. The pointer sees only the base class portion of the object unless virtual functions are involved.

  • Animal* ptr = &dog; legal, no cast required
  • Through ptr, only Animal's members are accessible
  • Without virtual, ptr->speak() calls Animal::speak(), not Dog::speak()
  • Add the virtual keyword to enable runtime dispatch, covered in the Polymorphism topic

Base Pointer to Derived Object

C++

ptr holds the address of a Dog but calls Animal::speak(). The virtual keyword (covered in Polymorphism) changes this behavior.

Object Slicing

Object slicing happens when a derived class object is assigned to a base class variable by value (not a pointer or reference). The derived-class-specific data is silently discarded because the base variable only has room for the base portion. This is a common source of bugs and is usually unintentional.

Object Slicing

Assigning by value slices off the derived part. Always use pointers or references when storing derived objects in base class variables.

  • Animal a = dog; copies only the Animal portion of dog into a
  • Any data members added by Dog are lost silently
  • a.speak() calls Animal::speak(), the Dog identity is gone
  • Avoid slicing by using Animal* or Animal& instead of a plain Animal variable

Object Slicing vs Reference

C++

Assigning by value silently strips breed. Using a reference avoids slicing entirely.

Knowledge Check

1. What does a derived class inherit from its base class?

2. Which inheritance type does C++ use when a class inherits from two or more base classes?

3. When a derived class object is created, which constructor runs first?

4. What is function overriding?

5. How do you explicitly call the base class version of an overridden function from inside the derived class?

6. What is object slicing?

7. With public inheritance, protected members of the base class become __ in the derived class.

8. A base class pointer can point to a derived class object. What is this called?

9. In what order are destructors called in an inheritance hierarchy?