---
title: 'Multiple Inheritance and the Diamond Problem'
source: 'https://academia.sh/en/courses/cpp-fundamentals/multiple-inheritance-and-the-diamond-problem'
course: 'C++ Fundamentals and Object Design'
language: en
updated: '2026-09-26T03:02:46+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Measures that in multiple inheritance only the first base subobject shares the same address as the derived object'
    - 'Shows that a common ancestor in diamond-shaped inheritance splits into two separate copies without virtual inheritance'
    - 'Verifies that virtual inheritance reduces the common ancestor to a single, shared base subobject'
---

# Multiple Inheritance and the Diamond Problem

The measurement of how the second base subobject in multiple inheritance does not share the same address as the derived object, and how a common ancestor in diamond-shaped inheritance doubles without virtual inheritance.

The previous two lessons measured single inheritance: a derived object had exactly
one base subobject, and that subobject's address always coincided with the derived
object's address. This lesson covers the case where a class derives from **more
than one** base class at once. Does the first base subobject still stand at the
head of the derived object? Does the second one also share the same address? And if
the two base classes have a common ancestor, how many times does that ancestor take
up space inside the derived object?

## In Multiple Inheritance the Second Base Subobject's Address Differs

`Triathlete` derives from two unrelated classes: `Swimmer` and `Runner`. Does the
first base subobject stand at the head of the derived object, does the second one
also share the same address?

```cpp
// multiple_inheritance_offset.cpp -- in multiple inheritance the first base is at the same address, the second is not
#include <iostream>

class Swimmer {
public:
    int style_ = 1;
};

class Runner {
public:
    int distance_ = 5;
};

class Triathlete : public Swimmer, public Runner {
public:
    int total_score_ = 0;
};

int main() {
    Triathlete t;

    std::cout << std::boolalpha;
    std::cout << "t and the first base Swimmer address the same "
              << (static_cast<void*>(&t) == static_cast<void*>(static_cast<Swimmer*>(&t))) << "\n";
    std::cout << "t and the second base Runner address the same "
              << (static_cast<void*>(&t) == static_cast<void*>(static_cast<Runner*>(&t))) << "\n";

    return 0;
}
```

```
t and the first base Swimmer address the same true
t and the second base Runner address the same false
```

**PS10.** `Triathlete` declares `Swimmer` first, `Runner` second in its inheritance
list — the compiler places the base subobjects in this order. The first base
subobject (`Swimmer`) stands at the head of `t`, so `&t` and
`static_cast<Swimmer*>(&t)` carry the same address, **true**. The second base
subobject (`Runner`) stands at an offset following the `Swimmer` subobject; `&t`
and `static_cast<Runner*>(&t)` are different addresses, **false**. In single
inheritance (the Inheritance lesson) the base subobject's address always coincided
with the derived object's, because there was only one base to carry; in multiple
inheritance this holds only for the **first** base. The class is **part**: both
base subobjects are a part of `t`, but only the first shares the same address.

## The Diamond Problem: Two Separate Copies from a Common Ancestor

Let `Swimmer` and `Runner` have a common ancestor, `Living`. When `Triathlete`
derives from both, does reaching the `Living` subobject through `Swimmer` and
through `Runner` land on the same object, or two separate copies?

```cpp
// diamond_separate.cpp -- without virtual inheritance the common ancestor splits into two separate copies
#include <iostream>

class Living {
public:
    int health_ = 100;
};

class Swimmer : public Living {};
class Runner : public Living {};
class Triathlete : public Swimmer, public Runner {};

int main() {
    Triathlete t;

    Living* via_swimmer = static_cast<Living*>(static_cast<Swimmer*>(&t));
    Living* via_runner = static_cast<Living*>(static_cast<Runner*>(&t));

    std::cout << std::boolalpha;
    std::cout << "Living reached via Swimmer and via Runner the same "
              << (static_cast<void*>(via_swimmer) == static_cast<void*>(via_runner)) << "\n";
    std::cout << "Triathlete's size large enough to hold two separate Living "
              << (sizeof(Triathlete) >= 2 * sizeof(Living)) << "\n";

    return 0;
}
```

