# 10 лекция - C++
(30.04.2022)
## Методы
Методы - функции класса или структуры
```c++
struct A
{
int x;
int f() {return x;}
};
A a;
sizeof(a); //4, размеру int, тк функция не храниться в классе, она хранится в секции кода, имеет специльное скрытое имя, для связывания с классом A
sizeof(A); //A, тоже самое
```
## Классы
### Инкапсуляция
Если вы хотите ограничить видимость некоторых методов и данных от пользователя библиотеки (Тк методы и поля могут поменяться в дальшейшем, что не должно ломать код, который раньше использовал вашу библиотеку)
#### Модификаторы доступа
1) `private`
2) `public`
3) `protected` (про наследование)
```c++
struct A
{
private: //(всё до конца закрыто снаружи или другого модификатора)
int x;
public: //(всё до конца доступно снаружи или другого модификатора)
int f() {return x;}
};
A a;
```
> У `struct` по умолчанию модификатор доступа `public`
> У `class` по умолчанию модификатор доступа `private`
> Больше различий НЕТ!
Как обратиться к `private` полям и методам если очень хочется
```c++
#define class struct
#define private public
```
(Можно сделать через ключи компиляции)
> Дефайны через ключи компиляции задают `define` на все файлы, если есть такой-же `define` в коде он переопредилит значние на новое (два одинаковых `define` не ошибка, варнинг, новый его переопределит)
### Наследование
Когда у нас есть два класа, которые делают похожие вещи, но немного поразному. Надо выделить общее - родительский класс, и наследовать от него разные реализации
`protected` поля не видны для окружающего мира, но видны наследникам
```c++
class A
{
private:
int x;
public:
int f() {return x;}
protected:
float x;
};
class B : public A //('public' - способ наследования)
{
private:
int z;
public:
int g() {return f() + z;}
int f() {return A::f() + z;} //можно перекрыть исходную (A::f() - вызов f из A)
protected:
};
B b;
b.f(); //будет вызов из B
```
> способ наследования понижает видимость до той, что мы указали
1) `public` ничего не меняет
2) `protected` сделает `public` поля класса `A` полями типа `protected`
3) `private` сделает `public` и `protected` поля класса `A` полями типа `private` (к ним будет доступ из `B`, естественно)
```c++
class A
{
private:
int y;
public:
int f() {return x;}
protected:
float x;
friend int h(A &a); //можно обьявить функцию или класс другом, она получает доступ ко всем private полям, Дружественность не наследуется и работает в одну сторону!
//Не метод класса
/*static int h() //Как статический метод, только без friend извращений
{
return y + 1;
} //*/
};
int h(A &a)
{
return a.y + 1;
}
```
> `friend` можно обьявлять в любом месте класса, и не может быть использован для доступа к `private` полям родителя.
#### Конструкторы/деструкторы при наследовании
Любят писать, что конструкторы и деструкторы не наследуются, но не все понимают, что это значит
```c++
class A
{
private:
int y;
public:
int f() {return x;}
A(int x = 0, int t = 2){}
};
class B : public A
{
private:
int z;
public:
int f() {return A::f() + z;}
protected:
};
A a(1, 4);
//B b(1, 4); //Так нельзя
B b; //здесь будет вызван конструктор A
```
> Конструкторы наследуются, но не переходят к наследнику
```c++
class A
{
private:
int y;
public:
int f() {return x;}
A(int x, int t = 2){}
};
class B : public A
{
private:
int z;
public:
int f() {return A::f() + z;}
B() : A(3) {} //так можно иниц родителей
protected:
};
A a(1, 4);
//B b(1, 4); //Так нельзя
B b; //здесь будет вызван конструктор A
```
> Сначала вызовется деструктор `B`, затем он вызывает деструктор `A`.
#### Указатели на классы
```c++
class A
{
private:
int y;
public:
int f() {return x;}
A(int x, int t = 2){}
};
class B : public A
{
private:
int z;
public:
int f() {return A::f() + z;}
B() : A(3) {} //так можно иниц родителей
protected:
};
A *a = new A;
B *b = new B;
a->f();
b->f();
A *p;
p = b; //указатель на A внутри B;
p->f(); //из A
```
```c++
class A
{
private:
int y;
public:
virtual int f() {return x;} //вызывается в соотствие с типом обьекта
//, а не типом указателя
A(int x, int t = 2){}
};
class B : public A
{
private:
int z;
public:
int f() {return A::f() + z;}
B() : A(3) {} //так можно иниц родителей
protected:
};
A *a = new A;
B *b = new B;
a->f();
b->f();
A *p;
p = a;
p->f(); //из A
p = b; //указатель на A внутри B;
p->f(); //из B
delete p; //освободит память B, но вызовет только деструктор A
```
> Конструкторы не бывают виртуальным (очевидно)
> Деструкторы бывают виртуальными (очевидно)
> Таблица виртуальных функций лежит в `A` и это дополнительный вызов в память
> виртуальные функции будут виртуальными во всех наследниках по умолчанию
Можно написать `virtual f() = 0;`, означает что здесь нет реализации `f`, этот класс будет абстрактным. Мы не сможем создать копию абстрактного класса, но можем создать на него указатель. Абстрактный класс не жизнеспособен самостоятельно, но жизнеспособен (и его копия есть) в составе наследника, который реализовал все абстрактные функции.
(можно написать только для `virtual` и `0`)
#### Множественное наследование
(есть только в C++ и Python)
```c++
class B: public A, public C
{
//...
}
```
Представим ситуацию
```
A -> B -> D
-> C
```
`B` и `C` наследуются от `A`, `D` наследуется от `B` и `C`.
> В `D` будет две разные реализации A!
Можно обьединить, надо поставить модификатор `virtual` в классах `B` и `C` при наследование от `A` - `class B : virtual public A`. (не бесплатно, тоже таблица виртуальности)
> `virtual` ставишь там где раcходятся реализацции на класс который не хочешь чтобы в будущем размножился.