Бублик Володимир Васильович Програмування - 2 Лекція 10. Ієрархічне програмування. Поліморфізм    Лекції для студентів 2 курсу
Challenges  ієрархій Різні реалізації одного й того ж класу ( StackAggregatingArray ,  StackDerivedFromArray ,  StackOnList) Розширення / спеціалізація класів ( DoubleList, CyclicList) Mixin  (підмішування) :  поєднання кількох функціональностей у одному класі ( PeekBack , Iterated)
Спеціалізація class  Parallelogram { protected : double   _ height; double   _ width; double   _ angle; public : Parallelogram ( double  h,  double  w,  double  a); double  area()  const ; double  height()  const ; double  width()  const ; double  angle()  const ; };
Спеціалізація Ромб і прямокутник служать прикладами   (спеціалізаціями )  паралелограма class  Rhombus:  public  Parallelogram { public : Rhombus ( double  h,  double  a) : Parallelogram (h, h, a){ }; };
Спеціалізація class  Rectangle:  public  Parallelogram { public : static const  double pi = 3.1415926535897932; Rectangle ( double  h,  double  w) : Parallelogram (h, w, pi/2){ }; double  area()  double { return  _height *  _ width; } };
Спеціалізація
Спеціалізація Як до прямокутника, так і ромба можна застосувати поведінку паралелограма Rhombus rh(10, pi/4); rh.area(); rh.height(); rh.width(); rh.angle(); Rectangle rec(10,20); // власна поведінка rec.area(); // успадкована поведінка Parallelogram :: rec.area(); rec.height(); rec.width(); rec.angle();
Управління доступом Можна закрити доступ до функції базового класу class  Rectangle:  public  Parallelogram { private : using   Parallelogram ::area(); public : static const  double pi = 3.1415926535897932; Rectangle ( double  h,  double  w) : Parallelogram (h, w, pi/2){ }; double  area()  double ; };
Mixin  (змішування) :  кратне успадкування Хижак class  Predator { private : string _name; string _favoritePray; public :  Predator (string name, pray); };
Mixin:  кратне успадкування Домашній улюбленець class  Pet { private : string _name; string _favoriteToy; public :  Pet (string name, string toy); };
Mixin:  кратне успадкування class  Cat:  public  Predator,  public  Pet { private : static unsigned int   _freeID; unsigned int  _myCatID; public :  Cat (string name, string toy, string prey): _myCatID (_freeID++), Predator (name, prey), Pet (name, toy){ }; };
Mixin:  кратне успадкування class  Cat:  public  Predator, p ublic  Pet
Consistency  (узгодженість) Хто слідкуватиме за узгодженістю імен в базових об'єктах? const string&  Predator :: rename (string newname) { return  name = newname; } void  cat::show()  const { cout<<Predator::name<<“ aka ”<<Pet::name; }
Спеціалізація  +  змішування: спільний базовий клас   Скільки паралелограмів міститься в одному квадраті? class  Square :  public  Rectangle,  public  Rhombus { public : Square ( double  side ) :  Rectangle( side, side ), Rhombus (side, pi/2) {}; };
Спільний базовий клас :  помилка
Неоднозначність   і неузгодженість Square sq(10); // Помилка: компілятор не знає, //  з якого паралелограма брати дані sq.width(); // Помилка: заміна атрибуту в одному з базових об'єктів // з наступним використанням іншого базового об'єкту sq.Rectangle::width() = 20; sq.Rectangle::height() = 20; cout<<sq.Rhombus::area();
Спільний базовий клас: запит
Віртуальне успадкування Відмінимо виклик конструктора в базових класах змішування Конструктор прямокутника (ромба) викличе конструктор паралелограма лише за умови, що його раніше не викликав конструктор квадрату class  Rectangle :  virtual   public  Parallelogram; class  Rhombus :  virtual   public  Parallelogram; class  Square :  public  Rectangle,  public  Rhombus;
Ініціалізація Parallelogram::Parallelogram( double  h,  double  w,  double  a): _height (h), _width (w), _angle (a) {}; Rectangle::Rectangle ( double  h,  double  w):  Parallelogram (h, w, pi/2) {}; Rhombus::Rhombus( double  side,  double  a ): Parallelogram (side, side, a) {}; Square::Square (  double  side ) :  Rectangle( side, side ), Rhombus (side,  pi/2 ) , Parallelogram( side, side, pi/2)   {};
Приклад // Спочатку паралелограм, потім прямокутник Rectangle rec(10, 20); // Спочатку паралелограм, потім ромб Rhombus rh(10, pi/3); // Конструктор паралелограма викликається // конструктором квадрата, прямокутник і ромб // використовують вже наявний паралелограм Square sq(10);
Контроль типів Простий обробник фігур void  processFigure ( const  Parallelogram & figure) { cout<<figure.whatAmI(); cout<<figure.area(); cout<<figure.perimeter(); return ; }
Доповнений паралелограм class  Parallelogram { protected : double   _ height,  _ width,  _ angle; public : Parallelogram ( double  h,  double  w,  double  a); double  area()  const ; const  string& whoAmI()  const { return  string(“Parallelogram”); } };
Доповнений ромб class  Rhombus:  public  Parallelogram { public : Rhombus ( double  h,  double  a) : Parallelogram (h, h, a){ }; const  string& whoAmI()  const { return  string(“Rhombus”); } }; Те ж саме для прямокутника і квадрата
Приклад   (маємо) // Очікуваний результат Parallelogram   par(20, 30, pi/6); processFigure (par); // паралелограм // Несподіваний результат Rhombus rh(10, pi/3); processFigure (rh); // паралелограм
Приклад (хочемо) // Очікуваний результат Parallelogram   par(20, 30, pi/6); processFigure (par); // паралелограм Rhombus rh(10, pi/3); processFigure (rh); // ромб
Повторення Статичний поліморфізм: Виклик функції за указником Simpson (a, b, eps, pf); Вибір функції за типами параметрі в Complex(1,2)+Complex(3,4); string(“First”)+string(“second”); (Динамічний) поліморфізм: вибрати функцію залежно від типу значення імені об'єкта figure.whatAmI();
Ієрархія типів Об'єкт може належати багатьом типам Квадрат одночасно є прямокутником, ромбом і паралелограмом Прямокутник і ромб одночасно є паралелограмами Ім'я паралелограма здатне приймати значення довільного допустимого типу Параметр  Parallelogram   & par  може прийняти паралелограм,   прямокутник, ромб або квадрат Указник  Parallelogram  *  ppar  може показувати на кожен з них, наприклад,   Parallelogram   * ppar = new Squre(10);
Віртуальні функції Об'єкти володіють даними-членами класу і розділяють функції-члени класу Прямий виклик класної функції (поки що) Triangle((0,0), (0,1), (1,0)).sideA(); Непрямий виклик  віртуальної  функції перевірити яке саме значення має  par взяти функцію (з таблиці віртуальних функцій  vtab ) Непрямий виклик поширюється лише на спеціально позначені функції
Доповнений паралелограм class  Parallelogram { protected : double   _ height,  _ width,  _ angle; public : Parallelogram ( double  h,  double  w,  double  a); virtual double  area()  const ;   virtual const  string& whoAmI()  const { return  string(“Parallelogram”); } };
Доповнений ромб class  Rhombus:  public  Parallelogram { public : Rhombus ( double  h,  double  a) : Parallelogram (h, h, a){ }; virtual const  string& whoAmI()  const { return  string(“Rhombus”); } }; Те ж саме для прямокутника і квадрата
Поліморфізм Можливість динамічно використовувати для роботи з похідними класами інтерфейс базового класу // Поліморфізм не використовується Rectangle rec(10, 20); rec.whoAmI(); // Поліморфізм Parallelogram * par = container.getParallelogram(); par->whoAmI();
Забутливе присвоєння Поліморфізм здатен проявитися лише при застосуванні указника базового класу або передачі базового класу відсилкою (в тому числі сталою) Присвоєння об'єкту похідного класу на місце, призначене для базового об'єкту, призведе до забування додаткових властивостей похідного типу і його зведення до базового Parallelogram par  =  Rectangle(10, 20);
Інтерфейс Можливість динамічно використовувати для роботи з похідними класами інтерфейс (неіснуючого)  абстрактного  базового класу class  Stack { public : virtual ~Stack () {} ; virtual   const  Elem& top() const =0; virtual   void  pop() =0; virtual   void  push( const   Elem &  value) =0; };
Абстрактний клас Відсутність реалізації класної функції позначається нулем Абстрактним називається клас, у якого відсутня реалізація хоча б однієї функції Використовуються для позначення спільного інтерфейсу класів, що допускають різні реалізації Об'єкт абстрактного класу створити не можна (чому?)
Успадкування інтерфейсу class  StackAggregatingArray: public  Stack { public : StackAggregatingArray ( size_t ); ~StackAggregatingArray (); const  Elem& top() const; void  pop() ; void  push( const   Elem &  value) ; private : static const  size_t  _ bos;  size_t _top; Array _array ; };
Успадкування інтерфейсу class  StackAggregatingArray: public  Stack { public : StackAggregatingArray ( size_t ); virtual  ~StackAggregatingArray (); virtual  const  Elem& top() const; virtual  void  pop() ; virtual  void  push( const   Elem &  value) ; private : static const  size_t  _ bos;  size_t _top; Array _stackArray ; };
Діаграма   реалізацій інтерфейсу
Зауваження Абстрактний клас повинен завжди мати (порожній) віртуальний деструктор Перевірте, що станеться, якщо Деструктор не матиме реалізації Деструктор не буде віртуальним
Виклик віртуальних функцій void  process(Stack & stack) { cout<<stack.top(); …………… }; StackDerivedFromArray sdfa; process(sdfa); StackAggregatingArray saga; process(saga);
Вправа Визначити і реалізувати абстрактний клас (інтерфейс) трикутника
З'єднання інтерфейсів class  PeekBack { public : virtual bool  peekback( size_t  i, Elem& elem) c onst =0; virtual  ~PeekBack() { }; }; class  PeekbackStack:  virtual public  Stack,  public  PeekBack { public : virtual bool  peekback(size_t i, Elem& elem)  const =0; virtual  ~PeekbackStack() { }; };
Реалізація з'єднаного інтерфейсу class  Peek B ackStackAgregating Array : // Успадкування інтерфейсу підглядань public  PeekbackStack, // Реалізація інтерфейсу стеку public  StackAgregatingArray, { public : // Реалізації інтерфейсу підглядань Peek B ackStackAgregating Array  ( size_t  size) ;  virtual  ~AgregatingPeekbackStack() ; virtual bool  peekback(int i, Elem& elem)  const ; };
Висновки Віртуальні функції служать для Реалізації інтерфейсів Заміщення ( overriding )  реалізації функції базового класу реалізацією, призначеною для підкласу Поліморфізм приводить до того, що одне і те ж ім'я (указник) протягом виконання програми набуває значень в різних класах, але із спільним інтерфейсом Порівняйте з довизначенням ( overloading )

