struct Outer {
struct Nested {
};
};
int s;
class Outer {
int secret;
inline static int s;
static void f() {};
class Inner {
inline static int s;
void foo() {
f();
size_t secret_s = sizeof(secret);
secret = 3; // error: no instance
Outer{}.secret = 3; // ok
s = 4; // Outer::Inner::s
Outer::s = 5;
::s = 6;
}
};
};
class List {
public:
class iterator { ... };
...
};
class ManagerManager {
private:
class implementation { ... };
...
};
struct Person {
enum class Emotion {
HAPPY, SCARED,
ANGRY, DISGUSTED,
SAD, SURPRISED
} emotion;
};
class Graph {
public:
...
private:
class Impl;
std::unique_ptr<Impl> pImpl;
};
function overloading
operator overloading
float fma(float x, float y, float z) {
return x * y + z;
}
double fma(double x, double y, double z) {
return x * y + z;
}
...
std::ostream& operator<<(
std::ostream& os, rect r
) {
os << "{ width = " << r.width
<< ", height = " << r.height
<< " }";
return os;
}
std::string type_name(int) {
return "int";
}
std::string type_name(char) {
return "char";
}
...
Ad hoc polymorphism
Parametric polymorphism
Subtyping
static polymorphism
dynamic polymorhpism*
*constexpr virtual since C++20
function overloading
operator overloading
templates
virtual functions
float fma(float x, float y, float z) {
return x * y + z;
}
double fma(double x, double y, double z) {
return x * y + z;
}
...
std::ostream& operator<<(
std::ostream& os, rect r
) {
os << "{ width = " << r.width
<< ", height = " << r.height
<< " }";
return os;
}
#include <concepts>
template <std::floating_point F>
int fma(F x, F y, F z) {
return x * y + z;
}
template <typename T>
class Array {
private:
size_t size;
std::unique_ptr<T> data;
public:
...
};
class Base {
virtual void do();
};
class Derived : public Base {
virtual void do() override();
};
void doer(Base& b) {
b.do();
}
doer(Derived{});
dynamic polymorhpism*
interface
Base class
Derived class 1
Derived class 2
Derived class 3
interface
interface
interface
dynamic polymorhpism*
interface
Base class
Derived class 1
Derived class 2
Derived class 3
interface
interface
interface
dynamic polymorhpism*
interface
Base class
Derived class 1
Derived class 2
Derived class 3
interface
interface
interface
dynamic polymorhpism*
void interface(const Base& b) {
b.doSomething();
}
class Base {
public:
void doSomething() const {
std::cout << "Base\n";
};
};
class Derived1 : public Base {
public:
void doSomething() const {
std::cout << "D1\n";
};
};
class Derived2 : public Base {
public:
void doSomething() const {
std::cout << "D2\n";
};
};
class Derived3 : public Base {
public:
void doSomething() const {
std::cout << "D3\n";
};
};
dynamic polymorhpism*
void interface(const Base& b) {
b.doSomething();
}
class Base {
public:
void doSomething() const {
std::cout << "Base\n";
};
};
class Derived1 : public Base {
public:
void doSomething() const {
std::cout << "D1\n";
};
};
class Derived2 : public Base {
public:
void doSomething() const {
std::cout << "D2\n";
};
};
class Derived3 : public Base {
public:
void doSomething() const {
std::cout << "D3\n";
};
};
interface(Base{}); // Base
interface(Derived1{}); // Base
interface(Derived2{}); // Base
interface(Derived3{}); // Base
dynamic polymorhpism*
void interface(const Base& b) {
switch(b.type) {
case Base::typeIdx:
b.doSomething();
break;
case Derived1::typeIdx:
static_cast<const Derived1&>(b)
.doSomething();
break;
case Derived2::typeIdx:
static_cast<const Derived2&>(b)
.doSomething();
break;
case Derived3::typeIdx:
static_cast<const Derived3&>(b)
.doSomething();
break;
...
}
}
class Base {
public:
constexpr static size_t typeIdx = 193843867;
const size_t type = typeIdx;
protected:
Base(size_t derivedTypeIdx) :
type{derivedTypeIdx}
{
}
public:
Base() = default;
void doSomething() const {
std::cout << "Base\n";
};
};
class Derived1 : public Base {
public:
constexpr static size_t typeIdx = 949769248;
public:
Derived1() :
Base{typeIdx}
{
}
void doSomething() const {
std::cout << "Derived1\n";
};
};
...
interface(Base{}) // Base
interface(Derived1{}); // Derived1
interface(Derived2{}); // Derived2
interface(Derived3{}); // Derived3
dynamic polymorhpism*
void interface(const Base& b) {
switch(b.type) {
case Base::typeIdx:
b.doSomething();
break;
case Derived1::typeIdx:
static_cast<const Derived1&>(b)
.doSomething();
break;
case Derived2::typeIdx:
static_cast<const Derived2&>(b)
.doSomething();
break;
case Derived3::typeIdx:
static_cast<const Derived3&>(b)
.doSomething();
break;
...
}
}
class Base {
public:
constexpr static size_t typeIdx = 193843867;
const size_t type = typeIdx;
protected:
Base(size_t derivedTypeIdx) :
type{derivedTypeIdx}
{
}
public:
Base() = default;
void doSomething() const {
std::cout << "Base\n";
};
};
class Derived1 : public Base {
public:
constexpr static size_t typeIdx = 949769248;
public:
Derived1() :
Base{typeIdx}
{
}
void doSomething() const {
std::cout << "Derived1\n";
};
};
...
interface(Base{}) // Base
interface(Derived1{}); // Derived1
interface(Derived2{}); // Derived2
interface(Derived3{}); // Derived3
dynamic polymorhpism*
interface
Base class
dynamic polymorhpism*
interface
Base class
Derived class 1
Derived class 2
Derived class 3
interface
interface
interface
dynamic polymorhpism*
interface
Base class
Derived class 1
Derived class 2
Derived class 3
interface
interface
interface
dynamic polymorhpism*
void interface(const Base& b) {
b.doSomething();
}
class Base {
public:
void doSomething() const {
std::cout << "Base\n";
};
};
class Derived1 : public Base {
public:
void doSomething() const {
std::cout << "D1\n";
};
};
class Derived2 : public Base {
public:
void doSomething() const {
std::cout << "D2\n";
};
};
class Derived3 : public Base {
public:
void doSomething() const {
std::cout << "D3\n";
};
};
interface(Base{}); // Base
interface(Derived1{}); // Base
interface(Derived2{}); // Base
interface(Derived3{}); // Base
dynamic polymorhpism*
void interface(const Base& b) {
b.doSomething();
}
class Base {
public:
void(* const doSomething)();
Base() :
Base{[](){
std::cout << "Base\n";
}}
{
}
protected:
Base(void(*pfn)()) :
doSomething{pfn}
{
}
};
class Derived1 : public Base {
public:
Derived1() :
Base{[](){
std::cout << "Derived1\n";
}}
{
};
};
...
interface(Base{}) // Base
interface(Derived1{}); // Derived1
interface(Derived2{}); // Derived2
interface(Derived3{}); // Derived3
dynamic polymorhpism*
class Base {
public:
void(* const greet)();
int(* const lucky)();
double(* const transform)(double);
Base() :
Base{[](){
std::cout << "Base\n";
},
[](){
return 42;
},
[](double x){
return x * 2.0;
}}
{
}
protected:
Base(
void(*pfnGreet)(),
int(*pfnLucky)(),
double(*pfnTransform)(double)
) : greet{pfnGreet},
lucky{pfnLucky},
transform{pfnTransform}
{
}
};
class Derived1 : public Base {
public:
Derived1() :
Base{[](){
std::cout << "Derived1\n";
},
[](){
return 13;
},
[](double x){
return x + 158.3;
}}
{
};
};
dynamic polymorhpism*
class Derived1 : public Base {
public:
Derived1() :
Base{[](){
std::cout << "Derived1\n";
}} // error, 2 arguments missing
{
};
};
class Base {
public:
void(* const greet)();
int(* const lucky)();
double(* const transform)(double);
Base() :
Base{[](){
std::cout << "Base\n";
},
[](){
return 42;
},
[](double x){
return x * 2.0;
}}
{
}
protected:
Base(
void(*pfnGreet)(),
int(*pfnLucky)(),
double(*pfnTransform)(double)
) : greet{pfnGreet},
lucky{pfnLucky},
transform{pfnTransform}
{
}
};
dynamic polymorhpism*
class Base {
public:
void(* const greet)();
int(* const lucky)();
double(* const transform)(double);
Base() :
Base{[](){
std::cout << "Base\n";
}}
{
}
protected:
Base(
void(*pfnGreet)(),
int(*pfnLucky)() = [](){
return 42;
},
double(*pfnTransform)(double) = [](double x){
return x * 2.0;
}
) : greet{pfnGreet},
lucky{pfnLucky},
transform{pfnTransform}
{
}
};
class Derived1 : public Base {
public:
Derived1() :
Base{[](){
std::cout << "Derived1\n";
}} // ok
{
};
};
greet
lucky
transform
greet
lucky
transform
[](){ std::cout << "Base\n"; }
[](double x){ return x * 2.0; }
[](){ return 42; }
greet
lucky
transform
greet
lucky
transform
dynamic polymorhpism*
[](){ std::cout << "Base\n"; }
[](double x){ return x * 2.0; }
[](){ return 42; }
vtable
vtable
vtable
vtable
dynamic polymorhpism*
class Base {
protected:
struct Vtable {
void(* const greet)();
int(* const lucky)();
double(* const transform)(double);
};
const Vtable& vptr;
private:
constexpr static Vtable vtable{
[](){
std::cout << "Base\n";
},
[](){
return 42;
},
[](double x){
return x * 2.0;
}
};
public:
Base(const Vtable& vt = vtable) :
vptr{vt}
{
}
void greet() const { vptr.greet(); }
int lucky() const { return vptr.lucky(); }
double transform(double x) const { return vptr.transform(x); }
};
class Derived1 : public Base {
private:
constexpr static Vtable vtable{
[](){
std::cout << "Derived1\n";
},
[](){
return 13;
},
[](double x){
return x + 138.5;
}
};
public:
Derived1() :
Base{vtable}
{
}
};
functions
virtual
class Base {
public:
virtual void greet() const {
std::cout << "Base\n";
}
virtual int lucky() const {
return 42;
}
virtual double transform(double x) const {
return x * 2.0;
}
virtual ~Base() = default;
};
class Derived1 : public Base {
public:
virtual void greet() const override {
std::cout << "Derived1\n";
}
virtual int lucky() const override {
return 13;
}
virtual double transform(double x) const override {
return x * 158.3;
}
};
void interface(const Base& b) {
b.doSomething();
}
interface(Base{}) // Base
interface(Derived1{}); // Derived1
interface(Derived2{}); // Derived2
interface(Derived3{}); // Derived3
functions
virtual
class Base {
public:
virtual void method() {
...
}
virtual ~Base() = default;
}
class Derived : public Base {
public:
virtual void method() override {
...
}
};
void user(Base& b) {
b.method(); // <- dynamic dispatch
}
Must match:
Return types can (either):
class Base {
virtual void method() {
...
}
virtual ~Base() = default;
}
class Derived1 : public Base {
virtual void method() override {
...
}
};
class Derived2 : public Derived1 {
virtual void method() override {
...
}
};
class Derived3 : public Derived2 {
};
void user(Base& b) {
b.method(); // calls the final overrider
}
Base::method
Derived1::method
Derived2::method
Derived2::method
destructor
virtual
class Base {
virtual void method() {
...
}
public:
virtual ~Base() = default;
}
class Derived : public Base {
virtual void method() override {
...
}
};
class Base {
virtual void method() {
...
}
protected:
~Base() = default;
}
class Derived : public Base {
virtual void method() override {
...
}
};
{
Base* b = new Derived();
delete b; // UB
}
{
Base* b = new Derived();
delete b; // dynamic dispatch
}
{
Base* b = new Derived();
delete b; // ill-formed
}
override
class PostIncrementableString {
private:
std::string data;
public:
virtual PostIncrementableString operator++(int) {
...
}
};
class BetterPostIncrementableString : public PostIncrementableString {
public:
// oops! forgot int parameter, original method is hidden
virtual BetterPostIncrementableString& operator++() {
...
}
};
override
class PostIncrementableString {
private:
std::string data;
public:
virtual PostIncrementableString operator++(int) {
...
}
};
class BetterPostIncrementableString : public PostIncrementableString {
public:
// now the compiler can find the mistake
virtual BetterPostIncrementableString& operator++() override {
...
}
};
override
class PostIncrementableString {
private:
std::string data;
public:
virtual PostIncrementableString operator++(int) {
...
}
};
class BetterPostIncrementableString : public PostIncrementableString {
public:
// now the compiler can find the mistake
virtual BetterPostIncrementableString& operator++(int) override {
...
}
};
class Dog {
public:
virtual std::string bark() {
return "bark";
}
virtual ~Dog() = default;
};
class Terrier : public Dog {
public:
virtual std::string bark() override {
return "hahauau";
}
};
class AustralianTerrier final : public Terrier {
public:
virtual std::string bark() override {
return "ɥɐɥɐnɐn";
}
};
class Boxer final : public Dog {
public:
virtual std::string bark() override {
return "Borke";
}
};
final
final
final
class Base {
public:
virtual void f();
virtual void g();
virtual ~Base() = default;
};
class D1 : public Base {
public:
virtual void f() override;
virtual void g() override final;
virtual ~Base() = default;
};
class D2 : public D1 {
public:
virtual void f() override;
virtual void g() override; // ERROR
virtual ~Base() = default;
};
class Base {
public:
virtual int f() { return 42; };
virtual ~Base() = default;
};
class D : public Base {
public:
int a, b;
virtual void f() override { return a + b; }
};
void user(Base b) {
std::cout << b.f();
}
user(Derived{}); // 42
Derived d{2, 3};
Base& b = d;
b = Derived{1, 4};
// d.b still == 3
class Base {
public:
virtual int f() { return 42; };
virtual ~Base() = default;
};
class D : public Base {
public:
int a, b;
virtual void f() override { return a + b; }
};
void user(const Base& b) {
std::cout << b.f();
}
user(Derived{}); // 0
Derived d{2, 3};
Derived& b = d;
b = Derived{1, 4};
// d.b == 4
private implementation
public interface
no implementation
public interface
struct Shape {
virtual void draw() const {
??? draw what ???
}
virtual ~Shape() = default;
}
struct Triangle : Shape {
virtual void draw() const override {
...
}
}
struct Rectangle : Shape {
virtual void draw() const override {
...
}
}
struct Circle : Shape {
virtual void draw() const override {
...
}
}
???
struct Shape {
// pure virtual function
virtual void draw() = 0;
virtual ~Shape() = default;
}
struct Triangle : Shape {
virtual void draw() const override {
...
}
}
struct Rectangle : Shape {
virtual void draw() const override {
...
}
}
struct Circle : Shape {
virtual void draw() const override {
...
}
}
<undefined>
class Base {
virtual void f() = 0; // pure
virtual void g() = 0; // pure
virtual void h() {};
public:
virtual ~Base() = default;
}
class Derived1 : public Base {
virtual void f() override {};
// g still pure
}
class Derived2 : public Derived1 {
virtual void g() override {};
}
abstract
abstract
non-abstract
class Abstract {
public:
virtual void something() = 0;
virtual ~Abstract();
}
Abstract a{};
void f(Abstract a);
Abstract f();
static_cast<Abstract>(derived)
class Abstract {
public:
virtual void something() = 0;
virtual ~Abstract();
}
const Abstract& a = Derived{};
void f(Abstract& a);
Abstract& f();
static_cast<Abstract&>(derived)
class IRenderable {
public:
virtual void render() const = 0;
virtual ~IRenderable() = default;
}
class Shape {
public:
double x, y;
virtual void draw() const = 0;
virtual double perimiter() const = 0;
virtual double area() const = 0;
virtual ~Shape() = default;
}
class IDatabase {
public:
virtual std::string& find(const std::string& key) = 0;
virtual void insert(const std::string& key, const std::string& value) = 0;
std::string& operator[](const std::string& key) {
return find(key);
}
virtual ~IDatabase() = default;
}
class Shape {
public:
double x, y;
virtual void draw() const = 0;
virtual double perimiter() const = 0;
virtual double area() const = 0;
virtual ~Shape() = default;
}
class CompoundShape : public Shape {
private:
std::vector<std::unique_ptr<Shape>> pieces;
public:
virtual void draw() const {
for (const auto& shape : pieces) {
shape->draw();
}
}
virtual double perimiter() const {
double sum = 0;
for (const auto& shape : pieces) {
sum += shape->perimiter();
}
return sum;
}
virtual double area() const {
double sum = 0;
for (const auto& shape : pieces) {
sum += shape->area();
}
return sum;
}
};
class Entity {
public:
Vector<2> position;
virtual void update(Duration) = 0;
virtual void onCollision(Collider) = 0;
...
};
class Player : public Entity {
public:
virtual void update(Duration deltaTime) {
position = Input.getMoveDelta() * deltaTime;
if (Input.pressed(Input::Fire)) {
shoot();
}
...
}
virtual void onCollision(Collider col) {
if (col.type == Collider::Type::Bullet) {
...
}
...
}
};
class Enemy : public Entity {
public:
virtual void update(Duration deltaTime) {
position += playerDir() * deltaTime;
if (inSight(player)) {
shoot();
}
...
}
virtual void onCollision(Collider col) {
if (col.type == Collider::Type::Bullet) {
...
}
...
}
};
vector<std::unique_ptr<Entity>> entities;
for (auto entt : entities) {
entt->update();
}
To be continued...
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