# 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ходятся реализацции на класс который не хочешь чтобы в будущем размножился.