OOp in C++

Part I

Programming paradigms

int fact(int x) {
    return (x <= 2)
        ? 1
        : fact(x - 1) + fact(x - 2);
}
int fact(int x) {
    int n = 1, i = 2;
    for (; i <= x; ++i) {
        n *= i;
    }
    return n;
}

Imperative

Declarative

Programming paradigms

image/svg+xml Shared state Deterministiclogic programming Relational & logicprogramming Constraint (logic)programming Concurrent constraintprogramming Lazy concurrentconstraintprogramming Logic and constraints Descriptivedeclarativeprogramming First-orderfunctional programming Functionalprogramming Continuationprogramming Monotonicdataflowprogramming Declarativeconcurrentprogramming Lazydataflowprogramming Abstract data typefunctionalprogramming Nonmonotonicdataflowprogramming Concurrent logicprogramming Functional reactiveprogramming Weak synchronousprogramming Strong synchronousprogramming Abstract data typeimperativeprogramming Multi-agentdataflowprogramming Dataflow andmessage passing Event-loopprogramming Multi-agentprogramming Message-passingconcurrentprogramming Active objectprogramming Object-capacityprogramming Dataflow and Message passing Shared state Softwaretransactionalmemory (STM) Concurrentobject-orientedprogramming Shared-stateconcurrentprogramming Sequentialobject-orientedprogramming Statefulfunctionalprogramming Imperativeprogramming Imperativesearchprogramming Unnamed state (sequencial or concurrent) Named state More ImperativeParadigms More declarativeParadigms Lazyfunctionalprogramming Lazydeclarativeconcurrentprogramming Functional record procedure closure instantaneouscomputation synchronisation on partial termination nondeterministicchoice name (unforgetableconstant) port (channel) thread local cell closure thread log search cell (state) continuation thread single assignment by-needsynchronization unification(equality) search solver thread by-need synchronisation nondetermined State Markup languages(only Datastructures) Turing complete Languages Undeterministic state Observable Nondeterminism

imperative

    int n = 1;
loop:
    int i = 2;
    n *= i;
    ++i;
    if (i > 10) return n;
    // no loop, only goto
    goto loop;
int fact(int x) {
    // variables on top
    int n = 1, i = 2;
    // for loop
    for (; i <= x; ++i) {
        n *= i;
    }
    return n; // single return
}

int n = fact(10);

Unstructured

Structured

Imperative

declarative

// modifies parameter, has side effects
void procedural(std::vector<int>& nums) {
    // sequentially executed procedures
    std::erase_if(nums, [&](int x){ return x % 2 == 0; });
    std::ranges::sort(nums);
    std::ranges::for_each(nums, [&](int& x) { x += nums.size(); });
}
// no side effects, returns result
std::vector<int> functional(const std::vector<int>& nums) {
    // nested function invocation
    return for_each(
        sort(
            erase_if(
                nums,
                [&](int x){ return x % 2 == 0; }
            )
        ),
        [&](int& x){ return x % 2 == 0; }
    );
}

(Procedural)

(Functional)

template<typename T, typename Func>
T helper(T input, Func&& func) {
    func(input);
    return input;
}
template<typename T, typename Func>
T for_each(const T& input, Func&& func) {
    return helper(
        input,
        [&](T& input){ 
            std::ranges::for_each(input, func);
        }
    );
}
template<typename T, typename Func>
T erase_if(const T& input, Func&& func) {
    return helper(
        input,
        [&](T& input){ 
            std::erase_if(input, func);
        }
    );
}
template<typename T>
T sort(const T& input) {
    return helper(
        input,
        [&](T& input){ 
            std::ranges::sort(input);
        }
    );
}

Imperative

declarative

// modifies parameter, has side effects
void procedural(std::vector<int>& nums) {
    // sequentially executed procedures
    std::erase_if(nums, [&](int x){ return x % 2 == 0; });
    std::ranges::sort(nums);
    std::ranges::for_each(nums, [&](int& x) { x += nums.size(); });
}
// no side effects, returns result
std::vector<int> functional(const std::vector<int>& nums) {
    // nested function invocation
    return for_each(
        sort(
            erase_if(
                nums,
                [&](int x){ return x % 2 == 0; }
            )
        ),
        [&](int& x){ return x % 2 == 0; }
    );
}

