Неявный каст из int в bool разрешен vector<bool> позволяет хранить булы в 1 бит
floating point types хранятся в формате 1 бит на знак, мантисса и экспонента экспонента это степень двойки на которую нужно умножить мантиссу, чтобы получить нужное число мантисса - биты, кодирующие значащие цифры, которые нужно умножить на 2 в положительной или отрицательной степени
чем больше число по модулю, тем ниже его точность (от 0f до 1f много различных значений, а от 1’000’000f до 1’000’001f их меньше)
| float | double | |
|---|---|---|
| знаковый бит | 1 | 1 |
| экспонента | 8 | 11 |
| мантисса | 23 | 52 |
| это типы с плавающей точкой, поэтому у float есть ~6 значащих разрядов, у double ~15. и где-то между этими разрядами можно поставить точку |
float/0 или double/0 будет inf float/nan = nan
size_t обычно равен unsigned long long, это такой беззнаковый целочисленный тип, который достаточен для того, чтобы индексировать любую ячейку в массиве
std::string
в std::string s = “abc” обращение по индексу 3 это не ошибка, там будет лежать символ с кодом 0
- .at(5) = ‘x’ - семантика точно такая же, как у [], но гарантирует, что вылетит outofrange если обратимся за пределы допустимого
- конкатенация (с char и string)
- .push_back(char) действие аналогично +=, но памяти релоцируется в 2 раза больше, а не +1, как у +=
- .pop_back() - минус 1 char справа
- s.back() = ‘y’;
на самом деле string - это using basic_string<char, char_traits<char>, allocator<char>> в неймспейсе std
стандартная строка имеет литеральный суффикс s (C++11):
"abcdef"s; - тип std::string
в полях std::basic_string хранится анонимный юнион. это значит, что там 2 поля класса шарят 1 адрес - Small Strings Optimization (SSO) std::string не хранит на куче строку, если она слишком короткая (занимает меньше места, чем pointer) этот юнион хранит либо массив чаров (local_buf, 15 байт), либо size_t (capacity) строка до 16 байт хранится на стеке, а не на куче
итак, std::string состоит из полей: pointer (либо на local_buf, либо на кучу), длина (число) и либо capacity, либо local_buf. (capacity нам не нужно хранить, если у нас строка хранится в буфере на 15 байт)
если строка короткая, то в std::string есть поле, являющееся указателем на другое поле (поэтому нельзя копировать строки через memcpy, эти указатели инвалидируются)
вектора можно сравнивать .push_back() в векторе работает за амортизированную единицу, т.е. когда выделенная память закончилась и вызывается еще push_back(), то релоцируется памяти в 2 раза больше, исходные элементы копируются на новое место, старый участок памяти удаляется .shrink_to_fit() удалить лишнюю память .resize(size_t, char) изменить размер и если размер увеличился, то заполнить значениями .reserve() обеспечивает выделенную память, чтобы хватило под нужное кол-во элементов без релокации. .capasity() под сколько элементов сейчас память зарезервирована
можно определить свой литеральный суффикс перегрузив оператор "" :
Class operator""_class(unsigned long long/const char*/long double) {
return Class();
}
Class class = 1_class;

енамы хранят именованные константы, они доступны как глобальные. объект типа енам хранится в памяти по умолчанию как инт (если через : не написать другой целочисленный тип).
неявная конвертация из енума в инт возможна, а из инта в енум нет.
можно сделать безымянный енум, если нужен набор целых констант
enum {red, black};
с С++11 появились енум классы. в отличии от енума, не вносят константы в глобальную область видимости и запрещает неявные конверсии в обе стороны если у переменной возможно лишь несколько предопределённых значений, то следует писать енум класс после енума можно поставить двоеточие и написать в каком типе его хранить (целочисленный) если в каком-то неймспейсе лень писать везде EnumName::, можно написать using enum EnumName;
значения енума можно инициализировать только constexpr и integral
все типы переменных известны на этапе компиляции
типы std::istream и std::ostream cin и cout - глобальные переменные таких типов
std::endl - особый объект, вывод которого в cout приводит к тому, что очищается буфер вывода. буфер вывода - когда чето пихаем в cout, оно не сразу в консоль выводится, оно выводится пачками, когда делается flush буфера. если происходит std::terminate, то std::cout может не успеть все вывести на консоль, до того, как убъётся программа
консольные приколы:
a.out./ 1>output.txt - перенаправить поток std::cout в консоль
0 - std::cin
1 - std::cout
2 - std::cerr
cout - объект, в себе хранит некоторый буффер, массив символов, который он собирается вывести. выводит на консоль тогда, когда буффер заполнится, тк выводить на консоль - долгая операция. при падении программы буфер может не успеть очиститься и вывод не будет выведен. чтобы очистить, можно написать
std::cout.flush(); - явная очистка буфера
std::cout << std::endl; - неявная очистка буфера, делает << ‘\n’ и вызывает flush
по умолчанию std::cout и std::cin связаны (в каждом есть указатель на друг друга) это сделано для того, чтобы между std::cout и std::cin делался flush
можно их отвязать друг от друга
std::cin.tie(nullptr)
std::cout.tie(nullptr)
printf и scanf тоже буферизованы, у них свои буфера и по умолчанию они тоже связаны с std::cin и std::cout потоками (чтобы порядок ввода вывода не нарушался)
можно отключить
std::cin.sync_with_stdio(false);
std::cout.sync_with_stdio(false);
или std::ios::sync_with_stdio(0);
sstream std::istringstream - строка, которая ведёт себя как поток. работает быстрее
view
std::string_view - using basic_string_view с параметром чар string_view (С++17) - обертка над строкой, не владеет самой строкой. посути пара указателей. можно создать от сишной строки, пары итераторов.
std::span (C++20) - обертка над диапазоном. позволяет наблюдать за диапазоном какого-то контейнера
range - некий концепт, который удовлетворяет свойствам, что у него его begin и end + они работают за константу и begin гарантирует такой же результат при повторном проходе

