---
title: Inheritance
source: 'https://academia.sh/en/courses/cpp-fundamentals/inheritance'
course: 'C++ Fundamentals and Object Design'
language: en
updated: '2026-09-26T03:02:46+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - "Defines a derived class's base subobject as a part of the object"
    - 'Measures with an address comparison that the base and derived object share the same address'
    - 'Observes that the constructor and destructor call order is two phases of a single object'
---

# Inheritance

The measurement of whether the base subobject a derived object carries inside it stands at the same address, and is part of a single construction/destruction sequence.

The previous topic measured how an object constructs, copies, and releases its own
resource; the class stood alone, not tied to another class. This topic addresses how
one class is built on top of another: **inheritance** places one class inside another.
The lesson's measurement question is the same — which object does this expression work
with — but now the object itself has two layers: a **base subobject** and the derived
object wrapping it. This lesson asks: is the base subobject a separate object from the
derived object, or a part of it?

## The Base Subobject and the Shared Address

`Manager` derives from `Employee`. When a `Manager` is constructed, the `Employee`
constructor runs first, then `Manager`'s own body. Naming `m` with a pointer of the
base type (`base`) — does this produce a copy, or does it carry the same address? This
needs to be measured with an address comparison.

```cpp
// inheritance_base.cpp -- derived object's base subobject and shared address
#include <iostream>
#include <string>

class Employee {
public:
    explicit Employee(std::string name) : name_(name) {
        std::cout << "Employee constructor: " << name_ << "\n";
    }
    ~Employee() {
        std::cout << "Employee destructor: " << name_ << "\n";
    }
protected:
    std::string name_;
};

class Manager : public Employee {
public:
    Manager(std::string name, int team_no) : Employee(name), team_no_(team_no) {
        std::cout << "Manager constructor: team " << team_no_ << "\n";
    }
    ~Manager() {
        std::cout << "Manager destructor\n";
    }
private:
    int team_no_;
};

int main() {
    Manager m("Aylin", 4);
    Employee* base = &m;

    std::cout << std::boolalpha;
    std::cout << "same address for m and base "
              << (static_cast<void*>(&m) == static_cast<void*>(base)) << "\n";

    return 0;
}
```

```
Employee constructor: Aylin
Manager constructor: team 4
same address for m and base true
Manager destructor
Employee destructor: Aylin
```

**PS1.** The `Manager` constructor calls the `Employee` constructor first
("Employee constructor: Aylin"), then runs its own body ("Manager constructor: team
4") — this is not two separate objects being constructed in sequence, it is **a
single object** constructed in two phases. The `base = &m` assignment does not
produce a copy, it only names the same address with a pointer of the base type: the
comparison `static_cast<void*>(&m) == static_cast<void*>(base)` confirms this,
**true**. On exit, as `m` goes out of scope, the destruction order follows
construction exactly in reverse — first `Manager`'s own destructor, then
`Employee`'s — again the two-phase teardown of a single object. (M08/K02 asked which
type resolved the call; here the question has changed — it is not the call, but the
**object** the expression points to that is measured, and while Java has no object
slicing, assigning to a base type **by value** in C++ constructs a new and incomplete
object; here there is only pointer access, no slicing.) The class is **part**: what
`base` points to is not an object independent of `m`, it is the base subobject inside
`m`.

## Access Through a Base Reference: Reaching the Part, Not a Separate Object

Passing a `Manager` to a function with an `Employee&` parameter asks the same
question: does the reference parameter bind to an object separate from `m`, or to
the base part inside `m`?

```cpp
// inheritance_reference.cpp -- access through a base reference reaches the same part
#include <iostream>
#include <string>

class Employee {
public:
    explicit Employee(std::string name) : name_(name) {}
    std::string name() const { return name_; }
protected:
    std::string name_;
};

class Manager : public Employee {
public:
    Manager(std::string name, int team_no) : Employee(name), team_no_(team_no) {}
    int team_no() const { return team_no_; }
private:
    int team_no_;
};

bool is_same_object(const Employee& e, const Manager& m) {
    return static_cast<const void*>(&e) == static_cast<const void*>(&m);
}

void label(const Employee& e) {
    std::cout << "name seen by the reference " << e.name() << "\n";
}

int main() {
    Manager m("Baris", 7);

    label(m);

    std::cout << std::boolalpha;
    std::cout << "whether the reference parameter points to the same object as m "
              << is_same_object(m, m) << "\n";

    return 0;
}
```

