static_cast
– ключевое слово языка C++
;
T xstatic_cast<U>(x);
С помощью него неявные преобразования можно сделать явно через
static_cast
, такие как
double -> int, A* -> void*
.
reinterpret_cast
– говорит компилятору трактовать
переменную x
как переменную типа U
побитово.
То есть интерпретировать память как будто бы там лежит другой тип.
reinterpret_cast
нужно делать от ссылки, например,
reinterpret_cast<double&>(y)
.long long y;
double &d = reinterpret_cast<double&>(y); // UB
= 3.14;
d std::cout << y << std::endl; // какая-то дичь
По умолчанию это делать нельзя, поэтому в примере выше это UB. Это можно делать, например, если есть две структуры в которых типы расположены в одном и том же порядке.
reinterpret_cast
также можно применять к указателям,
но только к совместимым
reinterpret_cast
не позволяет обходить
const
.
Для обхода есть const_cast
. Он позволяет снять
константность.
const int c = 5;
int &cc = const_cast<int&>(c); // также нужно давать ссылку
= 7; // UB
cc // и также const_cast от ссылки и указателя это две разные сущности
std::cout << c << ' ' << cc << std::endl;
Попытка изменить переменную, которая изначально была константой это UB.
C-style cast. Всегда бан в программах на C++. По сути он
последовательно перебирает все возможные касты, пока он не подойдет.
Например, вы можете не заметить, как случайно сделаете
const_cast
и словите UB.
Байка от Страуструпа: названия кастов специально сделаны слишком большими, чтобы их меньше хотелось писать.
Ещё есть dynamic_cast
, но мы пока про него не
говорим.
Стадии сборки
Препроцессинг
Компиляция
Ассемблирование
Линковка
Препроцессор обрабатывает команды препроцессора по типу
#include, #define, ...
. Это не компиляция, а просто
обработка текста.
Например, #include "file"
будет искать файл,
file
в директории файла и в специально указанных путях.
Потом он просто заменит эту строчку содержимым файла.
#include <header>
говорит, что header
нужно искать в системе.
Упражнение. Понять, где у вас лежит
iostream
.
Далее компилятор переделывает код(уже без директив с решеткой) в
ассемблерный код(.s
). Далее с помощью ассемблера он уже
преобразуется в объектный файл(уже с машинными инструкциями,
.o
). Далее линкер преобразует его в исполняемый файл.
В чём разница между 1.cpp -> 1.o
и
a.out
. Линковщик говорит, где нужно искать
функции(символы)
Типы – классы, данные – поля классов, операции – методы классов, объекты – экземпляры классов.
Класс можно объявить с помощью ключевого слова class
,
структуру с помощью ключевого слова struct
.
Пока мы будем использовать ключевое слово struct
.
struct S {
int x; // поле структуры
};
int main() {
;
S s.x; // обращение к полю x структуры s
sstd::cout << s.x << std::endl; // UB, так как не инициализировано
}
struct S {
int x = 1;
double d = 3.14;
};
int main() {
;
S sstd::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.f(228);
s}
Внутри структур можно использовать методы до того, как они объявлены в коде.
Можно объявлять методы вне структуры
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).
Одним из основных отличий классаот структуры является возможность объявить приватное поле/метод. Все поля в структурах по умолчанию публичные, а в классах наоборот приватные, к ним нельзя обратиться извне.
Ясно, что ошибки доступа проверяются на этапе компиляции.
Модификаторы доступа можно поменять в классе ключевыми словами
public, private
class C {
public:
int x;
private:
int y;
public:
int z;
private:
int t;
}