Неявный каст из int в bool разрешен vector<bool> позволяет хранить булы в 1 бит

floating point types хранятся в формате 1 бит на знак, мантисса и экспонента экспонента это степень двойки на которую нужно умножить мантиссу, чтобы получить нужное число мантисса - биты, кодирующие значащие цифры, которые нужно умножить на 2 в положительной или отрицательной степени

чем больше число по модулю, тем ниже его точность (от 0f до 1f много различных значений, а от 1’000’000f до 1’000’001f их меньше)

floatdouble
знаковый бит11
экспонента811
мантисса2352
это типы с плавающей точкой, поэтому у 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