Бублик Володимир Васильович Об'єктно- орієнтоване програмування Частина 2. Ієрархічне програмування. Лекція 11. Поліморфізм Лекції для студентів 2 курсу München, Franz-Josef Strauß Airport
Предмет вивчення Поліморфізм це центральна тема об'єктно- орієнтованого програмування, але стосується вона лише такого відкритого успадкування (public inheritance), при якому об'єкт похідного класу виявляється різновидом базового Це значить, що до похідного класу можна звернутися за виконанням будь-якого методу, декларованого, але не обов'язково реалізованого, в базовому класі З точки зору поліморфізму успадкування реалізації, яким служать закрите (private inheritance) і захищене успадкування (private inheritance) взагалі не розглядається і до успадкування не належить © Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 2
3 Що таке поліморфізм? Merriam-Webster's Online Dictionary polymorphism (1839) the quality or state of existing in or assuming different forms Wikipedia, the Free Encyclopedia polymorphism is a programming language feature that allows values of different data types to be handled using a uniform interface
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 4 Статичний поліморфізм (повторення) Одна операція додавання (виводу) для різних типів даних const Complex i = {0, 1}; Complex z = {1, 1}; double x = 2, y=3; cout << (x +5) + (z+i) + y<<endl; Complex operator+ (const Complex &, const Complex &); Complex operator+ (double, const Complex &); Complex operator+ (const Complex &, double); ostream& operator<<(ostream &os, const Complex &);
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 5 Функціональна полісемія if ( (*pf)( left)*(*pf)(0.5*( left + right) <0) right = 0.5*( left + right); const NonConstAction menuNonConst[] = { &Screen::home, &Screen::move, &Screen::back, &Screen::clear }; doActionNonConst(v, menuNonConst[k], n);
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 6 Все ще статичний поліморфізм class European{ public: string myNation() { return European; } }; void talk(European & person) { cout<<I am<< person.myNation()<<endl; } //European
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 7 Все ще статичний поліморфізм class Ukrainian: public European { public: string myNation() { return Ukrainian; } }; Ukrainian me; talk (me);//Ukrainian European & someone (me); talk (someone);//European
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 8 Поліморфізм стає динамічним class European{ public: virtual string myNation() { return European; } }; Ukrainian me; talk (me);//Ukrainian European & someone (me); talk (someone);//Ukrainian
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 9 Проблема Як зробити так, щоб базовий інтерфейс реагував на конкретний тип похідного об'єкту? Рішення Через механізми функціональної полісемії (непрямий виклик) Команда компілятору Бери функцію talk() у тому об'єкті, який зараз фактично маєш
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 10 Характеризація успадкувань Братські класи: кожен похідний клас належить одному базовому типу: Гострокутний, прямокутний і тупокутний трикутники дають вичерпну класифікацію трикутників
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 11 Кратне успадкування В одному похідному класі об'єднуються декілька базових класів «В одну телегу впрячь не можно Коня и трепетную лань.» О.С. Пушкін ( ) «Полтава» (1829)
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 12 Перший базовий клас //Клас хижаків class Predator { private: string _favoritePrey; string _name; public: Predator(const string& prey, const string& name); ~Predator(); void setFavotitePray (const string&); const string& getFavotitePray() const; void setName (const string&); const string& getName() const; };
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 13 //Клас кімнатних тварин class Pet { private: string _favoriteToy; string _name; public: Pet (const string& prey, const string& name); ~ Pet (); void setFavotiteToy (const string&); const string& getFavotiteToy () const; void setName (const string&); const string& getName() const; }; Другий базовий клас
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 14 Гібридний похідний клас //Клас кішок: //хижаки і кімнатні тварини одночасно class Cat : public Predator, public Pet { public: Cat(const string& prey, const string& toy, const string& name ); ~Cat(); }; Мурка чи Багіра? Cat myCat(мишка,клубок,Мурка); myCat.Predator::setName(Багіра);
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 15 Діаграма класів
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 16 Спільний базовий клас
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 17 Базовий клас //Клас паралелограмів class Paralellogram { protected: length_t _height; length_t _width; angle_t _angle; public: Paralellogram(length_t h, length_t w, length_t a): _height (h), _width (w), _angle (a){}; };
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 18 Перший рівень спадкувань //Клас прямокутників class Rectangle : public Paralellogram { public: Rectangle(length_t h, length_t w): Paralellogram (h, w, pi/2){}; }; //Клас ромбів class Rhombus : public Paralellogram { public: Rhombus(length_t side, angle_t a): Paralellogram (side, side, a){}; };
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 19 Другий рівень спадкувань //Квадрат походить одночасно від //прямокутника і ромба class Square : public Rectangle, public Rhombus { public: Square (length_t side ): Rectangle( side, side ), Rhombus (side, 90) {}; };
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 20 Діаграма класів Хотіли: Одержали:
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 21 Віртуальне успадкування //Клас прямокутників class Rectangle : virtual public Paralellogram { public: Rectangle(length_t h, length_t w): Paralellogram (h, w, pi/2){}; }; //Клас ромбів class Rhombus : virtual public Paralellogram { public: Rhombus(length_t side, angle_t a): Paralellogram (side, side, a){}; };
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 22 Віртуальна ініціалізація //Квадрат походить одночасно від //прямокутника і ромба class Square : public Rectangle, public Rhombus { public: Square (length_t side ): Rectangle( side, side ), Rhombus (side, 90), Paralellogram (side, side, pi/2) {}; };
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 23 Діаграма віртуального успадкування
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 24 Як уникати проблем (кратного) успадкування? Звідки брати реалізацію: з базового класу чи похідного? Виявляється, що коректніше використовувати абстрактні базові класи, вільні від реалізації Реалізація може заважати: бути суперечливою, виключати можливі альтернативи До того ж вона не всім потрібна
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 25 Проста ієрархія //Клас фігур невідомого типу class Shape { public: void whatAmI(); { cout << "I don't know what I am!\n"; } };
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 26 Проста ієрархія //Клас прямокутників class Rectangle : public Shape { public: void whatAmI() { cout << "I'm a rectangle!\n"; } };
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 27 Проста ієрархія //Клас трикутників class Triangle : public Shape { public: void whatAmI() { cout << "I'm a triangle!\n"; } };
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 28 Проста ієрархія Shape *s1, *s2, *s3; s1 = new Rectangle; s2 = new Triangle; s3 = new Shape; s1->whatAmI(); s2->whatAmI(); s3->whatAmI();
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 29 Віртуальні функції //Клас фігур невідомого типу class Shape { public: virtual void whatAmI(); { cout << "I don't know what I am!\n"; } };
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 30 Пізнє зв'язування Shape *s = new Shape;
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 31 Пізнє зв'язування Shape *s = new Shape; Shape *s = new Triangle;
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 32 Для чого потрібне пізнє зв'язування? В першу чергу для уможливлення нових застосувань клієнтських кодів Якщо клієнтський код не залежить жорстко від реалізації використаних в ньому засобів, то він може функціонувати на різних реалізаціях за умови використання спільного інтерфейсу
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 33 Reuse //Обробка стеку не залежить від його реалізації int process (Stack & stackToProcess) { int sum = 0; while (!stackToProcess.empty()) { sum += stackToProcess.top(); stackToProcess.pop(); } return sum; }
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 34 Спільний інтерфейс Як зробити функцію template void process(Stack & stackToProcess); придатною для обробки як одного типу стеку StackDerivedFromArray stack; process(stack); так і іншого? StackContainingArray stack; process(stack);
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 35 Абстрактний стек //Інтерфейс стеків template class Stack { public: virtual ~Stack(){}; virtual bool empty() const =0; virtual const T& top() const =0; virtual void pop() =0; virtual void push(const T& value) =0; };
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 36 Чисто віртуальні (абстрактні) методи Пізнє зв'язування через таблицю (указників) віртуальних функцій Реалізацію візьмемо у похідному класі тоді, коли це стане можливим Stack & stackToProcess:
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 37 Реалізація абстрактних методів StackContainingArray stachar(100); process(stachar); Stack & stackToProcess:
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 38 Деструктори //Базовий клас class Person { char * _name; public: Person (const char* name):_name (new char [strlen(name)+1]) { strcpy (_name, name); } virtual ~Person() { delete [] _name; } };
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 39 Деструктори //Похідний клас class Student: public Person { char * _inmatriculation; public: Student (const char* name, const char* m): Person(name), _inmatriculation (new char[strlen(m)+1]) { strcpy (_inmatriculation, m); } virtual ~Student() { delete [] _inmatriculation; } };
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 40 Деструктори Person * p = new Student("Qwerty"," "); delete p; Якби деструктор не був би віртуальним спрацював би лише деструктор вжитого інтерфейсу: ~Person() Висновок: деструктор має бути віртуальним (або невіртуальним захищеним: чому?)
© Бублик В.В. ООП-2. Ієрархічне програмування. Лекція 11. Поліморфізм 41 Порада Уникайте заплутаних ієрархій Добра ієрархія легка для коректного вжитку, але складна і непристосована для некоректного використання Moritz Escher