multiple inheritance
Dynamic type
- Polymorphic type - has at least one direct or inherited (potentially final or abstract) virtual function.
 
class Polymorphic1 {
    virtual void f() {}
}
class Polymorphic2 : public Polymorphic1 {
}
class Polymorphic3 {
    virtual void f() final {}
}
class Polymorphic4 {
    virtual void f() = 0;
}- Static type - compile time
 - Dynamic type - runtime
 
class Base {
public:
   virtual ~Base();
};
class Derived : public Base {
}Base* p = new Derived;
// static type Base
// dynamic type Deriveddynamic_cast
Base* b = new Derived1();
auto s_d1 = static_cast<Derived1*>(b);
auto s_d2 = static_cast<Derived2*>(b); // UB
auto d_d1 = dynamic_cast<Derived1*>(b);
auto d_d2 = dynamic_cast<Derived2*>(b); // nullptrBase* b = new Derived1();
Base& b_ref = *b;
auto s_d1 = static_cast<Derived1&>(b_ref);
auto s_d2 = static_cast<Derived2&>(b_ref); // UB
auto d_d1 = dynamic_cast<Derived1&>(b_ref);
auto d_d2 = dynamic_cast<Derived2&>(b_ref); // throw std::bad_castmultiple inheritance
computer
- Turing machine
 - Electronic device
 
is-a
class TuringMachine {
private:
    InfiniteArray<state> _tape;
    state _current_state;
public:
    TuringMachine() :
        _tape{ state::empty },
        _current_state{ state::start_state }
    {
    }
    struct transition_result {
        state new_state;
        state new_symbol;
        enum {
            Left, Right, None
        } tape_movement;
    };
    
    virtual transition_result transition(const state& current_state, const state& symbol) = 0;
};class Computer : public TuringMachine { ... };multiple inheritance
computer
- Turing machine
 - Electronic device
 
