polymorphism
Nested classes
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;
}
};
};
Nested classes
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;
};
polymorphism
polymorphism
AD Hoc
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";
}
...
polymorphism
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{});
- bounded polymorphism
polymorphism
dynamic polymorhpism*
dynamic
interface
Base class
Derived class 1
Derived class 2
Derived class 3
interface
interface
interface
polymorphism
dynamic polymorhpism*
dynamic
interface
Base class
Derived class 1
Derived class 2
Derived class 3
interface
interface
interface
polymorphism
dynamic polymorhpism*
dynamic
interface
Base class
Derived class 1
Derived class 2
Derived class 3
interface
interface
interface
polymorphism
dynamic polymorhpism*
dynamic
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";
};
};
polymorphism
dynamic polymorhpism*
dynamic
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
polymorphism
dynamic polymorhpism*
dynamic
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
polymorphism
dynamic polymorhpism*
dynamic
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
polymorphism
dynamic polymorhpism*
dynamic
interface
Base class
polymorphism
dynamic polymorhpism*
dynamic
interface
Base class
Derived class 1
Derived class 2
Derived class 3
interface
interface
interface
polymorphism
dynamic polymorhpism*
dynamic
interface
Base class
Derived class 1
Derived class 2
Derived class 3
interface
interface
interface
polymorphism
dynamic polymorhpism*
dynamic
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
polymorphism
dynamic polymorhpism*
dynamic
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
polymorphism
dynamic polymorhpism*
dynamic
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;
}}
{
};
};
polymorphism
dynamic polymorhpism*
dynamic
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}
{
}
};
polymorphism
dynamic polymorhpism*
dynamic
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
{
};
};
polymorphism
dynamic
Base
greet
lucky
transform
Base
greet
lucky
transform
[](){ std::cout << "Base\n"; }
[](double x){ return x * 2.0; }
[](){ return 42; }
Base
greet
lucky
transform
Base
greet
lucky
transform
polymorphism
dynamic polymorhpism*
dynamic
[](){ std::cout << "Base\n"; }
[](double x){ return x * 2.0; }
[](){ return 42; }
Base
vtable
Base
vtable
Base
vtable
Base
vtable
polymorphism
dynamic polymorhpism*
dynamic
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
}
- name
- parameter types
- cv-qualifiers
- ref-qualifiers
Must match:
Return types can (either):
- match
- be covariant - be pointers/references to a base and derived class
final overrider
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;
};
Slicing
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
Slicing
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
abstract classes
Class
private implementation
public interface
abstract Class
no implementation
public interface
abstract classes
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 {
...
}
}
???
abstract classes
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>
abstract classes
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
abstract classes
class Abstract {
public:
virtual void something() = 0;
virtual ~Abstract();
}
Abstract a{};
void f(Abstract a);
Abstract f();
static_cast<Abstract>(derived)
abstract classes
class Abstract {
public:
virtual void something() = 0;
virtual ~Abstract();
}
const Abstract& a = Derived{};
void f(Abstract& a);
Abstract& f();
static_cast<Abstract&>(derived)
interfaces
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;
}
interfaces
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;
}
};
interfaces
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();
}
rtti
To be continued...
polymorphism
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
Polymorphism
By Jan Bielak
Polymorphism
A presentation about polymorphism: ad hoc polymorphism - function overloading and operator overloading, parametric polymorphism - templates - and dynamic polymorphism - virtual functions as well as nested classes. Also on the show: vtables, abstract classes, override, final, interfaces and slicing.
- 719