GuideC++

Inheritance

OOP Basics / Inheritance

Concept Lesson
Intermediate
4 min

Learning Objective

Understand Inheritance well enough to explain it, recognize it in C++, and apply it in a small task.

Why It Matters

This concept is part of the foundation that later lessons and projects assume you already understand.

Table Of ContentsWhat Is InheritanceBenefitsIs-A RelationshipBasic Syntax
Private notes
0/8000

Notes stay private to your browser until account sync is configured.

Guide
1 min read18 headings

Inheritance in C++

Table of Contents

  1. What is Inheritance
  2. Basic Syntax
  3. Access Specifiers in Inheritance
  4. Constructor and Destructor Order
  5. Function Overriding
  6. Types of Inheritance
  7. Multiple Inheritance
  8. Best Practices

What is Inheritance

Inheritance allows creating new classes based on existing classes:

  • Base class (parent): The class being inherited from
  • Derived class (child): The class that inherits

Benefits

BenefitDescription
Code ReuseDon't repeat common functionality
ExtensibilityAdd new features to existing classes
HierarchyModel real-world relationships
PolymorphismEnable uniform interfaces

IS-A Relationship

// A Dog IS-A Animal
class Animal { };
class Dog : public Animal { };

// A Car IS-A Vehicle
class Vehicle { };
class Car : public Vehicle { };

Basic Syntax

class Base {
public:
    void baseMethod() { }
};

class Derived : public Base {  // Inherits from Base
public:
    void derivedMethod() { }
};

// Usage
Derived d;
d.baseMethod();     // Inherited from Base
d.derivedMethod();  // Defined in Derived

Complete Example

class Animal {
protected:
    string name;
    int age;

public:
    Animal(const string& n, int a) : name(n), age(a) {}

    void eat() {
        cout << name << " is eating" << endl;
    }

    void sleep() {
        cout << name << " is sleeping" << endl;
    }
};

class Dog : public Animal {
private:
    string breed;

public:
    Dog(const string& n, int a, const string& b)
        : Animal(n, a), breed(b) {}  // Call base constructor

    void bark() {
        cout << name << " says: Woof!" << endl;
    }

    void displayInfo() {
        cout << name << " is a " << age << " year old " << breed << endl;
    }
};

// Usage
Dog dog("Buddy", 3, "Labrador");
dog.eat();        // Inherited
dog.sleep();      // Inherited
dog.bark();       // Dog-specific
dog.displayInfo();

Access Specifiers in Inheritance

Three Inheritance Modes

class Base {
public:
    int publicMember;
protected:
    int protectedMember;
private:
    int privateMember;  // Never inherited
};

class PublicDerived : public Base {
    // publicMember remains public
    // protectedMember remains protected
};

class ProtectedDerived : protected Base {
    // publicMember becomes protected
    // protectedMember remains protected
};

class PrivateDerived : private Base {
    // publicMember becomes private
    // protectedMember becomes private
};

Access Level Table

Base Memberpublic Inheritanceprotected Inheritanceprivate Inheritance
publicpublicprotectedprivate
protectedprotectedprotectedprivate
privatenot accessiblenot accessiblenot accessible

Most Common: Public Inheritance

// Public inheritance = IS-A relationship
class Shape {
public:
    virtual void draw() = 0;
};

class Circle : public Shape {  // Circle IS-A Shape
public:
    void draw() override {
        cout << "Drawing circle" << endl;
    }
};

Protected Members

class Base {
protected:
    int data;  // Accessible in derived classes

public:
    Base(int d) : data(d) {}
};

class Derived : public Base {
public:
    Derived(int d) : Base(d) {}

    void modify() {
        data = 100;  // OK: protected is accessible
    }

    int getData() const { return data; }
};

// Outside classes
Derived d(50);
// d.data = 10;  // ERROR: protected
cout << d.getData();  // OK: through public method

Constructor and Destructor Order

Order of Execution

class Base {
public:
    Base() { cout << "Base constructor" << endl; }
    ~Base() { cout << "Base destructor" << endl; }
};