view это тоже range, но позволяет смотреть на range c условием
для view’s определен оператор | (pipe), который позволяет выход одного дать на вход другому

с С++20 существует range-based for с инициализацией
union
union хранит n типов на одном адресе, в каждый момент времени хранит только один одномоментно хранит только одно из своих полей, все поля принадлежат одному адресу. размер union - max поле с точностью до выравнивания
если в union’e есть поле с хотя бы одним нетривиальным конструктором, то конструктор по умолчанию не генерирует дефолтный конструктор union’a вызывает дефолтный конструктор первого поля
это Segfault. нельзя
u.s = “abs” - не создание строки на новом месте, а присваивание сишной строки к строке. оператор присваивания удаляет старую строку и записывает новую строку. но у нас там не было строки, были байты, которые кодируют 5, поэтому delete вызывается от памяти, которая не строка
правильный способ положить в union строку, не зная, что там было до этого - через placement new
new &(u.s) std::string("abc");
если в unoin лежал какой-либо нетривиальный объект, который был перезаписан, то его деструктор не вызовется и будет утечка памяти. поэтому нужно вызвать деструктор самостоятельно: u.s.~basic_string<char>();
у union’a не может быть виртуальных функций, родителей/наследников, нестатических ссылок если есть нестатический нетривиальный объект, то дефолтный деструктор = delete по умолчанию все поля публичные
active member of union - в каждое время в рантайме существует понятие активного члена union’a. активный член юниона - это тот единственный член, чтение которого не UB юнион сам по себе не знает, кто сейчас активный тип
объекты кладутся в константное поле через placement new
анонимный юнион - юнион без названия
union {
int a;
const char* s;
}
a = 1;в таком случае поля юниона выбрасываются в внешнюю область видимости это как объявить int a и const char* s, но шарящие общий адрес - будут перезатирать друг друга
юнионы используются в std::string
юнион с переменным количеством типов (variadic union) - это как tuple, только вместо tuple union

почему языки между собой линкуются через C? одна из причин - в си нет манглирования имён типом. если в си что-то называется одним образом, то в бинарнике оно будет называться также из-за этой гарантии имени в си нету перегрузки функций, методов, шаблонов C++ это C без гарантии имён и со всем тем, что стало возможно добавить в C может быть манглирование, но нет манглирования типом а в C++ есть манглирование типом. название одной и той же функции на разных компиляторах может иметь разное имя в ассемблере. то есть если скомпилировать несколько .cpp файлов разными компиляторами они не слинкуются чтоб сделать стабильный сишный интерфейс на плюсах, нужно написать extern “C”
объекты с external linkage имеют language linkage: может быть C++ linkage и C linkage
extern "C" double sqrt(double) - даёт этой функции C-linkage, то есть имя для этой функции не будет манглироваться. можно писать только в глобальном пространстве имён
extern "C" template <typename T> void f(T x); - нельзя, когда будет инстанцироваться функция нужно будет манглирование
struct S { extern "C" void f(); }; - нельзя, чтоб её вызвать мы её манглируем
функция int main() обязательно должна быть в глобальном неймспейсе, потому что она неявно extern “C” - часть C-API, не манглируется, чтоб её могла вызвать ОС
containers type convertions pointers expressions member funcs and modifiers