# Наследяване ## Пример за наследяване При създаване на нов клас, който има общи компоненти и поведение с вече дефиниран клас, вместо да дефинира повторно тези компоненти и поведение, можем да го обявим за наследник на вече дефинирания. ```c++ class A { . . . public: void f() { . . . } . . . }; class B : public A { . . . }; int main() { B obj; obj.f(); // OK! } ``` При наследяването: - Наследяват се член-данните и методите на основните класове - Получава се достъп до **някои** от наследените компоненти на основните класове В нашия пример класът **В е наследник на класа А.** Обекти от класовете изглеждат така: ![enter image description here](https://i.ibb.co/Fshm2LJ/Untitled-Diagram.png) След като В е наследник на класа А, то при създаване на обект от тип В се създава обект от тип А, **който е част от обекта** от тип В. ## Видове наследяване Някои типове наследяване в c++: - private (по подразбиране) - protected - public Примери: ```c++ class A { public: int x; protected: int y; private: int z; }; class B : public A { // x is public // y is protected // z is not accessible from B }; class C : protected A { // x is protected // y is protected // z is not accessible from C }; class D : private A // 'private' is default for classes { // x is private // y is private // z is not accessible from D }; ``` ## Подаване като параметри на функции. Класове-наследници могат да бъдат подавани като параметри на функции, които приемат обекти от базовия клас. Може да се използва функционалността само от базовия клас. ```c++ class A { public: int a; }; class B : public A { public: int b; }; void f(A& obj) { obj.a++; } void g(A* ptr) { (*ptr).a++; } void t(B& obj) { obj.b++; } int main() { A obj1; B obj2; f(obj1); //OK! f(obj2); //OK! g(&obj1); //OK! g(&obj2); //OK! t(obj2); //OK! //t(obj1)// not ok! } ``` ## Конструктори и деструктори при наследяване. Във всеки конструктор на класа-наследник трябва да се оказва кой конструктор да се извика за базовия клас. Ако не е оказано, ще се извика конструкторът по подразбиране. ```c++ class B : public A { . . . public: B(...) : A(...) //some constructor of A. { . . . } }; ``` Деструкторът на наследения клас извиква деструктора на базовия клас. ## Копиране при наследяване При разписване на конструктора за копиране и оператора за присовяване на клас-наследник, трябва експлицитно да извикваме копиране и за базовия клас. ```c++ B::B(const B& other) : A(other) // (copy constructor of A (base class) { copyFrom(other); } B& B::operator=(const B& other) { if (this != &other) { A::operator=(other); //operator= of A (base class) free(); copyFrom(other); } return *this; } ``` Разписваме копирането и триенето само ако наследникът ползва необработена динамична памет! Ако няма, то генерираните от компилатора (по горната схема) работят коректно! **Задача 1:** Реализирайте класов бар, в който се продават нормални напитки и алкохолни напитки. Трябва да имате функции за зареждане на напитки от бара и взимане на напитка от бара. При зареждането се посочва и броят на артикулите, които се зареждат. Ако не се каже брой - зарежда се 1 артикул. При взимането на напитка от бара: Избира се тип (нормална или алкохолна) и се връща най-отдавна заредената в бара. Във всеки момент трябва да могат да се взимат статистика за бара - брой продадени артикула oт всеки тип и сумарно продадени мл. **Напитката** има име (низ с произволна дължина, само от латински символи, в който първият символ е главен), количество (в мл [200, 1000]), а **алкохолната напитка** има *име (низ с произволна дължина, само от латински символи, в който първият символ е главен), количество (в мл [200, 1000]) и процент алкохол [5, 98]*. Примерен интерфейс: ```c++ int main() { Bar b; b.addDrink("Coke", 330, 2); //2x Coca cola 330 ml b.addDrink("Fanta", 500); //1x Fanta 500 ml b.addAlocoholDrink("Zagorka", 500, 5); //1x Zagorka, 5% alc, 500 ml b.getAcloholDrink(); // Zagorka b.getDrink() // Coke b.getDrink() // Coke b.getDrink() // Fanta b.getAlocholDrinksSold(); // 1 b.getDrinksMlSold(); // 830 } ```