Added 09.md
This commit is contained in:
		
							
								
								
									
										307
									
								
								pages/mipt_cxx1/09.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										307
									
								
								pages/mipt_cxx1/09.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,307 @@
 | 
			
		||||
## 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
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Суть в том, что наследника можно кастовать к родителям, а вот обратное, ясное дело нельзя, ведь наследник может больше, чем родитель.
 | 
			
		||||
		Reference in New Issue
	
	Block a user