← Back to all posts

14 C++ Notes

Inheritance

C++ Notes


What Is Inheritance

C++
class Person {
public:
    std::string name;
    int age;
};

class Player : public Person { // public inheritance
public:
    int score;
};

Protected Members

Inheritance Types

Parent Memberpublic inheritanceprotected inheritanceprivate inheritance
public memberStays publicBecomes protectedBecomes private
protected memberStays protectedStays protectedBecomes private
private memberInaccessible in childInaccessible in childInaccessible in child

Private members of the parent are never directly accessible in the child, regardless of inheritance type. They exist inside the object in memory, but the language blocks all direct access to them.

Resurrecting Members with using

C++
class Child : private Person {
protected:
    using Person::age;     // resurrect age as protected
    using Person::getAge;  // resurrect method (all overloads)
};

Object Building Order with Inheritance

When a child object is created, the parent part of the object must be built before the child part. This order is fixed by the compiler and cannot be changed.

Construction Order
  1. Parent constructor runs first
  2. Child constructor runs after
Destruction Order (reversed)
  1. Child destructor runs first
  2. Parent destructor runs after

Destructors are always called automatically in reverse order of construction. You never manually call a base destructor, the compiler handles it.

Constructors with Inheritance

C++
class Parent {
protected:
    std::string name;
    int age;
public:
    Parent(std::string name, int age)
    {
        this->name = name;
        this->age  = age;
    }
};

class Child : public Parent {
    double salary;
public:
    Child(std::string name, int age, double salary)
        : Parent(name, age) // call base constructor in initializer list
    {
        this->salary = salary;
    }
};

// in main:
Child child("Hassan", 21, 50000);

Default Copy Constructor

Java BehaviorC++ Behavior
Objects always live on the heap. Copying a reference copies the reference, not the object. Both variables end up pointing to the same object, a shallow copy by default. Objects can live on the stack or the heap. The default copy copies actual data member by member. If the object has a stack array, the copy gets its own separate array, no shared memory.

The default copy constructor works for heap objects too. Copying depends on the class definition, not on where the object lives. Copying a heap object: Class* obj2{ new Class(*obj1) };, note the dereference, you pass the object itself, not the pointer.

Custom Copy Constructor

C++
class Child : public Parent {
    int y;
public:
    Child(int x, int y) : Parent(x), y(y) {}

    // custom copy constructor
    Child(const Child& other)
        : Parent(other), // slices and copies the base part
          y(other.y)      // copies child-specific members
    {
        // deep copy logic for raw pointers goes here if needed
    }
};

Inheriting Base Constructors

C++
class Parent {
public:
    Parent(int x) { std::cout << "Parent constructor\n"; }
};

class Child : public Parent {
public:
    using Parent::Parent; // inherit all parent constructors
};

// in main:
Child obj(10); // calls Parent(int x) through inheritance

Use inherited constructors carefully. They only know about parent members, if the child has its own data members without default values, those will be left uninitialized.

Inheriting with Destructors

Forgetting virtual on the base destructor is one of the most common bugs in C++ inheritance. Given Person* p = new Player(...); followed by delete p;, without virtual ~Person() the Player destructor never runs. Any heap memory the Player allocated in its own constructor leaks permanently, with no error to warn you.

Reusing Names: Hiding vs Overriding


Code Example: main.cpp

C++
#include <iostream>
#include <string>
using namespace std;

// ============================================================
// BASE CLASS
// ============================================================
class Person {
private:
    string ssn;          // private: NEVER directly accessible in child

protected:
    int age;             // protected: accessible in child class

public:
    string name;         // public: accessible everywhere

    Person(string name, int age, string ssn)
        : name(name), age(age), ssn(ssn)
    {
        cout << "Person constructor\n";
    }

    Person(const Person& other)
        : name(other.name), age(other.age), ssn(other.ssn)
    {
        cout << "Person copy constructor\n";
    }

    virtual ~Person() { cout << "Person destructor\n"; }

    int getAge() const { return age; }

    void display() const {
        cout << "Person: " << name << ", Age: " << age << endl;
    }
};

// ============================================================
// PUBLIC INHERITANCE
// ============================================================
class Player : public Person {
private:
    int score;

public:
    Player(string name, int age, string ssn, int score)
        : Person(name, age, ssn), score(score) // base constructor called first
    {
        cout << "Player constructor\n";
    }

    // custom copy constructor: base part copied via slicing
    Player(const Player& other)
        : Person(other), score(other.score)
    {
        cout << "Player copy constructor\n";
    }

    ~Player() { cout << "Player destructor\n"; }

    // name hiding (not virtual overriding)
    void display() const {
        cout << "Player: " << name
             << ", Age: "   << getAge()
             << ", Score: " << score << endl;
    }
};

// ============================================================
// PROTECTED INHERITANCE
// ============================================================
class ProtectedChild : protected Person {
public:
    ProtectedChild(string name, int age, string ssn)
        : Person(name, age, ssn) {}

    void show() {
        cout << "ProtectedChild name: " << name << endl; // name is now protected here
    }
};

// ============================================================
// PRIVATE INHERITANCE + USING (resurrecting members)
// ============================================================
class PrivateChild : private Person {
protected:
    using Person::age;    // resurrected as protected
    using Person::getAge; // resurrected method

public:
    PrivateChild(string name, int age, string ssn)
        : Person(name, age, ssn) {}

    void show() {
        cout << "PrivateChild age: " << getAge() << endl;
    }
};

// ============================================================
// INHERITING BASE CONSTRUCTORS VIA 'using'
// ============================================================
class SimpleBase {
public:
    SimpleBase(int x) {
        cout << "SimpleBase constructor with x = " << x << endl;
    }
};

class DerivedUsingCtor : public SimpleBase {
public:
    using SimpleBase::SimpleBase; // inherit all SimpleBase constructors
};

// ============================================================
// MAIN
// ============================================================
int main() {
    cout << "\n--- Object Construction Order ---\n";
    Player p1("Hassan", 21, "123-ABC", 100);
    // prints: Person constructor, then Player constructor

    cout << "\n--- Copy Constructor ---\n";
    Player p2 = p1; // calls Player copy constructor, which calls Person copy constructor

    cout << "\n--- Name Hiding ---\n";
    p1.display();           // calls Player::display() (child version)
    p1.Person::display();   // explicitly calls Person::display() (parent version)

    cout << "\n--- Protected Inheritance ---\n";
    ProtectedChild pc("Ali", 25, "456-DEF");
    pc.show();
    // pc.name;  ERROR: name became protected, not accessible from main

    cout << "\n--- Private Inheritance + using ---\n";
    PrivateChild pr("Sara", 30, "789-GHI");
    pr.show();
    // pr.name;  ERROR: private in this context

    cout << "\n--- Inheriting Constructors ---\n";
    DerivedUsingCtor obj(10); // calls SimpleBase(int) through inherited constructor

    cout << "\n--- Destruction Order (reversed, LIFO) ---\n";
    // obj, pr, pc, p2, p1 destroyed in reverse order as main ends
    // for each: child destructor first, then parent destructor

    return 0;
}

Closing

Access levels through inheritance, the fixed construction and destruction order, slicing in copy constructors, and the virtual destructor bug that catches almost everyone once. Last one left: polymorphism, where virtual stops being a footnote and becomes the whole point.

← Previous 13 - Classes and Objects Next → 15 - Polymorphism