class Polymorphic1 {
virtual void f() {}
}
class Polymorphic2 : public Polymorphic1 {
}
class Polymorphic3 {
virtual void f() final {}
}
class Polymorphic4 {
virtual void f() = 0;
}
class Base {
public:
virtual ~Base();
};
class Derived : public Base {
}
Base* p = new Derived;
// static type Base
// dynamic type Derived
dynamic_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); // nullptr
Base* 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_cast
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 { ... };
class TuringMachine {
...
};
class ElectronicDevice {
...
};
class Computer : public TuringMachine, public ElectronicDevice {
...
};
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;
}
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
}
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
}
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
}
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
}
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
}
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
}
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
}
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
}
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
}
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
}
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
}
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
}
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:
...
};
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:
...
};
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:
...
};
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:
...
};
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
}
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;
...
};
struct Base {
int x, y;
};
struct Derived : Base {
int z, w;
};
x
y
z
w
Derived der;
Base* pBase = &der;
Derived* pDerived = static_cast<Derived*>(pBase);
struct Base {
int x, y;
virtual void foo() {}
};
struct Derived : Base {
int z, w;
};
x
y
z
w
VFTablePtr
Derived der;
Base* pBase = &der;
Derived* pDerived = static_cast<Derived*>(pBase);
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
...
...
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);
...
...
...
...
VFTablePtr
...
...
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);
...
...
VFTablePtr
...
...
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();
class Base {
...
};
class Derived1 : public Base {
...
};
class Derived2 : public Base {
...
};
class Derived12 : public Derived1, public Derived2 {
...
};
class Base { ... };
class Derived1 : public Base { ... };
class Derived2 : public Base { ... };
class Derived12 : public Derived1, public Derived2 { ... };
...
...
...
...
...
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
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(); // ok
VFTablePtr
VFTablePtr
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(); // ok
VFTablePtr
VFTablePtr
Still two Base instances
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";
}
};
VFTablePtr
VBasePtr
VBasePtr
Derived12 d12;
Derived1* pd1 = &d12;
Derived2* pd2 = &d12;
Base* pb = &d12;
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";
}
};
VFTablePtr
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??..
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";
}
};
VFTablePtr
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); // ok
struct B {
B() = delete;
B(int);
};
struct D1 : virtual B {
D1(int x) : B(x) {}
};
struct D2 : virtual B {
D2(int x) : B(x) {}
};
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) {}
};
struct B {
B() = default;
B(int);
};
struct D1 : virtual B {
D1(int x) : B(x) {}
};
struct D2 : virtual B {
D2(int x) : B(x) {}
};
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
struct AmphibiousSchoolBus : SchoolBus, AmphibiousVehicle
struct SchoolBus : SchoolVehicle, Bus
struct SchoolVehicle : virtual LandVehicle
struct Bus : WheeledVehicle
struct WheeledVehicle : virtual LandVehicle
struct Vehicle
struct AmphibiousVehicle : virtual LandVehicle, virtual WaterVehicle
struct LandVehicle : virtual Vehicle
struct WaterVehicle : virtual Vehicle
AmphibiousSchoolBus
SchoolBus
AmphibiousVehicle
SchoolVehicle
Bus
LandVehicle
WaterVehicle
LandVehicle
WheeledVehicle
Vehicle
Vehicle
Vehicle
Vehicle
(Left to right DFS)
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