2.7 Type conversions

static_cast – ключевое слово языка C++

T x;
static_cast<U>(x);

С помощью него неявные преобразования можно сделать явно через static_cast, такие как double -> int, A* -> void*.

reinterpret_cast – говорит компилятору трактовать переменную x как переменную типа U побитово. То есть интерпретировать память как будто бы там лежит другой тип.

  1. reinterpret_cast нужно делать от ссылки, например, reinterpret_cast<double&>(y).
long long y;
double &d = reinterpret_cast<double&>(y); // UB
d = 3.14;
std::cout << y << std::endl; // какая-то дичь
  1. По умолчанию это делать нельзя, поэтому в примере выше это UB. Это можно делать, например, если есть две структуры в которых типы расположены в одном и том же порядке.

  2. reinterpret_cast также можно применять к указателям, но только к совместимым

  3. reinterpret_cast не позволяет обходить const.

Для обхода есть const_cast. Он позволяет снять константность.

const int c = 5;
int &cc = const_cast<int&>(c); // также нужно давать ссылку
cc = 7; // UB
// и также const_cast от ссылки и указателя это две разные сущности
std::cout << c << ' ' << cc << std::endl;

Попытка изменить переменную, которая изначально была константой это UB.

C-style cast. Всегда бан в программах на C++. По сути он последовательно перебирает все возможные касты, пока он не подойдет. Например, вы можете не заметить, как случайно сделаете const_cast и словите UB.

Байка от Страуструпа: названия кастов специально сделаны слишком большими, чтобы их меньше хотелось писать.

Ещё есть dynamic_cast, но мы пока про него не говорим.

Стадии сборки

  1. Препроцессинг

  2. Компиляция

  3. Ассемблирование

  4. Линковка

Препроцессор обрабатывает команды препроцессора по типу #include, #define, .... Это не компиляция, а просто обработка текста.

Например, #include "file" будет искать файл, file в директории файла и в специально указанных путях. Потом он просто заменит эту строчку содержимым файла. #include <header> говорит, что header нужно искать в системе.

Упражнение. Понять, где у вас лежит iostream.

Далее компилятор переделывает код(уже без директив с решеткой) в ассемблерный код(.s). Далее с помощью ассемблера он уже преобразуется в объектный файл(уже с машинными инструкциями, .o). Далее линкер преобразует его в исполняемый файл.

В чём разница между 1.cpp -> 1.o и a.out. Линковщик говорит, где нужно искать функции(символы)

3 Basics of OOP

3.1 Classes and structures, encapsulation

Типы – классы, данные – поля классов, операции – методы классов, объекты – экземпляры классов.

Класс можно объявить с помощью ключевого слова class, структуру с помощью ключевого слова struct.

Пока мы будем использовать ключевое слово struct.

struct S {
    int x; // поле структуры
};

int main() {
    S s;
    s.x; // обращение к полю x структуры s
    std::cout << s.x << std::endl; // UB, так как не инициализировано
}
struct S {
    int x = 1;
    double d = 3.14;
};

int main() {
    S s;
    std::cout << s.x << std::endl; // ok, x = 1
}

Размер структуры равен сумме полей с точностью до выравнивания. Например sizeof(S) == 16, несмотря на то, что sizeof(int) + sizeof(double) = 12. Её байты заполнены так: IIII....DDDDDDDD, это сделано в силу того, что восьмибайтные переменные кладутся по адресам кратным 8. Сам объект S тоже хочется положить по адресу кратному 8, чтобы их можно было класть подряд. Если бы S выглядела так: IIIIDDDDDDDD, то две такие нельзя было бы поставить подряд.

Например, можно сделать reinterpret_cast<int&>(s), тогда мы прочитаем int с первых четырех байт S.

Структуры можно инициализировать агрегатно, S s{2, 2.718}.

Внутри структур нельзя писать выражения и объявлять пространства имен. Но можно создавать методы и использовать using.

struct S {
    ...

    void f(int y) {
        std::cout << x + y << std::endl;
    }
}

int main() {
    S s;
    s.f(228);
}

Внутри структур можно использовать методы до того, как они объявлены в коде.

Можно объявлять методы вне структуры

void S::f(int y) {
    std::cout << x + y;
    ff();
}

Ключевое слово this – возвращает указатель на объект, в котором мы сейчас находимся.

struct S {
    int x;
    double d;

    void ff(int x) {
        // x and this->x are not the same
    }
}

Можно объявлять структуры внутри структур(inner class).

Можно анонимно объявлять структуры

struct {
    char c;
} a;

Можно объявить структуру прямо внутри функции(local class).

3.2 Access modifiers

Одним из основных отличий классаот структуры является возможность объявить приватное поле/метод. Все поля в структурах по умолчанию публичные, а в классах наоборот приватные, к ним нельзя обратиться извне.

Ясно, что ошибки доступа проверяются на этапе компиляции. Модификаторы доступа можно поменять в классе ключевыми словами public, private

class C {
public:
    int x;
private:
    int y;
public:
    int z;
private:
    int t;
}