```
name seen by the reference Baris
whether the reference parameter points to the same object as m true
```

**PS2.** In the call `label(m)`, `m` binds to an `Employee&`; the `name()` call reads
the `name_` field of the `Employee` part inside `m` and prints "Baris" — without ever
touching the `team_no_` that `Manager` added. In the call `is_same_object(m, m)`, the
first parameter binds `m` to `Employee&`, the second parameter binds the same `m` to
`Manager&`; comparing the addresses of the two references gives **true**. Reference
binding does not construct a copy here either — the parameter binds to the object
named in the call itself (more precisely, to that object's base part). The class is
again **part**: what the `Employee&` parameter points to is not an object living
outside `m`, it is the base subobject inside `m`.

## The Size of an Object That Grows: A Part Enlarges the Base Object

Address equality shows the base subobject stands at the start of `m`; a size
comparison shows the base subobject occupies space **inside** `m`, in the same memory
region, not in a separate box.

```cpp
// inheritance_size.cpp -- a derived object's size encloses the base part
#include <iostream>
#include <string>

class Employee {
public:
    explicit Employee(std::string name) : name_(name) {}
protected:
    std::string name_;
};

class Manager : public Employee {
public:
    Manager(std::string name, int team_no) : Employee(name), team_no_(team_no) {}
private:
    int team_no_;
};

int main() {
    std::cout << std::boolalpha;
    std::cout << "whether Manager's size is at least Employee plus an int "
              << (sizeof(Manager) >= sizeof(Employee) + sizeof(int)) << "\n";
    std::cout << "whether Manager's size is larger than Employee's "
              << (sizeof(Manager) > sizeof(Employee)) << "\n";
    return 0;
}
```

```
whether Manager's size is at least Employee plus an int true
whether Manager's size is larger than Employee's true
```

**PS3.** `sizeof(Manager)` carries both the full size of the `Employee` subobject and
the `team_no_` field `Manager` itself adds — both comparisons are **true**. This
confirms the address equality of the first two measurements at the level of memory
layout: the base subobject does not stand inside `m` in a separate, independently
allocated box; as a physical portion of `m`, it occupies space within the same memory
block as `Manager`'s own fields. The class is **part**: the size of a `Manager`
object includes the size of the `Employee` part it carries, not as an additional
reference or pointer to it.

## Public, Protected, and Private Inheritance: Same Address, Different External Openness

Three classes derive from the same `Base` with different access specifiers:
`PublicDerived` with `public`, `ProtectedDerived` with `protected`, `PrivateDerived`
with `private` inheritance. Can all three access `Base`'s protected member from
within themselves? And from outside, which one alone can be converted to `Base*`?

```cpp
// inheritance_access_specifiers.cpp -- public protected private inheritance carry the same address, provide different external openness
#include <iostream>

class Base {
protected:
    int value_ = 42;
};

class PublicDerived : public Base {
public:
    int read() const { return value_; }
};

class ProtectedDerived : protected Base {
public:
    int read() const { return value_; }
};

class PrivateDerived : private Base {
public:
    int read() const { return value_; }
};

int main() {
    PublicDerived pub;
    ProtectedDerived prot;
    PrivateDerived priv;

    std::cout << "read() in public inheritance " << pub.read() << "\n";
    std::cout << "read() in protected inheritance " << prot.read() << "\n";
    std::cout << "read() in private inheritance " << priv.read() << "\n";

    Base* base_pub = &pub;
    std::cout << std::boolalpha
              << "whether the externally obtained base* address matches pub in public inheritance "
              << (static_cast<void*>(base_pub) == static_cast<void*>(&pub)) << "\n";

    return 0;
}
```

```
read() in public inheritance 42
read() in protected inheritance 42
read() in private inheritance 42
whether the externally obtained base* address matches pub in public inheritance true
```

**PS19.** All three classes' `read()` function accesses `value_` from within the
class's own body, and all three print **42** — the access specifier (`public`,
`protected`, `private`) does not change access from within the class itself, because
a `protected` member is already accessible from the deriving class's own body. What
changes is visibility **from outside**: `Base* base_pub = &pub;` compiles directly
only because `PublicDerived` derives with `public` inheritance — the same conversion
from `ProtectedDerived` or `PrivateDerived` could not present the base subobject to
the outside as `Base*`. The address comparison is again **true**: the access
specifier does not change `PublicDerived`'s memory layout or the base subobject's
address, it only restricts which pointer type can reach that address from outside.
The class is **part**: the `value_` that `read()` operates on is always the class's
own base part regardless of which access specifier it was derived with — the access
specifier chooses whether to make it visible, it does not change the object.