(Procedural)

(Functional)

  • Data stored in data structures (variables)
  • Algorithms (functions) manipulate and mutate data structures
  • Statements executed sequentially
  • Mutable state

Procedural

Object oriented

  • Data (fields) and operations (methods) form objects
  • Methods manipulate and mutate data owned by objects

Imperative

Functional

  • When necessary, state is simulated (ex. monads)
  • Functions manipulate data and are (mostly) pure
  • Function invocations form trees of expressions
  • Minimal state

Declarative

Object oriented programming

Class

object

field

Method

ENCAPSULATION

inheritance

polymorphism

Object oriented programming

Class

private implementation

public interface

Object

Class

Class

Object

Object

Object

Object

Object

Object

Object

Object

class Dictionary {
private:
  // complex data structures
  // hidden from user
public:
  // stable interface 
  // regardless of implementation
  Value find(Key);
  void insert(Key, Value);
  void erase(Key);
}

object

s

ENCAPSULATION

Object oriented programming

Class

private implementation

public interface

Object

Class

Class

Object

Object

Object

Object

Object

Object

Object

Object

messages

Object oriented programming

Person

mood, thoughts, ...

Anna

Bank

atm

Jerry

Tom

Bob

Century Bank

MasterBank

Your Vault

Mall ATM 2

Mall ATM 1

talk

cheer

deposit

transfer

balance

withdraw

talk

cheer

...

Object oriented programming

Class

fields (data members)

methods (member functions)

private

public

fields (data members)

methods (member functions)

class Person {
private:
    std::map<Emotion, Strength> mood;
    std::vector<Thaught> mind;
    ...
public:
    void cheer(float strength) {
        mood.at("happy").increase(strength);
        ...
    }
    ...
};

Object oriented programming

class Triangle {
public:
    float base, height;

    // does not modify object
    // can be called on a const Triangle
    float area() const {
        return base * area / 2.0;
    }
    
    // potentially modifies object
    // cannot be called on a const Triangle
    float scale(float factor) {
        base *= scale;
        height *= scale;
    }
};

Const member functions

const Triangle t{2.0, 3.0};
t.area(); // ok
t.base = 3.0; // error
t.scale(4.0); // error
Triangle t{2.0, 3.0};
t.area(); // ok
t.base = 3.0; // ok
t.scale(4.0); // ok

Object oriented programming

class Triangle {
public:
    float base, height;

    float area(/* const Triangle* this */) const {
        return this->base * this->area / 2.0;
    }
    
    float scale(/* Triangle* this, */ float factor) {
        this->base *= scale;
        this->height *= scale;
    }
};

this pointer

Object oriented programming

this pointer

class Triangle {
public:
    float base, height;

    float area() const {
        return base * area / 2.0;
    }

    float scale(float factor) {
        base *= scale;
        height *= scale;
    }
    
    void swap(Triangle& other) {
        if (this == &other)
            return;
        std::swap(base, other.base);
        std::swap(height, other.height);
    }
};

Object oriented programming

static members

class Circle {
private:
    static constexpr float PI = 3.141592653;
    
    static hypot2(float x, float y) {
        return x * x + y * y;
    }
public:
    float radius;
    
    float area() const {
        return PI * radius * radius;
    }
    float circumference() const {
        return 2.0 * PI * radius;
    }
    bool contains(float x, float y) const {
        return hypot2(x, y) <= radius * radius;
    }
    
    void swap(Circle& other) {
        std::swap(radius, other.radius);
    }
    static void swap(Circle& lhs, Circle& rhs) {
        lhs.swap(rhs);
    }
};
Circle c1{2.0}, c2{3.0};
Circle::swap(c1, c2);

Object oriented programming

y
x
p_y
p_x
p_\theta
p_r
p_x=r\sin(p_\theta)
p_r=\sqrt{p_x^2*p_y^2}
p_\theta=\tan^{-1} (\frac{y}x)
p_y=r\cos(p_\theta)
p

