7.6 KiB
2.7 Type conversions
static_cast
-- ключевое слово языка C++
T x;
static_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
d = 3.14;
std::cout << y << std::endl; // какая-то дичь
-
По умолчанию это делать нельзя, поэтому в примере выше это UB. Это можно делать, например, если есть две структуры в которых типы расположены в одном и том же порядке.
-
reinterpret_cast
также можно применять к указателям, но только к совместимым -
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
, но мы пока про него не говорим.
Стадии сборки
-
Препроцессинг
-
Компиляция
-
Ассемблирование
-
Линковка
Препроцессор обрабатывает команды препроцессора по типу #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;
}