## 3.7 Pointers to members ```C++ struct S { int x; double y; void f(int z) { std::cout << x + z << std::endl; } }; int main() { int S::* p = &S::x; S s; s.*p; // returns s.x; S* ps = &s; ps->*p; // returns s.x; void (S::* pf)(int) = &S::f; // pointer to method (s.*pf)(3); (ps->*pf)(5) } ``` Интуитивно указатель на поле хранит "сдвиг относительно начала структуры". ## 3.8 Enums and enum classes Дословно перечислимый тип ```C++ enum E { White, Gray, Black }; int main() { int e = White; std::cout << e << std::endl; // 0 } ``` Можно использовать как отдельный тип `E`, но он хранится в памяти как `int` и нумеруется начиная с нуля. В C++-11 появились `enum class`. Она не вносит интовые константы и запрещает неявные конверсии. С помощью двоеточия можно описать каким типом он должен представляться(обязательно целочисленным) ```C++ enum class E : int8_t {} ``` # IV. Inheritance ## 4.1 Public, private and protected inheritance ```C++ class Base { protected: int x; public: void f() {} }; class Derived : Base { int y; void g() { std::cout << x << std::endl; // x is inherited from Base, and protected } }; int main() { Derived d; // std::cout << d.x << std::endl; CE, x is protected } ``` Ключевое слово `protected` означает то, что в отличие от `private` данный член класса будет доступен еще и его наследникам. Наследование также бывает публичным, приватным, и защищенным. По умолчанию у структур оно публичное, у классов приватное. Публичное наследование значит, что "все знают" о том, что `Derived` является наследником `Base`, а приватное значит, что "никто не знает". Например, если мы сделаем `Derived : private Base`, то из `main` мы не сможем вызвать `d.f()`, так как хоть `f` и публично в `Base`, мы унаследовали его приватно. Если мы сделаем `Derived : public Base`, то мы все равно не сможем обратится к `d.x`, так как `d.x` приватно в `Base`. Защищенное наследование значит, что только лишь друзья, наследники, и сам `Derived` имеет доступ к родительским полям. По сути права доступа к полю "перемножаются" на тип наследования, нужно "пройти" оба модификатора. ```C++ struct Granny { int x; void f() {} }; struct Mom : protected Granny { int y; void g() { std::cout << x << std::endl; } }; struct Son : Mom { int z; void h() { std::cout << x << std::endl // OK, can pass protected modifier } }; int main() { Son s; // s.x; cannot pass protected modifier from Mom to Granny s.y; // OK, default inheritance for struct is public } ``` Как работает дружба при наследовании? Допустим в `struct Granny` мы объявили `main` своим другом. Тогда теперь мы всё ещё не сможем обратиться к `s.x`. И это логично, ведь `friend` снимает все ограничения, которые ты наложил, но не те, которые наложил кто-то другой, например, твой наследник. > Строгая мама запрещает общаться с доброй бабушкой ## 4.2 Visibility Что происходит если есть конфликт имён? ```C++ struct Base { int x; void f() { std::cout << 1 << std::endl; } }; struct Derived : Base { int y; void f() { std::cout << 2 << std::endl; } }; int main() { Derived d; d.f() // OK, 2 } ``` Главный принцип: _частное главнее общего_. А что если ```C++ struct Base { int x; void f(int) { std::cout << 1 << std::endl; } }; struct Derived : Base { int y; void f(double) { std::cout << 2 << std::endl; } }; int main() { Derived d; d.f(0) // ??? } ``` Программа выведет 2. Более того, если бы `f` не принимала аргументов, то была бы ошибка компиляции. Полезно думать об этом так, `Derived::f` затмевает `Base::f` как будто бы это более локальная область видимости. Если мы хотим явно вызваться от родителя, то можно написать `d.Base::f(0);`. Приватность и публичность также не влияет на то, какой метод мы будем вызывать, свой или родительский. Чтобы научиться выбирать, нужно написать `using Base::f` внутри `Derived`. Более того, `using Base::f` игнорирует родительский модификатор доступа, что логично. Сначала создается область видимости, потом проверяются права доступа. Сам `using` можно сделать приватным ```C++ private: using Base::x; ``` Самый кринж ```C++ struct Granny { int x; void f() {} }; struct Mom: private Granny { friend int main(); int x; }; struct Son : Mom { int x; void f(Granny& g) { std::cout << g.x << std::endl; } }; ``` Оно не скомпилируется, так как `Granny` из области видимости сына приватное и он не имеет к нему доступа. Поэтому нужно писать `::Granny &g`, дабы подчеркнуть, что имя берется из глобальной области. ## 4.3 Memory layout, constructors and destructors in case of inheritance. ```C++ struct Base { int x; }; struct Derived : Base { double y; }; int main() { sizeof(Derived); // OK, 16. First x, then y, according to padding it's 2 * 8 = 16 } ``` А что если `Base` вообще не содержит полей, только, возможно, методы. `sizeof(Base) > 0`, так как структура должна иметь хоть какой-то размер, чтобы разные размеры имели разные адреса. Но тем не менее, `sizeof(Derived) == 8`. Данный феномен именуется EBO (Empty Base Optimization). Пустому `Base` разрешается ничего не занимать в памяти. При конструкции всегда должен **сначала** инициализироваться родитель. То есть либо у `Base` есть дефолтный конструктор, либо нужно писать что-то типа ```C++ struct A { A(int) { std::cout << "A " << x << std::endl; } }; struct Base { A x; Base(int x): x(x) { std::cout << "Base" << std::endl; } }; struct Derived : Base { A y; Derived(double y): Base(0), y(y) { std::cout << "Derived" << std::endl; } } int main() { Derived d = 1; } ``` Программа выведет ``` A 0 Base A 1 Derived ``` Если написать деструкторы, то так как сначала выполняется тело деструктора, а потом уничтожаются поля, причём в обратном порядке, то выведется ``` ~Derived ~A 1 ~Base ~A 0 ``` ## 4.4 Casts in case of inheritance ```C++ struct Base { int x = 1; }; struct Derived : Base { int y = 2; }; void f(Base& b) { std::cout << b.x << std::endl; } int main() { Derived d; f(d); // OK, can cast Derived to Base } ``` Суть в том, что наследника можно кастовать к родителям, а вот обратное, ясное дело нельзя, ведь наследник может больше, чем родитель.