Zaawansowane programowanie w C++ (PCP) Wykład 3 - polimorfizm. dr inż. Robert Nowak - p. 1/14
Powtórzenie Powtórzenie: klasy autonomiczne: konstruktor, konstruktor kopiujacy, operator przypisania, destruktor tworzenie nowych typów: dziedziczenie i agregacja dziedziczenie: nadpisywanie metod, problem przycinania, zasłanianie - p. 2/14
» Rozwiazanie: pole typu Zalety podejścia obiektowego: klasy autonomiczne enkapsulacja (ochrona składowych) konstrukcja i destrukcja polimorfizm - brak odpowiednika w podejściu strukturalnym. umożliwia definiowanie ogólnych cech pokrewnych typów; - p. 3/14
Problem typu dla obiektów» Rozwiazanie: pole typu Problem wołania właściwych metod dla obiektu, gdy używa się wskaźnika do obiektu klasy bazowej. class Osoba { void drukuj(ostream& os) const { os << osoba ; } class Pracownik : public Osoba { void drukuj(ostream& os) const { os << pracownik ; } void drukujosobe(const Osoba& f) { cout << f.drukuj(); } Osoba o; Pracownik p; drukujosobe(o);//wołana metoda Osoba::drukuj drukujosobe(p);//wołana metoda Osoba::drukuj - p. 4/14
Rozwiazanie: pole typu» Rozwiazanie: pole typu class Osoba { enum Typ { OSOBA, PRACOWNIK, KLIENT const Typ typ_;//pole pamięta typ obiektu Osoba() : typ_(osoba) {} class Pracownik : public Osoba { Pracownik() : typ_(pracownik) {} void drukuj(const Osoba& o) { switch(o.typ)... } Bardzo złe rozwiazanie! kompilator nie potrafi sprawdzić poprawności kod staje się nieczytelny i trudno modyfikowalny - p. 5/14
Funkcje wirtualne» Rozwiazanie: pole typu Inne rozwiazanie problemu pola typu, wspierane przez kompilator. class Osoba { virtual void drukuj(ostream& os) const { metoda jest wirtualna jeżeli w klasie bazowej jest poprzedzona słowem virtual metody sa nadpisywane (zastępowane) w klasie pochodnej maja identyczna sygnaturę w klasie pochodnej metoda może być (ale nie musi) poprzedzona słowem virtual class Pracownik : public Osoba { virtual void drukuj(ostream& os) const { os << pracownik ; - p. 6/14
» Rozwiazanie: pole typu kompilator wybiera najbardziej właściwa metodę (wśród nadpisywanych) wyjatek: jawne wskazanie funkcji, np. Prostokat::drukuj() nadpisywanie bardziej restrykcyjne niż przedefiniowywanie Zastosowania: umożliwia definiowanie ogólnych cech pokrewnych typów; można pisać ogólne funkcje działajace dla wszystkich pochodnych pewnej klasy bazowej; elastyczny system typów: można dodawać nowe typy bez modyfikacji już istniejacego kodu; Znajdowanie cech wspólnych dla typów nie jest prostym zadaniem. Mechanizm funkcji wirtualnych w innych językach programowania i w C++. - p. 7/14
Późne wiazanie» Rozwiazanie: pole typu class A { virtual void f(){ virtual void g(){ private: //SkladoweA class B : public A { virtual void f(){ virtual void g(){ private: //SkladoweB A a; B b; A* pa = b; pa->f(); obiekta vtbl składowea obiektb vtbl składowea składoweb &A::f &A::g &B::f &B::g Późne wiazanie (narzuty) pamięciowe: jeden wskaźnik w obiekcie (vtbl) czasowe: jeden skok więcej - p. 8/14
Destruktor» Rozwiazanie: pole typu Jeżeli klasa jest klasa bazowa, to powinna mieć wirtualny destruktor! class Bazowa { class Pochodna : public Bazowa { Bazowa* ptr = new Pochodna(); delete ptr;//błąd! Wywoła się destruktor dla bazowa. class Bazowa2 { virtual Bazowa2(){} class Pochodna2 : public Bazowa2 { Bazowa2* ptr2 = new Pochodna2(); delete ptr2;//wywoła się destruktor dla Pochodna2 - p. 9/14
» Rozwiazanie: pole typu funkcje wirtualne, późne wiazanie kompilator wybiera najbardziej właściwa metodę (wśród nadpisywanych) wyjatek: jawne wskazanie funkcji, np. Prostokat::drukuj() Zastosowania: umożliwia definiowanie ogólnych cech pokrewnych typów; można pisać ogólne funkcje działajace dla wszystkich pochodnych pewnej klasy bazowej; elastyczny system typów: można dodawać nowe typy bez modyfikacji już istniejacego kodu; Znajdowanie cech wspólnych dla typów nie jest prostym zadaniem. - p. 10/14
Klasy abstrakcyjne» Rozwiazanie: pole typu Klasy abstrakcyjne: klasy, które maja sens jedynie jako interfejs (klasa bazowa) nie można dostarczyć sensownej implementacji metod wirtualnych nie powinny być tworzone obiekty takiej klasy często reprezentuje abstrakcyjne pojęcie Funkcje czysto wirtualne: class Figura { virtual void rysuj() = 0; Jeżeli klasa zawiera funcje czysto wirtualne to jest klasa abstrakcyjna. kompilator nie dopuszcza do tworzenia obiektów takich klas wykrywany bład obcinania już na etapie kompilacji! - p. 11/14
Hierarchie klas» Rozwiazanie: pole typu class Figura { virtual void rysuj() = 0; virtual bool czywypukla() = 0; class Wielokat : public Figura { virtual bool czywypukla(){ return true;} protected: std::vector<point> wierz_; class Prostokat : public Wielokat { virtual void rysuj();//implementacja Figura Koło Wielokąt Złożona Trójkąt Prostokąt - p. 12/14
Interfejsy» Rozwiazanie: pole typu Klasa abstrakcyjna: nie musi dostarczać konstruktorów stanowi interfejs dla swoich klas pochodnych, tzn. określamy metody jakie te klasy musza implementować bardzo wygodne rozdzielenie interfejsu od implementacji Czysty interfejs: klasa abstrakcyjna składajaca się głównie z funkcji czysto wirtualnych nie przechowuje stanu tylko określa interfejs - p. 13/14
Właściwości klasy bazowej» Rozwiazanie: pole typu Klasa wartość (klasa autonomiczna): brak metod wirtualnych konstruktor, konstruktor kopiujacy, operator przypisania, destruktor najczęściej obiekt automatyczny lub składowa klasy przekazywany przez wartość lub stała referencję Klasa bazowa dla hierarchii klas: używa metod wirtualnych, powinna mieć wirtualny destruktor najlepiej gdy abstrakcyjna albo prywatny konstruktor kopiujacy i operator przypisania (zapobiega wycinaniu) najczęściej obiekt na stercie przekazywana przez wskaźnik lub referencję - p. 14/14