Object oriented programming

class Point {
private:
    // ...
public:
    static Point fromPolar(double angle, double radius);
    static Point fromCartesian(double x, double y);

    double x() const;
    void x(double newX);
    double y() const;
    void y(double newY);
    double angle() const;
    void angle(double newAngle);
    double radius() const;
    void radius(double newRadius);
};
y
x
p_y
p_x
p_\theta
p_r
p_x=r\sin(p_\theta)
p_r=\sqrt{p_x^2*p_y^2}
p_\theta=\tan^{-1} (\frac{y}x)
p_y=r\cos(p_\theta)
p

Object oriented programming

class Point {
private:
    // ...
public:
    [[nodiscard]] constexpr static Point fromPolar(double angle, double radius) noexcept;
    [[nodiscard]] constexpr static Point fromCartesian(double x, double y) noexcept;

    [[nodiscard]] constexpr double x() const noexcept;
    constexpr void x(double newX) noexcept;
    [[nodiscard]] constexpr double y() const noexcept;
    constexpr void y(double newY) noexcept;
    [[nodiscard]] constexpr double angle() const noexcept;
    constexpr void angle(double newAngle) noexcept;
    [[nodiscard]] constexpr double radius() const noexcept;
    constexpr void radius(double newRadius) noexcept;
};
y
x
p_y
p_x
p_\theta
p_r
p_x=r\sin(p_\theta)
p_r=\sqrt{p_x^2*p_y^2}
p_\theta=\tan^{-1} (\frac{y}x)
p_y=r\cos(p_\theta)
p

Object oriented programming

class Point {
private:
    double _x, _y;
public:
    static Point fromPolar(double angle, double radius) {
        Point p;
        p._x = sin(angle) * radius;
        p._y = cos(angle) * radius;
        return p;
    }
    static Point fromCartesian(double x, double y) {
        Point p;
        p._x = x;
        p._y = y;
        return p;
    }

    double x() {
        return _x;
    }
    void x(double newX) {
        _x = newX;
    }
    double y() {
        return _y;
    }
    void y(double newY) {
        _y = newY;
    }

    double angle() {
        return atan2(_y, _x);
    }
    void angle(double newAngle) {
        (*this) = fromPolar(newAngle, radius());
    }
    double radius() {
        return hypot(_x, _y);
    }
    void radius(double newRadius) {
        (*this) = fromPolar(angle(), newRadius);
    }
};
class Point {
private:
    double _angle, _radius;
public:
    static Point fromPolar(double angle, double radius) {
        Point p;
        p._angle = angle;
        p._radius = radius;
        return p;
    }
    static Point fromCartesian(double x, double y) {
        Point p;
        p._angle = atan2(y, x);
        p._radius = hypot(x, y);
        return p;
    }

    double x() {
        return sin(_angle) * _radius;
    }
    void x(double newX) {
        (*this) = fromCartesian(newX, y());
    }
    double y() {
        return cos(_angle) * _radius;
    }
    void y(double newY) {
        (*this) = fromCartesian(x(), newY);
    }

    double angle() {
        return _angle;
    }
    void angle(double newAngle) {
        _angle = newAngle;
    }
    double radius() {
        return _radius;
    }
    void radius(double newRadius) {
        _radius = newRadius;
    }
};

Object oriented programming

class Point {
private:
    double _x, _y;
public:
    [[nodiscard]] constexpr static Point fromPolar(double angle, double radius) noexcept {
        Point p{};
        p._x = sin(angle) * radius;
        p._y = cos(angle) * radius;
        return p;
    }
    [[nodiscard]] constexpr static Point fromCartesian(double x, double y) noexcept {
        Point p{};
        p._x = x;
        p._y = y;
        return p;
    }

    [[nodiscard]] constexpr double x() const noexcept {
        return _x;
    }
    constexpr void x(double newX) noexcept  {
        _x = newX;
    }
    [[nodiscard]] constexpr double y() const noexcept   {
        return _y;
    }
    constexpr void y(double newY) noexcept  {
        _y = newY;
    }