class Derived : public Base {
public:
    Derived() { cout << "Derived constructor" << endl; }
    ~Derived() { cout << "Derived destructor" << endl; }
};

Derived d;
// Output:
// Base constructor      <- First
// Derived constructor   <- Second
// Derived destructor    <- First (reverse order)
// Base destructor       <- Second

Calling Base Constructor

class Person {
protected:
    string name;
    int age;

public:
    Person(const string& n, int a) : name(n), age(a) {
        cout << "Person constructor" << endl;
    }
};

class Student : public Person {
private:
    string studentId;

public:
    // Must call base constructor in initializer list
    Student(const string& n, int a, const string& id)
        : Person(n, a), studentId(id) {  // Call Person first
        cout << "Student constructor" << endl;
    }
};

Default Base Constructor

class Base {
public:
    Base() { cout << "Base default" << endl; }
    Base(int x) { cout << "Base(" << x << ")" << endl; }
};

class Derived : public Base {
public:
    // Implicitly calls Base()
    Derived() {
        cout << "Derived" << endl;
    }

    // Explicitly calls Base(int)
    Derived(int x) : Base(x) {
        cout << "Derived(" << x << ")" << endl;
    }
};

Function Overriding

Basic Override

class Animal {
public:
    void speak() {
        cout << "Animal speaks" << endl;
    }
};

class Dog : public Animal {
public:
    // Override base class method
    void speak() {
        cout << "Dog barks" << endl;
    }
};

Dog d;
d.speak();  // "Dog barks"

Calling Base Version

class Animal {
public:
    void speak() {
        cout << "Animal speaks" << endl;
    }
};

class Dog : public Animal {
public:
    void speak() {
        Animal::speak();  // Call base version
        cout << "Dog barks" << endl;
    }
};

Dog d;
d.speak();
// Output:
// Animal speaks
// Dog barks

Virtual Functions (Polymorphism)

class Shape {
public:
    virtual void draw() {  // Virtual for polymorphism
        cout << "Drawing shape" << endl;
    }

    virtual ~Shape() = default;  // Virtual destructor
};

class Circle : public Shape {
public:
    void draw() override {  // Override keyword (C++11)
        cout << "Drawing circle" << endl;
    }
};

// Polymorphic behavior
Shape* shape = new Circle();
shape->draw();  // "Drawing circle" - calls Circle::draw()
delete shape;

override and final Keywords

class Base {
public:
    virtual void foo() {}
    virtual void bar() {}
    void baz() {}  // Not virtual
};

class Derived : public Base {
public:
    void foo() override {}     // OK: overrides Base::foo
    // void bar(int) override; // ERROR: signature doesn't match
    // void baz() override;    // ERROR: Base::baz is not virtual
};

class Final : public Base {
public:
    void foo() final {}  // Cannot be overridden further
};

class TryOverride : public Final {
public:
    // void foo() override;  // ERROR: foo is final
};

Types of Inheritance

Single Inheritance

class Animal { };
class Dog : public Animal { };  // One base class

Multilevel Inheritance

class Animal { };
class Mammal : public Animal { };
class Dog : public Mammal { };  // Chain of inheritance

Hierarchical Inheritance

class Animal { };
class Dog : public Animal { };   // Multiple classes
class Cat : public Animal { };   // inherit from same base
class Bird : public Animal { };

Multiple Inheritance

class Flyable {
public:
    virtual void fly() = 0;
};

class Swimmable {
public:
    virtual void swim() = 0;
};

class Duck : public Flyable, public Swimmable {
public:
    void fly() override { cout << "Duck flying" << endl; }
    void swim() override { cout << "Duck swimming" << endl; }
};

Multiple Inheritance

Basic Syntax

class A {
public:
    void methodA() { cout << "A" << endl; }
};

class B {
public:
    void methodB() { cout << "B" << endl; }
};

class C : public A, public B {
public:
    void methodC() { cout << "C" << endl; }
};

C c;
c.methodA();  // From A
c.methodB();  // From B
c.methodC();  // From C

The Diamond Problem

class Animal {
public:
    int age;
};

class Mammal : public Animal { };
class Bird : public Animal { };