## A Three-Layer Constructor Chain

The first measurement showed a two-layer chain (`Employee` → `Manager`). When the
chain extends to a third layer (`A` → `B` → `C`), how does the construction and
destruction order expand?

```cpp
// inheritance_three_layer_chain.cpp -- three-layer constructor chain from the most base to the most derived
#include <iostream>

class A {
public:
    A() { std::cout << "A constructor\n"; }
    ~A() { std::cout << "A destructor\n"; }
};

class B : public A {
public:
    B() { std::cout << "B constructor\n"; }
    ~B() { std::cout << "B destructor\n"; }
};

class C : public B {
public:
    C() { std::cout << "C constructor\n"; }
    ~C() { std::cout << "C destructor\n"; }
};

int main() {
    std::cout << "-- construction --\n";
    {
        C c;
        std::cout << "-- end of scope, destruction begins --\n";
    }
    return 0;
}
```

```
-- construction --
A constructor
B constructor
C constructor
-- end of scope, destruction begins --
C destructor
B destructor
A destructor
```

**PS20.** `C c;` constructs a single object, but this construction splits into three
phases: first the most base `A`, then `B`, then `C`'s own body — no matter how many
layers the chain has, the order is always **most base to most derived**. Destruction
runs in exactly the reverse order at end of scope: first `C`, then `B`, then `A`.
This is a generalization of the two-phase construction/destruction from the first
measurement — a `C` object actually consists of three nested parts (the `A`
subobject, the `B` subobject growing inside it, and `C`'s own fields growing inside
that), and each part is constructed only after the part before it completes. The
class is **part**: `C` is a single object, but that object has three layers, and
each layer is built on top of the previous one.

## Assigning by Value to the Base Slices the Object: A Separate Copy

Previous measurements always accessed the base part through a pointer or a
reference — the address was always the same. When a `Manager` object is assigned
**by value** to a variable of type `Employee`, does the address stay the same, or
does an independent object emerge?

```cpp
// inheritance_slicing.cpp -- assigning by value to the base slices the object, producing a separate independent copy
#include <iostream>
#include <string>

class Employee {
public:
    explicit Employee(std::string name) : name_(std::move(name)) {}
    std::string name() const { return name_; }
    void set_name(std::string name) { name_ = std::move(name); }
protected:
    std::string name_;
};

class Manager : public Employee {
public:
    Manager(std::string name, int team_no) : Employee(std::move(name)), team_no_(team_no) {}
    int team_no() const { return team_no_; }
private:
    int team_no_;
};

int main() {
    Manager m("Deniz", 9);
    Employee sliced = m;

    std::cout << std::boolalpha;
    std::cout << "whether sliced and m carry the same address "
              << (static_cast<void*>(&sliced) == static_cast<const void*>(&m)) << "\n";

    sliced.set_name("Other");
    std::cout << "m's name after sliced's name is changed " << m.name() << "\n";
    std::cout << "sliced object's name " << sliced.name() << "\n";

    return 0;
}
```

```
whether sliced and m carry the same address false
m's name after sliced's name is changed Deniz
sliced object's name Other
```

**PS21.** `Employee sliced = m;` does not construct a pointer or a reference, it
constructs a **new `Employee` object** — `Employee`'s copy constructor copies
`name_` from `m`'s base part, and `Manager`'s added `team_no_` is not carried at
all, because `Employee`'s own definition has no such field. The address comparison
confirms this: **false** — `sliced` is a separate object living at its own address,
independent of `m`. When `sliced`'s name is changed, `m`'s name stays **Deniz**: the
two objects are independent of each other. This is a **different class** from the
rest of the lesson: while pointer and reference access always reach the same base
**part**, assignment by value (slicing) produces a new, independent **copy** of type
`Employee` — and this copy does not carry the fields `Manager` added, only the part
`Employee` defines.

