Part I
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
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
// 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);
}
);
}
// 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)
Procedural
Object oriented
Imperative
Functional
Declarative
private implementation
public interface
Object
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);
}
s
private implementation
public interface
Object
Object
Object
Object
Object
Object
Object
Object
Object
messages
mood, thoughts, ...
Anna
Jerry
Tom
Bob
Century Bank
MasterBank
Your Vault
Mall ATM 2
Mall ATM 1
talk
cheer
deposit
transfer
balance
withdraw
talk
cheer
...
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);
...
}
...
};
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 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
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;
}
};
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);
}
};
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);
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);
};
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;
};
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;
}
};
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);
}
};
sizeof(name)
alignof(name)
decltype(name)
name
#name
.................
size
aligment
storage duration
type
value
name
lifetime
(different than typeid(name))
(optional)
(may be undefined)
.................
automatic
dynamic
thread
static
automatic
dynamic
thread
static
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
}
}
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;
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;
}
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;
}
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
}
{
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
To be continued...
Part I