    [[nodiscard]] constexpr double angle() const noexcept   {
        return atan2(_y, _x);
    }
    constexpr void angle(double newAngle) noexcept  {
        (*this) = fromPolar(newAngle, radius());
    }
    [[nodiscard]] constexpr double radius() const noexcept  {
        return hypot(_x, _y);
    }
    constexpr void radius(double newRadius) noexcept  {
        (*this) = fromPolar(angle(), newRadius);
    }
};

Object oriented programming

Class

object

field

Method

ENCAPSULATION

inheritance

polymorphism

sizeof(name)
alignof(name)
decltype(name)
name
#name
.................
size
aligment
storage duration
type
value
name
lifetime
(different than typeid(name))
(optional)
(may be undefined)

object

.................

storage duration

automatic

dynamic

thread

static

storage duration

automatic

dynamic

thread

static

storage duration

automatic

dynamic

thread

static

some_t global_var; // static
extern some_t defined_elsewhere_var; // automatic

void f() {
    int x; // automatic
    static int y; // static
}
int counter() {
    static int value = 0;
    return value++; 
}

int main() {
    for (int i = 0; i < 10; ++i) {
        std::cout << counter();
        // 0 1 2 3 4 5 6 7 8 9 
    }
}

storage duration

automatic

dynamic

thread

static

type* ptr = new type;
type* ptr = new type(init-args);
type* ar = new type[size];
type* ar = new type[size](init-args);
delete ptr;
delete[] ar;

storage duration

automatic

dynamic

thread

static

// transfers ownership
int* multiplesOfThree(int count) {
    int* ar = new int[count];
    for (int i = 0; i < count; ++i) {
        ar[i] = 3 * i;
    }
    return ar;
}

storage duration

automatic

dynamic

thread

static

{
    int* number = new int(5);
    if (!(number = nullptr)) // typo, leak
        f(number);
    delete number; // noop
}
{
    int* number = new int(5);
} // forgot to delete, leak
{
    int* number = new int(5);
    int* ar = new int[100'000'000]; // leak if throws
    delete[] ar;
    delete number;
}

memory leaks

storage duration

automatic

dynamic

thread

static

{
    std::unique_ptr<int> number = std::make_unique<int>(5);
    if (!(number = nullptr)) // typo, no leak
        f(number);
}
{
    auto number = std::make_unique<int>(5);
} //  automatically deleted, no leak
{
    auto number = std::make_unique<int>(5);
    auto ar = std::make_unique<int[]>(100'000'000);
    // no leak if throws
}

RAII and lifetime

  1. Storage allocation or reuse
  2. Object construction
  3.  ... Object is alive ...
  4. Object destruction
  5. Storage deallocation or reuse
{
    std::string s;
    // storage allocated on stack
    // std::string constructor invoked
    //    internal string buffer allocated
    //    size set to 0
}
// storage deallocated on stack
// std::string destructor invoked
//    internal string buffer deallocated

inheritance

polymorphism

&

To be continued...

OOP in C++

Part I

  • Imperative
    • Unstructured
    • Structured
    • Procedural
    • Object Oriented
  • Declarative
    • Functional
  • Concurrent
  • Automata-based
  • Event-driven
  • Metaprogramming
    • Macro
    • Template
    • Reflective
  • Class
  • Object
  • Member
    • Field
    • Static
    • Method
      • Const
      • Virtual
    • Mutable
    • Access
    • Bitfield
  • this pointer
  • Friend
  • Inheritance
    • public, private, protected
  • User defined literal
  • User defined conversion operator
  • Operator overloading
  • RAII
  • memory
    • automatic
    • dynamic
      • new, delete
      • smart ptrs
    • thread-local
    • static
  • Member pointers
  • RTTI
  • dynamic_cast
  • typeid
  • Unions
    • std optional
    • std variant
    • std any
  • Rule of 0
  • Rule of 5
  • nested class
  • empty base optimization

OOP in C++ Part I

By Jan Bielak

OOP in C++ Part I

A presentation about programming paradigms and their applications to C++. It is presented here: https://www.youtube.com/watch?v=fzpzCVNQva8 .

  • 232