```
Living reached via Swimmer and via Runner the same false
Triathlete's size large enough to hold two separate Living true
```

**PS11.** `Swimmer` and `Runner` derive from `Living` through **ordinary**
(non-virtual) inheritance; each carries its own `Living` subobject. When
`Triathlete` derives from both, these two `Living` copies both enter `Triathlete`
— they do not merge. `via_swimmer` and `via_runner` carry different addresses,
**false**: these are two separate `Living` objects. `sizeof(Triathlete)` covering
at least two `Living` sizes confirms this in memory layout, **true**. This is the
**diamond problem**: writing `t.health_` would stop the compiler because it is
ambiguous which copy is meant, it only compiles with an explicit path like
`t.Swimmer::health_` or `t.Runner::health_` — this lesson never runs that ambiguous
line, it only shows the existence of the two copies through address and size
measurement. The class is **part**: `Triathlete` carries not one but two separate
parts of `Living`.

## Virtual Inheritance: A Single Shared Base Subobject

If `Swimmer` and `Runner` derive from `Living` through **virtual** inheritance,
when `Triathlete` derives from both, is the `Living` subobject still split in two,
or reduced to a single shared object?

```cpp
// diamond_virtual.cpp -- virtual inheritance reduces the common ancestor to a single shared subobject
#include <iostream>

class Living {
public:
    int health_ = 100;
};

class Swimmer : virtual public Living {};
class Runner : virtual public Living {};
class Triathlete : public Swimmer, public Runner {};

int main() {
    Triathlete t;

    Living* via_swimmer = static_cast<Living*>(static_cast<Swimmer*>(&t));
    Living* via_runner = static_cast<Living*>(static_cast<Runner*>(&t));

    std::cout << std::boolalpha;
    std::cout << "Living reached via Swimmer and via Runner the same "
              << (static_cast<void*>(via_swimmer) == static_cast<void*>(via_runner)) << "\n";
    std::cout << "t.health_ and via_swimmer->health_ carry the same address "
              << (static_cast<void*>(&t.health_) == static_cast<void*>(&via_swimmer->health_)) << "\n";

    return 0;
}
```

```
Living reached via Swimmer and via Runner the same true
t.health_ and via_swimmer->health_ carry the same address true
```

**PS12.** The `virtual public Living` declaration tells `Swimmer` and `Runner`,
"do not construct your own `Living` copy, use the single copy shared by the
topmost derived class." When `Triathlete` derives from both, there is now only one
`Living` subobject — `via_swimmer` and `via_runner` carry the same address,
**true**. Thanks to this sharing, `t.health_` is no longer ambiguous either, it
compiles directly and points to the same shared object, **true**. The diamond
problem from the previous measurement — two separate `Living` copies — is gone
here, because both paths to `Living` converge on the same single object. The class
is **part**: `Triathlete` now carries not two but a single part of `Living`, and
this part is reached through both `Swimmer` and `Runner`.

## Reaching Two Separate Copies by Qualified Name and Changing Only One

PS11 said that writing `t.health_` does not compile because it is ambiguous which
copy is meant, and that it only compiles with a qualified name like
`t.Swimmer::health_` or `t.Runner::health_`, but never ran that line. When only one
copy is changed with a qualified name, does the other copy really remain
unaffected?

```cpp
// multiple_inheritance_qualified_access.cpp -- in non-virtual inheritance two separate Living copies are reached by qualified name and one is changed
#include <iostream>

class Living {
public:
    int health_ = 100;
};

class Swimmer : public Living {};
class Runner : public Living {};
class Triathlete : public Swimmer, public Runner {};

int main() {
    Triathlete t;

    t.Swimmer::health_ = 70;

    std::cout << "t.Swimmer::health_ after change its value " << t.Swimmer::health_ << "\n";
    std::cout << "t.Runner::health_ value remaining unchanged " << t.Runner::health_ << "\n";
    std::cout << std::boolalpha
              << "t.Swimmer::health_ and t.Runner::health_ carry the same address "
              << (static_cast<void*>(&t.Swimmer::health_) == static_cast<void*>(&t.Runner::health_)) << "\n";

    return 0;
}
```

