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
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
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);
};
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;
};
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
- Storage allocation or reuse
- Object construction
- ... Object is alive ...
- Object destruction
- 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 .
- 651