## Overload Selection by Static Type, Object Identity Does Not Change

The `info` function is overloaded for both `Employee&` and `Manager&`. When the same
`m` object is passed to `info` once as `Manager&` and once through an `Employee&`
reference, which overload is selected — and do both work with the same object?

```cpp
// inheritance_overload_comparison.cpp -- overload selection by static type, object identity fixed at run time
#include <iostream>
#include <string>

class Employee {
public:
    explicit Employee(std::string name) : name_(std::move(name)) {}
    std::string name_;
};

class Manager : public Employee {
public:
    Manager(std::string name, int team_no) : Employee(std::move(name)), team_no_(team_no) {}
    int team_no_;
};

void info(const Employee& e) {
    std::cout << "info(Employee&) called, name " << e.name_ << "\n";
}

void info(const Manager& m) {
    std::cout << "info(Manager&) called, name " << m.name_ << ", team " << m.team_no_ << "\n";
}

int main() {
    Manager m("Kaan", 3);
    Employee& base_ref = m;

    info(m);
    info(base_ref);

    std::cout << std::boolalpha
              << "whether both calls work with the same m object "
              << (static_cast<const void*>(&m) == static_cast<const void*>(&base_ref)) << "\n";

    return 0;
}
```

```
info(Manager&) called, name Kaan, team 3
info(Employee&) called, name Kaan
whether both calls work with the same m object true
```

**PS22.** `info(m)` selects `info(Manager&)` by looking at `m`'s static type
(`Manager`) at compile time; `info(base_ref)` selects `info(Employee&)` by looking
at `base_ref`'s static type (`Employee&`) — neither is `virtual`, the selection is
made entirely **at compile time**, based on the expression's static type. This is on
the same axis as the question measured in M08/K02: which overload is selected is a
**compile-time** decision. But this lesson measures a different question: which
**object** does the selected function work with? The address comparison is **true**
— the object `info(Manager&)` works with as `m` and the object `info(Employee&)`
works with as `base_ref` point to exactly the same object. Overload selection
determines which **function** is called based on the static type; object identity
stays the same regardless of this selection. The class is **part**: two different
overloads work on the same object's base part.

## The Bounding Measurement

The inheritance measured in this lesson is **single** and **non-virtual**: `Manager`
derives from a single base class, and no function was `virtual`, so the observation
that the base subobject stands at the start of `m` and its address coincides with
`m`'s is bound to these conditions — under multiple inheritance (the Multiple
Inheritance and the Diamond Problem lesson), the second and later base subobjects
may stand at a different offset, and their addresses may not coincide with `m`'s.
The measurement also does not cover the case where `Employee` exists on its own,
without a `Manager` — it measured only the `Employee` part inside a `Manager`
object. Which function a call through a base pointer or reference runs — whether the
call looks at the static type or the real type — is not this lesson's subject; the
next lesson measures that.

## Summary

- When a `Manager` object is constructed, the `Employee` constructor runs first,
  then `Manager`'s own body — not the construction of two separate objects, but two
  phases of a single object.
- `Employee* base = &m` does not produce a copy; `base` and `m` carry the same
  address, because under single, non-virtual inheritance the base subobject stands
  at the start of the derived object.
- Passing `m` to an `Employee&` parameter also binds to the same address — reference
  binding does not construct a copy, the class is **part**.
- `sizeof(Manager)` carries both the full size of the `Employee` subobject and the
  fields `Manager` itself adds; the base subobject does not stand in a separate box,
  it stands in a physical portion of the same object.
- Destruction order follows construction in reverse: first `Manager`'s own
  destructor, then `Employee`'s — again the two-phase teardown of a single object.

## Next Step

In this lesson the base subobject's address always coincided with `m`'s, because no
function was `virtual` and the function to be called was determined at compile
time, by looking at the expression's **static type**. The next lesson, Virtual
Functions and Vtables, measures how a call through a base pointer or reference
determines which function runs based on the object's **real** type — and this time
what is measured is not address equality, but that the same expression always works
with the same object at run time.