class Bat : public Mammal, public Bird {
    // Has TWO copies of Animal::age!
};

Bat b;
// b.age = 5;          // ERROR: ambiguous
b.Mammal::age = 5;     // OK: specify which
b.Bird::age = 3;       // Different copy!

Solution: Virtual Inheritance

class Animal {
public:
    int age;
};

class Mammal : virtual public Animal { };  // virtual
class Bird : virtual public Animal { };    // virtual

class Bat : public Mammal, public Bird {
    // Only ONE copy of Animal
};

Bat b;
b.age = 5;  // OK: unambiguous

Interface-like Multiple Inheritance

// Best practice: Inherit from interfaces (pure abstract classes)
class Drawable {
public:
    virtual void draw() = 0;
    virtual ~Drawable() = default;
};

class Moveable {
public:
    virtual void move(int x, int y) = 0;
    virtual ~Moveable() = default;
};

class Sprite : public Drawable, public Moveable {
public:
    void draw() override { /* ... */ }
    void move(int x, int y) override { /* ... */ }
};

Best Practices

✅ Do

// 1. Use public inheritance for IS-A relationships
class Car : public Vehicle { };  // Car IS-A Vehicle

// 2. Use virtual destructors in base classes
class Base {
public:
    virtual ~Base() = default;
};

// 3. Use override keyword
class Derived : public Base {
    void method() override { }
};

// 4. Prefer composition over inheritance when appropriate
class Engine { };
class Car {
    Engine engine;  // Car HAS-A Engine
};

// 5. Keep inheritance hierarchies shallow
// Prefer: A -> B -> C (3 levels)
// Avoid:  A -> B -> C -> D -> E -> F (6 levels)

// 6. Use protected for members that derived classes need
class Base {
protected:
    int sharedData;
};

❌ Don't

// 1. Don't inherit just for code reuse
// Use composition instead
class NotAList : public vector<int> { };  // Bad
class HasAList { vector<int> data; };     // Better

// 2. Don't forget virtual destructors
class Base {
    ~Base() { }  // BAD if inherited
};

// 3. Don't change method semantics in derived class
class Rectangle {
    virtual int area() { return width * height; }
};
class Square : public Rectangle {
    int area() override { return side * side; }  // Different formula OK
    // But don't change meaning entirely
};

// 4. Don't expose implementation through public inheritance
class Stack : public vector<int> { };  // Exposes vector's interface

// 5. Don't inherit from classes not designed for inheritance
class String : public std::string { };  // No virtual destructor!

Liskov Substitution Principle

Objects of a derived class should be usable wherever base class objects are expected:

void processAnimal(Animal& a) {
    a.eat();
    a.sleep();
}

Dog dog("Buddy", 3);
Cat cat("Whiskers", 2);

processAnimal(dog);  // OK: Dog IS-A Animal
processAnimal(cat);  // OK: Cat IS-A Animal

Quick Reference

// Base class
class Base {
public:
    Base() { }                    // Constructor
    virtual ~Base() { }           // Virtual destructor
    virtual void method() { }     // Virtual method
protected:
    int protectedData;            // Accessible in derived
private:
    int privateData;              // Not accessible in derived
};

// Derived class
class Derived : public Base {     // Public inheritance
public:
    Derived() : Base() { }        // Call base constructor

    void method() override { }    // Override virtual method

    void useProtected() {
        protectedData = 10;       // OK: protected
    }
};

// Multiple inheritance
class Multi : public Base1, public Base2 {
    // Inherits from both
};

// Virtual inheritance (diamond problem)
class VDerived : virtual public Base {
    // For diamond inheritance
};

Compile & Run

g++ -std=c++17 -Wall -Wextra examples.cpp -o examples
./examples

Skill Check

Test this lesson

Answer 4 quick questions to lock in the lesson and feed your adaptive practice queue.

--
Score
0/4
Answered
Not attempted
Status
1

Which module does this lesson belong to?

2

Which section is covered in this lesson content?

3

Which term is most central to this lesson?

4

What is the best way to use this lesson for real learning?

Your answers save locally first, then sync when account storage is available.
Practice queue