10 Polymorphism

  • 1.
    Бублик Володимир ВасильовичПрограмування - 2 Лекція 10. Ієрархічне програмування. Поліморфізм Лекції для студентів 2 курсу
  • 2.
    Challenges ієрархійРізні реалізації одного й того ж класу ( StackAggregatingArray , StackDerivedFromArray , StackOnList) Розширення / спеціалізація класів ( DoubleList, CyclicList) Mixin (підмішування) : поєднання кількох функціональностей у одному класі ( PeekBack , Iterated)
  • 3.
    Спеціалізація class Parallelogram { protected : double _ height; double _ width; double _ angle; public : Parallelogram ( double h, double w, double a); double area() const ; double height() const ; double width() const ; double angle() const ; };
  • 4.
    Спеціалізація Ромб іпрямокутник служать прикладами (спеціалізаціями ) паралелограма class Rhombus: public Parallelogram { public : Rhombus ( double h, double a) : Parallelogram (h, h, a){ }; };
  • 5.
    Спеціалізація class Rectangle: public Parallelogram { public : static const double pi = 3.1415926535897932; Rectangle ( double h, double w) : Parallelogram (h, w, pi/2){ }; double area() double { return _height * _ width; } };
  • 6.
  • 7.
    Спеціалізація Як допрямокутника, так і ромба можна застосувати поведінку паралелограма Rhombus rh(10, pi/4); rh.area(); rh.height(); rh.width(); rh.angle(); Rectangle rec(10,20); // власна поведінка rec.area(); // успадкована поведінка Parallelogram :: rec.area(); rec.height(); rec.width(); rec.angle();
  • 8.
    Управління доступом Можназакрити доступ до функції базового класу class Rectangle: public Parallelogram { private : using Parallelogram ::area(); public : static const double pi = 3.1415926535897932; Rectangle ( double h, double w) : Parallelogram (h, w, pi/2){ }; double area() double ; };
  • 9.
    Mixin (змішування): кратне успадкування Хижак class Predator { private : string _name; string _favoritePray; public : Predator (string name, pray); };
  • 10.
    Mixin: кратнеуспадкування Домашній улюбленець class Pet { private : string _name; string _favoriteToy; public : Pet (string name, string toy); };
  • 11.
    Mixin: кратнеуспадкування class Cat: public Predator, public Pet { private : static unsigned int _freeID; unsigned int _myCatID; public : Cat (string name, string toy, string prey): _myCatID (_freeID++), Predator (name, prey), Pet (name, toy){ }; };
  • 12.
    Mixin: кратнеуспадкування class Cat: public Predator, p ublic Pet
  • 13.
    Consistency (узгодженість)Хто слідкуватиме за узгодженістю імен в базових об'єктах? const string& Predator :: rename (string newname) { return name = newname; } void cat::show() const { cout<<Predator::name<<“ aka ”<<Pet::name; }
  • 14.
    Спеціалізація + змішування: спільний базовий клас Скільки паралелограмів міститься в одному квадраті? class Square : public Rectangle, public Rhombus { public : Square ( double side ) : Rectangle( side, side ), Rhombus (side, pi/2) {}; };
  • 15.
  • 16.
    Неоднозначність і неузгодженість Square sq(10); // Помилка: компілятор не знає, // з якого паралелограма брати дані sq.width(); // Помилка: заміна атрибуту в одному з базових об'єктів // з наступним використанням іншого базового об'єкту sq.Rectangle::width() = 20; sq.Rectangle::height() = 20; cout<<sq.Rhombus::area();
  • 17.
  • 18.
    Віртуальне успадкування Відмінимовиклик конструктора в базових класах змішування Конструктор прямокутника (ромба) викличе конструктор паралелограма лише за умови, що його раніше не викликав конструктор квадрату class Rectangle : virtual public Parallelogram; class Rhombus : virtual public Parallelogram; class Square : public Rectangle, public Rhombus;
  • 19.
    Ініціалізація Parallelogram::Parallelogram( double h, double w, double a): _height (h), _width (w), _angle (a) {}; Rectangle::Rectangle ( double h, double w): Parallelogram (h, w, pi/2) {}; Rhombus::Rhombus( double side, double a ): Parallelogram (side, side, a) {}; Square::Square ( double side ) : Rectangle( side, side ), Rhombus (side, pi/2 ) , Parallelogram( side, side, pi/2) {};
  • 20.
    Приклад // Спочаткупаралелограм, потім прямокутник Rectangle rec(10, 20); // Спочатку паралелограм, потім ромб Rhombus rh(10, pi/3); // Конструктор паралелограма викликається // конструктором квадрата, прямокутник і ромб // використовують вже наявний паралелограм Square sq(10);
  • 21.
    Контроль типів Простийобробник фігур void processFigure ( const Parallelogram & figure) { cout<<figure.whatAmI(); cout<<figure.area(); cout<<figure.perimeter(); return ; }
  • 22.
    Доповнений паралелограм class Parallelogram { protected : double _ height, _ width, _ angle; public : Parallelogram ( double h, double w, double a); double area() const ; const string& whoAmI() const { return string(“Parallelogram”); } };
  • 23.
    Доповнений ромб class Rhombus: public Parallelogram { public : Rhombus ( double h, double a) : Parallelogram (h, h, a){ }; const string& whoAmI() const { return string(“Rhombus”); } }; Те ж саме для прямокутника і квадрата
  • 24.
    Приклад (маємо) // Очікуваний результат Parallelogram par(20, 30, pi/6); processFigure (par); // паралелограм // Несподіваний результат Rhombus rh(10, pi/3); processFigure (rh); // паралелограм
  • 25.
    Приклад (хочемо) //Очікуваний результат Parallelogram par(20, 30, pi/6); processFigure (par); // паралелограм Rhombus rh(10, pi/3); processFigure (rh); // ромб
  • 26.
    Повторення Статичний поліморфізм:Виклик функції за указником Simpson (a, b, eps, pf); Вибір функції за типами параметрі в Complex(1,2)+Complex(3,4); string(“First”)+string(“second”); (Динамічний) поліморфізм: вибрати функцію залежно від типу значення імені об'єкта figure.whatAmI();
  • 27.
    Ієрархія типів Об'єктможе належати багатьом типам Квадрат одночасно є прямокутником, ромбом і паралелограмом Прямокутник і ромб одночасно є паралелограмами Ім'я паралелограма здатне приймати значення довільного допустимого типу Параметр Parallelogram & par може прийняти паралелограм, прямокутник, ромб або квадрат Указник Parallelogram * ppar може показувати на кожен з них, наприклад, Parallelogram * ppar = new Squre(10);
  • 28.
    Віртуальні функції Об'єктиволодіють даними-членами класу і розділяють функції-члени класу Прямий виклик класної функції (поки що) Triangle((0,0), (0,1), (1,0)).sideA(); Непрямий виклик віртуальної функції перевірити яке саме значення має par взяти функцію (з таблиці віртуальних функцій vtab ) Непрямий виклик поширюється лише на спеціально позначені функції
  • 29.
    Доповнений паралелограм class Parallelogram { protected : double _ height, _ width, _ angle; public : Parallelogram ( double h, double w, double a); virtual double area() const ; virtual const string& whoAmI() const { return string(“Parallelogram”); } };
  • 30.
    Доповнений ромб class Rhombus: public Parallelogram { public : Rhombus ( double h, double a) : Parallelogram (h, h, a){ }; virtual const string& whoAmI() const { return string(“Rhombus”); } }; Те ж саме для прямокутника і квадрата
  • 31.
    Поліморфізм Можливість динамічновикористовувати для роботи з похідними класами інтерфейс базового класу // Поліморфізм не використовується Rectangle rec(10, 20); rec.whoAmI(); // Поліморфізм Parallelogram * par = container.getParallelogram(); par->whoAmI();
  • 32.
    Забутливе присвоєння Поліморфізмздатен проявитися лише при застосуванні указника базового класу або передачі базового класу відсилкою (в тому числі сталою) Присвоєння об'єкту похідного класу на місце, призначене для базового об'єкту, призведе до забування додаткових властивостей похідного типу і його зведення до базового Parallelogram par = Rectangle(10, 20);
  • 33.
    Інтерфейс Можливість динамічновикористовувати для роботи з похідними класами інтерфейс (неіснуючого) абстрактного базового класу class Stack { public : virtual ~Stack () {} ; virtual const Elem& top() const =0; virtual void pop() =0; virtual void push( const Elem & value) =0; };
  • 34.
    Абстрактний клас Відсутністьреалізації класної функції позначається нулем Абстрактним називається клас, у якого відсутня реалізація хоча б однієї функції Використовуються для позначення спільного інтерфейсу класів, що допускають різні реалізації Об'єкт абстрактного класу створити не можна (чому?)
  • 35.
    Успадкування інтерфейсу class StackAggregatingArray: public Stack { public : StackAggregatingArray ( size_t ); ~StackAggregatingArray (); const Elem& top() const; void pop() ; void push( const Elem & value) ; private : static const size_t _ bos; size_t _top; Array _array ; };
  • 36.
    Успадкування інтерфейсу class StackAggregatingArray: public Stack { public : StackAggregatingArray ( size_t ); virtual ~StackAggregatingArray (); virtual const Elem& top() const; virtual void pop() ; virtual void push( const Elem & value) ; private : static const size_t _ bos; size_t _top; Array _stackArray ; };
  • 37.
    Діаграма реалізацій інтерфейсу
  • 38.
    Зауваження Абстрактний класповинен завжди мати (порожній) віртуальний деструктор Перевірте, що станеться, якщо Деструктор не матиме реалізації Деструктор не буде віртуальним
  • 39.
    Виклик віртуальних функційvoid process(Stack & stack) { cout<<stack.top(); …………… }; StackDerivedFromArray sdfa; process(sdfa); StackAggregatingArray saga; process(saga);
  • 40.
    Вправа Визначити іреалізувати абстрактний клас (інтерфейс) трикутника
  • 41.
    З'єднання інтерфейсів class PeekBack { public : virtual bool peekback( size_t i, Elem& elem) c onst =0; virtual ~PeekBack() { }; }; class PeekbackStack: virtual public Stack, public PeekBack { public : virtual bool peekback(size_t i, Elem& elem) const =0; virtual ~PeekbackStack() { }; };
  • 42.
    Реалізація з'єднаного інтерфейсуclass Peek B ackStackAgregating Array : // Успадкування інтерфейсу підглядань public PeekbackStack, // Реалізація інтерфейсу стеку public StackAgregatingArray, { public : // Реалізації інтерфейсу підглядань Peek B ackStackAgregating Array ( size_t size) ; virtual ~AgregatingPeekbackStack() ; virtual bool peekback(int i, Elem& elem) const ; };
  • 43.
    Висновки Віртуальні функціїслужать для Реалізації інтерфейсів Заміщення ( overriding ) реалізації функції базового класу реалізацією, призначеною для підкласу Поліморфізм приводить до того, що одне і те ж ім'я (указник) протягом виконання програми набуває значень в різних класах, але із спільним інтерфейсом Порівняйте з довизначенням ( overloading )