```
t.Swimmer::health_ after change its value 70
t.Runner::health_ value remaining unchanged 100
t.Swimmer::health_ and t.Runner::health_ carry the same address false
```

**PS32.** `t.Swimmer::health_ = 70;` uses the qualified name to explicitly tell the
compiler which copy is meant, and changes only that copy. `t.Runner::health_`
queried afterward is still its first value, **100** — the change made through
`Swimmer` never leaks into `Runner`'s own `Living` copy. The address comparison
confirms this directly: **false**, the two qualified accesses really reach two
separate objects. This directly proves what PS11 showed indirectly through memory
size: inside `Triathlete`, `Living` exists not once but twice, and qualified access
gives the programmer the ability to choose which of the two copies to work with —
manually making a decision the compiler cannot make on its own. The class is
**part**: `t.Swimmer::health_` and `t.Runner::health_` are two different parts of
`t`, capable of changing independently.

## The Most Derived Class Calls the Virtual Base's Constructor

`Living` now has a non-default constructor that takes a parameter. `Swimmer` and
`Runner` each try to construct `Living` with their own value in their own
constructors, but `Triathlete` also constructs `Living` with a separate value in
its own constructor. In virtual inheritance, which constructor's value does the
shared `health_` carry?

```cpp
// diamond_virtual_constructor_chain.cpp -- the most derived class directly calls the virtual base's constructor, intermediate ones are bypassed
#include <iostream>

class Living {
public:
    explicit Living(int health) : health_(health) {
        std::cout << "Living constructor health=" << health_ << "\n";
    }
    int health_;
};

class Swimmer : virtual public Living {
public:
    Swimmer() : Living(1) {
        std::cout << "Swimmer constructor (its own Living(1) call will be bypassed)\n";
    }
};

class Runner : virtual public Living {
public:
    Runner() : Living(2) {
        std::cout << "Runner constructor (its own Living(2) call will be bypassed)\n";
    }
};

class Triathlete : public Swimmer, public Runner {
public:
    Triathlete() : Living(100), Swimmer(), Runner() {
        std::cout << "Triathlete constructor, shared health_ = " << health_ << "\n";
    }
};

int main() {
    Triathlete t;
    return 0;
}
```

```
Living constructor health=100
Swimmer constructor (its own Living(1) call will be bypassed)
Runner constructor (its own Living(2) call will be bypassed)
Triathlete constructor, shared health_ = 100
```

**PS33.** `Living constructor health=100` prints only **once**, `health=1` or
`health=2` never appears — the responsibility for calling the virtual base's
constructor falls not on the intermediate classes, but on the **most derived**
class. The `Living(1)` and `Living(2)` calls in `Swimmer`'s and `Runner`'s own
mem-initializer lists are ignored when constructed through `Triathlete`; these
lines would only take effect if `Swimmer` or `Runner` were constructed **on its
own** (without `Triathlete`). `Triathlete`'s own mem-initializer list's `Living(100)`
is the only call that really runs — this is why the shared `health_` is **100**.
Virtual bases, independent of their position in the inheritance chain, are always
initialized by the constructor of the topmost derived class; this is the
constructor-side counterpart of the "single shared object" principle measured as
PS12 — since the shared object is single, the call that constructs it also has to
be single. The class is **part**: `health_` is a single shared part of `t`, and
only the most derived class's constructor determines this part's value.

## The Structural Cost of Virtual Inheritance: Fewer Copies, an Added Pointer

PS11 showed that `Living` took up space twice in the non-virtual diamond, PS12
showed it reduced to a single copy in the virtual diamond. Does the total object
size shrink by the same proportion as the copy count drops, or does virtual
inheritance's own overhead eat up part of this gain?