is-a
class TuringMachine {
    ...
};
class ElectronicDevice {
    ...
};class Computer : public TuringMachine, public ElectronicDevice {
    ...
};multiple inheritance
class Derived : public Base1, public Base2, public BaseN {
    ...
};struct Base1 {
    void b1();
};
struct Base2 {
    int b2 = 3;
};
struct Derived : Base1, Base2 {
};
void foo() {
    Derived d;
    d.b1();
    d.b2 = 3;
}struct Base1 {
    void b1();
};
struct Base2 {
protected:
    int b2 = 3;
};
struct Derived : Base1, Base2 {
    using Base2::b2;
};
void foo() {
    Derived d;
    d.b1();
    d.b2 = 3;
}using declaration
struct Base1 {
    void print(int) {
        std::cout << "i\n";
    }
};
struct Base2 {
    void print(float) {
        std::cout << "f\n";
    }
};
struct Derived : Base1, Base2 {
};
void foo() {
    Derived d;
    d.print(3);    // ambiguous
    d.print(2.0f); // ambiguous
}shadowing / dominance
d.print()
Derived
Base1
Base2
Derived::print
Base1::print
Base2::print
Name lookup
struct Base1 {
    void print(int) {
        std::cout << "i\n";
    }
};
struct Base2 {
    void print(float) {
        std::cout << "f\n";
    }
};
struct Derived : Base1, Base2 {
};
void foo() {
    Derived d;
    d.print(3);    // ambiguous
    d.print(2.0f); // ambiguous
}shadowing / dominance
d.print()
Derived
Base1
Base2
Derived::print
Base1::print
Base2::print
Name lookup
struct Base1 {
    void print(int) {
        std::cout << "i\n";
    }
};
struct Base2 {
    void print(float) {
        std::cout << "f\n";
    }
};
struct Derived : Base1, Base2 {
};
void foo() {
    Derived d;
    d.print(3);    // ambiguous
    d.print(2.0f); // ambiguous
}shadowing / dominance
d.print()
Derived
Base1
Base2
Derived::print
Base1::print
Base2::print
Name lookup
struct Base1 {
    void print(int) {
        std::cout << "i\n";
    }
};
struct Base2 {
    void print(float) {
        std::cout << "f\n";
    }
};
struct Derived : Base1, Base2 {
};
void foo() {
    Derived d;
    d.print(3);    // ambiguous
    d.print(2.0f); // ambiguous
}shadowing / dominance
d.print()
Derived
Base1
Base2
Derived::print
Base1::print
Base2::print
Name lookup
struct Base1 {
    void print(int) {
        std::cout << "i\n";
    }
};
struct Base2 {
    void print(float) {
        std::cout << "f\n";
    }
};
struct Derived : Base1, Base2 {
};
void foo() {
    Derived d;
    d.print(3);    // ambiguous
    d.print(2.0f); // ambiguous
}shadowing / dominance
d.print()
Derived
Base1
Base2
Derived::print
Base1::print
Base2::print
Name lookup
struct Base1 {
    void print(int) {
        std::cout << "i\n";
    }
};
struct Base2 {
    void print(float) {
        std::cout << "f\n";
    }
};
struct Derived : Base1, Base2 {
};
void foo() {
    Derived d;
    d.print(3);    // ambiguous
    d.print(2.0f); // ambiguous
}shadowing / dominance
d.print()
Derived
Base1
Base2
Derived::print
Base1::print
Base2::print
Name lookup
struct Base1 {
    void print(int) {
        std::cout << "i\n";
    }
};
struct Base2 {
    void print(float) {
        std::cout << "f\n";
    }
};
struct Derived : Base1, Base2 {
};
void foo() {
    Derived d;
    d.print(3);    // ambiguous
    d.print(2.0f); // ambiguous
}shadowing / dominance
d.print()
Derived
Base1
Base2
Derived::print
Base1::print
Base2::print
Name lookup
struct Base1 {
    void print(int) {
        std::cout << "i\n";
    }
};
struct Base2 {
    void print(float) {
        std::cout << "f\n";
    }
};
struct Derived : Base1, Base2 {
};
void foo() {
    Derived d;
    d.print(3);    // ambiguous
    d.print(2.0f); // ambiguous
}shadowing / dominance
d.print()
Derived
Base1
Base2
Derived::print
Base1::print
Base2::print
Name lookup
ambiguous
struct Base1 {
    void print(int) {
        std::cout << "i\n";
    }
};
struct Base2 {
    void print(float) {
        std::cout << "f\n";
    }
};
struct Derived : Base1, Base2 {
};
void foo() {
    Derived d;
    d.Base1::print(3);    // i
    d.Base2::print(2.0f); // f
}shadowing / dominance
d.print()
Derived
Base1
Base2
Derived::print
Base1::print
Base2::print
Name lookup
struct Base1 {
    void print(int) {
        std::cout << "i\n";
    }
};
struct Base2 {
    void print(float) {
        std::cout << "f\n";
    }
};
struct Derived : Base1, Base2 {
    using Base1::print;
};
void foo() {
    Derived d;
    d.print(3);    // i
    d.print(2.0f); // i
}shadowing / dominance
d.print()
Derived
Base1
Base2
Base1::print
Base1::print
Base2::print
Name lookup
struct Base1 {
    void print(int) {
        std::cout << "i\n";
    }
};
struct Base2 {
    void print(float) {
        std::cout << "f\n";
    }
};
struct Derived : Base1, Base2 {
    using Base1::print;
    using Base2::print;
};
void foo() {
    Derived d;
    d.print(3);    // i
    d.print(2.0f); // f
}shadowing / dominance
d.print()
Derived
Base1
Base2
Base1::print Base2::print
Base1::print
Base2::print
Name lookup
struct Base1 {
    void print(int) {
        std::cout << "i\n";
    }
};
struct Base2 {
    void print(float) {
        std::cout << "f\n";
    }
};
struct Derived : Base1, Base2 {
    using Base1::print;
    using Base2::print;
};
void foo() {
    Derived d;
    d.print(3);    // i
    d.print(2.0f); // f
}shadowing / dominance
print(float) print(int)
overload set
Overload resolution
d.print(3) d.print(2.0f)
print(int) print(float)
struct Adder {
    float toAdd;
    Adder(float val) : toAdd{val} {}
    int operator()(float x) const { 
        return x + toAdd;
    }
};
struct Fib {
    std::vector<int> sequence;
    Fib(size_t count) :
    	sequence{ create_seq(count) }
    {
    }
private:
    ...
};base class initialisation
struct Derived : Adder, Fib {};
void foo() {
    Derived d; // error: no default ctor
    Derived d{3.0f, 2}; // ok: implicit conversion
}struct Adder {
    float toAdd;
    explicit Adder(float val) : toAdd{val} {}
    int operator()(float x) const { 
        return x + toAdd;
    }
};
struct Fib {
    std::vector<int> sequence;
    explicit Fib(size_t count) :
    	sequence{ create_seq(count) }
    {
    }
private:
    ...
};base class initialisation
struct Derived : Adder, Fib {};
void foo() {
    Derived d; // error: no default ctor
    Derived d{3.0f, 2}; // error: no conversion
    Derived d{Adder{3.0f}, Fib{2}} // ok
}struct Adder {
    float toAdd;
    explicit Adder(float val) : toAdd{val} {}
    int operator()(float x) const { 
        return x + toAdd;
    }
};
struct Fib {
    std::vector<int> sequence;
    explicit Fib(size_t count) :
    	sequence{ create_seq(count) }
    {
    }
private:
    ...
};base class initialisation
struct Derived : Adder, Fib {
    int x;
    Derived(int toAdd, size_t count) :
    	Adder{toAdd},
        Fib{count},
        x{42} // after base classes
    {
    }
};
void foo() {
    Derived d; // error: no default ctor
    Derived d{3.0f, 2}; //  ok
}struct Adder {
    float toAdd;
    explicit Adder(float val = .0f) : toAdd{val} {}
    int operator()(float x) const { 
        return x + toAdd;
    }
};
struct Fib {
    std::vector<int> sequence;
    explicit Fib(size_t count = 0) :
    	sequence{ create_seq(count) }
    {
    }
private:
    ...
};base class initialisation
struct Derived : Adder, Fib {
    using Adder::Adder;
    using Fib::Fib;
};
void foo() {
    Derived d1;
    Derived d2{3.0f};
    Derived d3{size_t(2)};
    Derived d3{2uz}; // since C++23
}Multiple inheritance AND Interfaces
Interfaces
multiple inheritance
class IPrintable {
public:
    virtual void print() const = 0;
};
class IMeowable {
    virtual void moew() const noexcept = 0;
};class MeowingCatPhoto : public Image, public IPrintable, public IMeowable {
public:
    virtual void print() const override {
        Pixel* pixels = getPixels();
        ...
    }
    virtual void moew() const noexcept override {
        ...
    }
}struct Pixel {
    float r, g, b;
};
class Image {
    using data_t = Pixel[768][1024];
    data_t pixels;
public:
    [[nodiscard]] const Pixel* getPixels() const noexcept;
    ...
};memory layout
derived*
struct Base {
    int x, y;
};
struct Derived : Base {
    int z, w;
};Base*
x
y
z
w
Derived der;
Base* pBase = &der;
Derived* pDerived = static_cast<Derived*>(pBase);memory layout
derived*
struct Base {
    int x, y;
    virtual void foo() {}
};
struct Derived : Base {
    int z, w;
};Base*
x
y
z
w
VFTablePtr
Derived der;
Base* pBase = &der;
Derived* pDerived = static_cast<Derived*>(pBase);memory layout
derived*
Base*
x
y
z
w
VFTablePtr
struct Base {
    int x, y;
    virtual void foo() {}
};
struct Derived : Base {
    int z, w;
};
struct Base2 {
    int a, b;
}
struct Derived2 : Derived, Base2 {
    int g;
}Derived2 d2;
Derived* pDerived = &d2;
Base2* pBase2 = &d2;
Base* pBase = &d2;
auto* p_derived = static_cast<Derived2*>(pBase2);
auto* p_derived2 = static_cast<Derived2*>(pDerived);
auto* p_derived3 = static_cast<Derived2*>(pBase);a
b
g
derived2*
Base2*
memory layout
derived*
Base*
...
...
VFTablePtr
struct Base {
    ...
    virtual void foo() {}
};
struct Derived : Base {
    ...
};
struct Base2 {
    ...
}
struct Derived2 : Derived, Base2 {
    ...
}Derived2 d2;
Derived* pDerived = &d2;
Base2* pBase2 = &d2;
Base* pBase = &d2;
auto* p_derived = static_cast<Derived2*>(pBase2);
auto* p_derived2 = static_cast<Derived2*>(pDerived);
auto* p_derived3 = static_cast<Derived2*>(pBase);...
...
derived2*
Base2*
memory layout
derived*
Base*
...
...
VFTablePtr
...
...
derived2*
Base2*
auto* p_derived = static_cast<Derived2*>(pBase2);
..B7A  cmp    qword ptr [pBase2],0  
..B82  je     main+0A8h (07FF720981B98h)  
..B84  mov    rax,qword ptr [pBase2]  
..B8B  sub    rax,28h  
..B8F  mov    qword ptr [rbp+1D8h],rax  
..B96  jmp    main+0B3h (07FF720981BA3h)  
..B98  mov    qword ptr [rbp+1D8h],0  
..BA3  mov    rax,qword ptr [rbp+1D8h]  
..BAA  mov    qword ptr [p_derived],rax  
auto* p_derived2 = static_cast<Derived2*>(pDerived);
..BB1  mov    rax,qword ptr [pDerived]  
..BB5  mov    qword ptr [p_derived2],rax  struct Base {
    ...
    virtual void foo() {}
};
struct Derived : Base {
    ...
};
struct Base2 {
    ...
}
struct Derived2 : Derived, Base2 {
    ...
}Derived2 d2;
Derived* pDerived = &d2;
Base2* pBase2 = &d2;
Base* pBase = &d2;
auto* p_derived = static_cast<Derived2*>(pBase2);
auto* p_derived2 = static_cast<Derived2*>(pDerived);
auto* p_derived3 = static_cast<Derived2*>(pBase);memory layout
derived*
Base*
...
...
VFTablePtr
...
...
derived2*
Base2*
struct Base {
    ...
    virtual void foo() {}
};
struct Derived : Base {
    ...
};
struct Base2 {
    ...
    virtual void bar() {}
}
struct Derived2 : Derived, Base2 {
    ...
}VFTablePtr
Derived2 d2;
Derived* pDerived = &d2;
Base* pBase = &d2;
Base2* pBase2 = &d2;
pDerived->foo();
pBase->foo();
pBase2->bar();the diamond problem
B
D1
D2
D12
"The deadly diagram of death"
the diamond problem
class Base {
    ...
};
class Derived1 : public Base {
    ...
};
class Derived2 : public Base {
    ...
};
class Derived12 : public Derived1, public Derived2 {
    ...
};the diamond problem
class Base { ... };
class Derived1 : public Base { ... };
class Derived2 : public Base { ... };
class Derived12 : public Derived1, public Derived2 { ... };derived1
Derived1::Base
...
...
...
...
derived12
Derived2
Derived2::Base
...
the diamond problem
struct Base {
    void bar() {}
};
struct Derived1 : Base { };
struct Derived2 : Base { };
struct Derived12 : Derived1, Derived2 { };Derived12 d;
d.bar(); // ambiguous
d.Derived1::bar();
d.Derived2::bar();
auto pBase = static_cast<Base*>(&d); // ambiguous
auto pBase1 = static_cast<Base*>(static_cast<Derived1*>(&d));
auto pBase2 = static_cast<Base*>(static_cast<Derived2*>(&d));
auto pD12 = static_cast<Derived12*>(pBase); // ambiguous
auto pD12_1 = static_cast<Derived12*>(static_cast<Derived1*>(pBase);
auto pD12_2 = static_cast<Derived12*>(static_cast<Derived1*>(pBase);
d.bar()
Derived12
Derived1
Derived1
Base
Base
ambiguous
the diamond problem
struct Base {
    virtual void bar() = 0;
};
struct Derived1 : Base { };
struct Derived2 : Base { };
struct Derived12 : Derived1, Derived2 { 
	virtual void bar() override {
        std::cout << "d12\n";
    }
};Derived12 d;
auto pDerived1 = static_cast<Derived1*>(&d);
auto pDerived2 = static_cast<Derived2*>(&d);
auto pBase1 = static_cast<Base*>(pDerived1);
auto pBase2 = static_cast<Base*>(pDerived2);
pDerived1->bar(); // ok
pDerived2->bar(); // ok
pBase1->bar(); // ok
pBase2->bar(); // okderived1
Derived1::Base
derived12
Derived2
Derived2::Base
VFTablePtr
VFTablePtr
the diamond problem
struct Base {
    virtual void bar() = 0;
};
struct Derived1 : Base { };
struct Derived2 : Base { };
struct Derived12 : Derived1, Derived2 { 
	virtual void bar() override {
        std::cout << "d12\n";
    }
};Derived12 d;
auto pDerived1 = static_cast<Derived1*>(&d);
auto pDerived2 = static_cast<Derived2*>(&d);
auto pBase1 = static_cast<Base*>(pDerived1);
auto pBase2 = static_cast<Base*>(pDerived2);
pDerived1->bar(); // ok
pDerived2->bar(); // ok
pBase1->bar(); // ok
pBase2->bar(); // okderived1
Derived1::Base
derived12
Derived2
Derived2::Base
VFTablePtr
VFTablePtr
Still two Base instances
virtual inheritance
struct Base {
    virtual void bar() = 0;
};
struct Derived1 : virtual Base { }; // instrusive
struct Derived2 : virtual Base { }; // instrusive
struct Derived12 : Derived1, Derived2 { 
	virtual void bar() override {
        std::cout << "d12\n";
    }
};derived1
derived12
Derived2
Base
VFTablePtr
virtual inheritance
VBasePtr
VBasePtr
Derived12 d12;
Derived1* pd1 = &d12;
Derived2* pd2 = &d12;
Base* pb = &d12;virtual inheritance
struct Base {
    virtual void bar() = 0;
};
struct Derived1 : virtual Base { }; // instrusive
struct Derived2 : virtual Base { }; // instrusive
struct Derived12 : Derived1, Derived2 { 
	virtual void bar() override {
        std::cout << "d12\n";
    }
};derived1
derived12
Derived2
Base
VFTablePtr
virtual inheritance
VBasePtr
VBasePtr
Derived12 d12;
Derived1* pd1 = &d12;
Derived2* pd2 = &d12;
Base* pb = &d12;auto pd12_1 = static_cast<Derived12*>(pd1); // ok
auto pd12_2 = static_cast<Derived12*>(pd1); // ok
auto pd12_3 = static_cast<Derived12*>(pb); // no, why??..virtual inheritance
struct Base {
    virtual void bar() = 0;
};
struct Derived1 : virtual Base { }; // instrusive
struct Derived2 : virtual Base { }; // instrusive
struct Derived12 : Derived1, Derived2 { 
	virtual void bar() override {
        std::cout << "d12\n";
    }
};derived1
derived12
Derived2
Base
VFTablePtr
virtual inheritance
VBasePtr
VBasePtr
Derived12 d12;
Derived1* pd1 = &d12;
Derived2* pd2 = &d12;
Base* pb = &d12;auto pd12_1 = static_cast<Derived12*>(pd1); // ok
auto pd12_2 = static_cast<Derived12*>(pd1); // ok
auto pd12_3 = dynamic_cast<Derived12*>(pb); // okvirtual inheritance
struct B {
    B() = delete;
    B(int);
};
struct D1 : virtual B {
    D1(int x) : B(x) {}
};
struct D2 : virtual B {
    D2(int x) : B(x) {}
};virtual inheritance
struct D12 : D1, D2 {
    D12(int x) : B(x), D1(x), D2(x) {}
};struct D12 : D1, D2 {
    D12(int x) : D1(x), D2(x) {}
};struct D : virtual B, D1, D2 {
    D(int x) : B(x), D1(x) {}
};virtual inheritance
struct B {
    B() = default;
    B(int);
};
struct D1 : virtual B {
    D1(int x) : B(x) {}
};
struct D2 : virtual B {
    D2(int x) : B(x) {}
};virtual inheritance
struct D12 : D1, D2 {
    D12(int x) : B(x), D1(x), D2(x) {}
};struct D12 : D1, D2 {
    D12(int x) : D1(x), D2(x) {}
};struct D : virtual B, D1, D2 {
    D(int x) : B(x), D1(x) {}
};virtual base classes should be default-constructible
base class construction order
struct AmphibiousSchoolBus : SchoolBus, AmphibiousVehiclestruct SchoolBus : SchoolVehicle, Busstruct SchoolVehicle : virtual LandVehiclestruct Bus : WheeledVehiclestruct WheeledVehicle : virtual LandVehiclestruct Vehiclestruct AmphibiousVehicle : virtual LandVehicle, virtual WaterVehiclestruct LandVehicle : virtual Vehiclestruct WaterVehicle : virtual Vehiclebase class construction order
AmphibiousSchoolBus
SchoolBus
AmphibiousVehicle
SchoolVehicle
Bus
LandVehicle
WaterVehicle
LandVehicle
WheeledVehicle
Vehicle
Vehicle
Vehicle
Vehicle
1
1
1
1
2
2
3
4
5
6
7
8
9
(Left to right DFS)
multiple inheritance
rtti
- multiple inheritance, inheriting (non)abstract classes
 - the diamond problem
 - virtual inheritance
 - delegation to sister class
 - polymorphic object
 - base class subobjects and addresses, rtti pointer
 - static type and dynamic type
 - <typeinfo>, typeid, std::type_info, std::bad_typeid
 - std::type_info::hash_code, std::type_index
 - std::type_info::name(), symbol (de)mangling
 - dynamic_cast, downcast, upcast, sidecast, std::bad_cast
 - std::variant, std::monostate, std::bad_variant_access
 - std::any, std::any_cast
 
RAII
inheritance
virtual
- 
	
constructor
 - 
	
member initializer list
 - 
	
destructor
 - 
	
default constructor
 - 
	
std::initializer_list
 - 
	
in-class initializer
 - 
	
copy ctor
 - 
	
cpy assign op
 - 
	
move ctor
 - 
	
mv assign op
 - 
	
default members
 - 
	
deleted members
 - 
	
rule of 0
 - 
	
rule of 3/5
 - 
	
smart-ptrs
 - 
	
converting constructor
 - 
	
explicit specifier
 
- 
	
is-a relation
 - 
	
has-a relation
 - 
	
public inheritance
 - 
	
protected members
 - 
	
using declaration
 - 
	
final specifier
 - 
	
private & protected inheritance
 - 
	
multiple inheritance
 - 
	
diamond problem
 - 
	
empty base optimization
 
- 
	
base method hiding problem (shadowing)
 - 
	
virtual methods
 - 
	
override specifier
 - 
	
dynamic type
- 
		
RTTI
 - 
		
dynamic_cast
 - 
		
typeid
 - 
		
vtables
 
 - 
		
 - 
	
by value passing problem (slicing)
 - 
	
pure virtual function
 - 
	
abstract class
 
misc
- 
	
friends
 - 
	
nested members
 - 
	
conversion operators
 - 
	
operator overloading
 - 
	
member pointers
 - 
	
unions
- 
		
std optional
 - 
		
std variant
 - 
		
std any
 
 - 
		
 
type ereasure
pimpl
enable shared from this with private inheritance
linkage and static (non-member) functions
Liskov substitution principle
function member ref qualification
constexpr virtual
virtual inheritance
aggregate, pod, literal type
implement class in cpp file
Multiple Inheritance
By Jan Bielak
Multiple Inheritance
- 824