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); // errorTriangle t{2.0, 3.0};
t.area(); // ok
t.base = 3.0; // ok
t.scale(4.0); // okclass 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.................sizealigmentstorage durationtypevaluenamelifetime(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 deallocatedTo be continued...
Part I