```cpp
// diamond_virtual_structural_cost.cpp -- comparing virtual inheritance's effect on object size against the same class set without it
#include <iostream>

class Living {
public:
    int health_ = 1;
};

class SwimmerPlain : public Living {};
class RunnerPlain : public Living {};
class TriathletePlain : public SwimmerPlain, public RunnerPlain {};

class SwimmerVirtual : virtual public Living {};
class RunnerVirtual : virtual public Living {};
class TriathleteVirtual : public SwimmerVirtual, public RunnerVirtual {};

int main() {
    std::cout << std::boolalpha
              << "is the virtual diamond's size smaller than the non-virtual diamond's: "
              << (sizeof(TriathleteVirtual) < sizeof(TriathletePlain)) << "\n";
    std::cout << "is the virtual diamond's size larger than a single Living (is there overhead): "
              << (sizeof(TriathleteVirtual) > sizeof(Living)) << "\n";
    std::cout << "is the non-virtual diamond's size at least two Living: "
              << (sizeof(TriathletePlain) >= 2 * sizeof(Living)) << "\n";

    return 0;
}
```

```
is the virtual diamond's size smaller than the non-virtual diamond's: false
is the virtual diamond's size larger than a single Living (is there overhead): true
is the non-virtual diamond's size at least two Living: true
```

**PS34.** The expectation was for the virtual diamond to come out smaller since the
copy count dropped, but the measurement does not confirm this: **false**. Because
`Living` has only a single `int` member, the cost of a copy is already small (a
few bytes); against this, `Swimmer` and `Runner` each carry a separate record
(a virtual base pointer or a similar mechanism, varies by compiler) to resolve how
to reach their own `Living` — two bases, two separate records. On this compiler,
the total cost of the added records outweighed the single `Living` copy that was
saved. The virtual diamond being larger than a single `Living` (**true**) was
already expected; what is instructive is that "fewer copies" and "a smaller object"
are not the same thing. This is the multiple-inheritance counterpart of the finding
measured as PS26 for virtual functions, "a vptr is added to every object": keeping
a shared thing (a method table / a base subobject) in one place loads a small but
non-zero bookkeeping cost onto the object — and this cost can even be larger than
the size of the copy that was eliminated. The class is **part**: the measured size
difference comes not from `t`'s virtual base subobject itself, but from the added
parts that carry the path leading to that subobject.

## The Bounding Measurement

The virtual inheritance measured in this lesson showed the sharing of data
(`health_`) and the rule that only the most derived class calls the virtual base
class's constructor; but the measurement did not cover the extra indirection cost
virtual inheritance carries (every access being resolved through a virtual base
pointer) — it only measured address equality, not how many steps the resolution
takes. The case where `Triathlete` has its own virtual functions, and `Swimmer` and
`Runner` each define different functions with the same signature — a naming
collision and ambiguous redefinition — is outside this lesson's scope. Qualified
access (`t.Swimmer::health_`) was shown only under non-virtual inheritance; how
qualified access to the single shared copy behaves under virtual inheritance —
which would be indistinguishable from ordinary access, since the copy is already
single — was not additionally tested by this lesson.

## Summary

- In multiple inheritance only the **first** base class's subobject shares the same
  address as the derived object; subsequent base subobjects stand at a different
  offset.
- If two base classes have a common ancestor and the inheritance is not virtual,
  that ancestor takes up space inside the derived object as **two separate
  copies** — the diamond problem.
- `sizeof` confirms the existence of two separate copies in memory size; the common
  ancestor object reached by two different paths carries different addresses.
- `virtual public` inheritance reduces the common ancestor to a **single**
  subobject shared by the topmost derived class; the ancestor reached by two paths
  now carries the same address.
- The expression measured in this lesson falls in the **part** class: the base
  subobject is a part of the derived object, but in multiple and diamond-shaped
  inheritance, that part's address and count change.

## Next Step

What this lesson measured were always constructor and destructor calls that
completed in the normal flow; no constructor threw an error. The next lesson,
Exceptions, measures the case where a constructor is interrupted midway — throws
an error: how does an exception thrown before construction completes affect the
destruction of that object's already-constructed parts, and does an exception
object produce a **copy** as it is thrown with `throw` and caught with `catch`.
What is measured is again which object an expression works with, but this time the
question is the state of an object cut off in the middle of construction.
