Dziedziczenie wielobazowe
dana klasa może mieć kilka bezpośrednich klas bazowych: dana klasa może mieć kilka bezpośrednich klas bazowych: kolorpołożenie kolorpołożenie punkt punkt dziedziczenie wielobazowe to sposób, w jaki programista opisuje obiekty które łączą cechy różnych klas. dziedziczenie wielobazowe to sposób, w jaki programista opisuje obiekty które łączą cechy różnych klas.
Dziedziczenie wielobazowe dana klasa może mieć kilka bezpośrednich klas bazowych: dana klasa może mieć kilka bezpośrednich klas bazowych: kolorpołożenie kolorpołożenie punkt punkt class kolor {}; class położenie {}; class punkt: public kolor, public położenie {... };
Deklaracja klasy pochodnej class punkt: public kolor, public położenie {... }; domyślnie dziedziczymy prywatnie, jeżeli chcemy publicznie to zadeklarujmy to jawnie dla każdej z klas bazowych, która ma być przodkiem publicznym domyślnie dziedziczymy prywatnie, jeżeli chcemy publicznie to zadeklarujmy to jawnie dla każdej z klas bazowych, która ma być przodkiem publicznym dwukropek w deklaracji występuje tylko raz, potem przecinki dwukropek w deklaracji występuje tylko raz, potem przecinki wszystkie klasy bazowe muszą być w całości zdefiniowane wszystkie klasy bazowe muszą być w całości zdefiniowane nie wystarczy definicja nazwy klasy bazowej (class C) nie wystarczy definicja nazwy klasy bazowej (class C) w ten sposób wykluczamy zapętlenie w dziedziczeniu – klasa nie może być własnym przodkiem. w ten sposób wykluczamy zapętlenie w dziedziczeniu – klasa nie może być własnym przodkiem.
Konstruktory klas bazowych class punkt: public kolor, public położenie {... }; konstruktory klas bazowych wywoływane są w kolejności takiej, w jakiej zadeklarowano klasy bazowe w deklaracji danej klasy konstruktory klas bazowych wywoływane są w kolejności takiej, w jakiej zadeklarowano klasy bazowe w deklaracji danej klasy 1. kolor() 2. położenie()
Niejednoznaczności Występują gdy takie same nazwy pól lub metod odziedziczone są po więcej niż jednej bezpośredniej klasie bazowej. Występują gdy takie same nazwy pól lub metod odziedziczone są po więcej niż jednej bezpośredniej klasie bazowej. class kolor {public: int k; char *nazwa}; class położenie { public: int k; int j }; class punkt: public kolor, public położenie {... }; punkt zawiera pola: kolor::kpołożenie::k kolor::nazwapołożenie::j
Niejednoznaczności punkt zawiera pola: kolor::kpołożenie::k kolor::nazwapołożenie::j odwołania w zakresie klasy punkt: odwołania w zakresie klasy punkt: punkt::nazwa; // OK punkt::j; // OK // punkt::k — błąd taki błąd wystąpi również gdy jest tylko jedna dostępna nazwa (gdy np. pozostałe są prywatnymi składowymi klas) taki błąd wystąpi również gdy jest tylko jedna dostępna nazwa (gdy np. pozostałe są prywatnymi składowymi klas) najpierw jest sprawdzana jednoznaczność, potem dostępność. najpierw jest sprawdzana jednoznaczność, potem dostępność. żeby uniknąć niejednoznaczności należy podać, operatorem zakresu, przodka od którego nazwa pochodzi żeby uniknąć niejednoznaczności należy podać, operatorem zakresu, przodka od którego nazwa pochodzi
Niejednoznaczności class kj {public: int k,j}; class odcinek: public punkt, public polozenie {}; klasa odcinek zawiera pola: klasa odcinek zawiera pola: punkt::kolor::kkj::k punkt::kolor::nazwa kj::j punkt::położenie::kpunkt::położenie::j odwołując się do nazwy, trzeba podać zakres, w którym jest jednoznaczna: odwołując się do nazwy, trzeba podać zakres, w którym jest jednoznaczna: odcinek::nazwa→odcinek::punkt::kolor::nazwa położenie::j →odcinek::punkt::położenie::j punkt::j →odcinek::punkt::położenie::j położenie::k→odcinek::punkt::położenie::k
Niejednoznaczności Aby zlikwidować niejednoznaczność nazwy można zdefiniować akcesor, którego nazwa przykryje nazwę niejednoznaczną. Aby zlikwidować niejednoznaczność nazwy można zdefiniować akcesor, którego nazwa przykryje nazwę niejednoznaczną. np. dla klasy punkt: np. dla klasy punkt: int & punkt::k(){ return kolor::k; } int & punkt::k(){ return kolor::k; } użycie: użycie: punkt p; punkt p; p.k()=7; p.k()=7; wewnątrz metod klasy punkt i pochodnych punkt::k() będzie się zachowywać jak pukt::kolor::k wewnątrz metod klasy punkt i pochodnych punkt::k() będzie się zachowywać jak pukt::kolor::k niedoskonałe – bo zmienną zastępujemy metodą, niedoskonałe – bo zmienną zastępujemy metodą, skuteczne – bo kompilator zoptymalizuje kod skuteczne – bo kompilator zoptymalizuje kod
Wielokrotne wystąpienia tej samej klasy bazowej każdy z bezpośrednich przodków musi występować pojedynczo. każdy z bezpośrednich przodków musi występować pojedynczo. nie wolno: nie wolno: class odcinek: kolor, położenie, położenie {}; bezpośredni przodek nie może być równocześnie niebezpośrednim przodkiem. bezpośredni przodek nie może być równocześnie niebezpośrednim przodkiem. nie wolno: nie wolno: class odc_2: punkt, położenie {}; Wniosek: jeżeli potrzebujemy kilku egzemplarzy danej klasy to: Wniosek: jeżeli potrzebujemy kilku egzemplarzy danej klasy to: 1. możemy je zawrzeć w klasie, a nie odziedziczyć 2. możemy je odziedziczyć kilkakrotnie, lecz każdorazowo jako przodka niebezpośredniego
Wielokrotne wystąpienia tej samej klasy bazowej okno okno okno okno o_z ramkąo_z_menu o_z_ramka_i_menu o_z_ramka_i_menu class okno {}; class o_z_ramka:public okno {}; class o_z_menu:public okno {}; class o_z_ramka_i_menu:public o_z_ramka, public o_z_menu{};
Wirtualne klasy bazowe okno okno o_z ramkąo_z_menu o_z_ramka_i_menu o_z_ramka_i_menu
Wirtualne klasy bazowe - deklarowanie okno okno o_z ramkąo_z_menu o_z_ramka_i_menu o_z_ramka_i_menu class okno {}; class o_z_ramka:public virtual okno {}; class o_z_menu:public virtual okno {}; class o_z_ramka_i_menu:public o_z_ramka, public o_z_menu{}; we wszystkich klasach pochodnych dla których chcemy żeby w razie dziedziczenia po kilku z nich ich bezpośrednia klasa bazowa wystąpiła tylko raz należy tę klasę bazową zadeklarować ze słowem kluczowym virtual we wszystkich klasach pochodnych dla których chcemy żeby w razie dziedziczenia po kilku z nich ich bezpośrednia klasa bazowa wystąpiła tylko raz należy tę klasę bazową zadeklarować ze słowem kluczowym virtual wystarczy żeby bezpośredni potomek dziedziczył wirtualnie – o_z_ramka_i_menu już nie musi. wystarczy żeby bezpośredni potomek dziedziczył wirtualnie – o_z_ramka_i_menu już nie musi.
Ograniczenia dostępu do odziedziczonych pól jeżeli pola wirtualnej klasy bazowej są dostępne przez przynajmniej jedną ze ścieżek wiodących do tej klasy, to można się odwoływać do nich w zakresie klasy potomnej jeżeli pola wirtualnej klasy bazowej są dostępne przez przynajmniej jedną ze ścieżek wiodących do tej klasy, to można się odwoływać do nich w zakresie klasy potomnej Oczywiście jeżeli w innej ścieżce jest dziedziczona prywatnie to nie można użyć zakresu w którym jest niewidoczna Oczywiście jeżeli w innej ścieżce jest dziedziczona prywatnie to nie można użyć zakresu w którym jest niewidoczna przykład: przykład: class o_z_ramka:private virtual okno {}; o_z_ramka_i_menu::o; // ok., odpowiednik ponizszego o_z_ramka_i_menu::o_z_menu::o; //ok. // o_z_ramka_i_menu::o_z_ramka::o – tutaj prywatne
Ograniczenia dostępu do odziedziczonych pól okno okno o_z ramkąo_z_menu o_z_ramka_i_menu o_z_ramka_i_menu class okno {}; class o_z_ramka:private virtual okno {}; class o_z_menu: private virtual okno {}; class o_z_ramka_i_menu :public o_z_ramka, public o_z_menu, public virtual okno {}; :public o_z_ramka, public o_z_menu, public virtual okno {}; bezpośredni przodek wirtualny może jednocześnie być przodkiem niebezpośrednim – nie ma niejednoznaczności bezpośredni przodek wirtualny może jednocześnie być przodkiem niebezpośrednim – nie ma niejednoznaczności
Kolejność wywołania konstruktorów przed wszystkimi innymi wywoływane są konstruktory klas bazowych wirtualnych w kolejności od pierwszego wystąpienia w deklaracji kl. pochodnej. przed wszystkimi innymi wywoływane są konstruktory klas bazowych wirtualnych w kolejności od pierwszego wystąpienia w deklaracji kl. pochodnej. parametry konstruktora dla klasy bazowej wirtualnej można przekazać w konstruktorach wszystkich (nawet niebezpośrednich) klas pochodnych. parametry konstruktora dla klasy bazowej wirtualnej można przekazać w konstruktorach wszystkich (nawet niebezpośrednich) klas pochodnych. Zastosowanie znajdą te parametry, które opisano w liście inicjalizazacyjnej konstruowanej klasy – np. konstruujemy z_ramka_i_menu to parametry konstrukcji okna z listy okna z ramka zostana zignorowane. Zastosowanie znajdą te parametry, które opisano w liście inicjalizazacyjnej konstruowanej klasy – np. konstruujemy z_ramka_i_menu to parametry konstrukcji okna z listy okna z ramka zostana zignorowane. UWAGA: jeżeli nie opisze się wywołania się konstruktora wirtualnej klasy bazowej jawnie to zostanie zastosowany konstruktor domniemany. UWAGA: jeżeli nie opisze się wywołania się konstruktora wirtualnej klasy bazowej jawnie to zostanie zastosowany konstruktor domniemany.
Wielokrotne wywoływanie metod klasy bazowej okno okno o_z ramkąo_z_menu o_z_ramka_i_menu o_z_ramka_i_menu każda klasa ma metodę rysuj(); każda klasa ma metodę rysuj(); metoda rysuj() danej klasy wywołuje metodę rysuj() przodka; metoda rysuj() danej klasy wywołuje metodę rysuj() przodka; o_z_ramka_i_menu::rysuj() wywoła metody rysuj() obu przodków; o_z_ramka_i_menu::rysuj() wywoła metody rysuj() obu przodków; o_z_ramka_i_menu::rysuj() pośrednio wywoła dwukrotnie okno::rysuj(); o_z_ramka_i_menu::rysuj() pośrednio wywoła dwukrotnie okno::rysuj();
Wielokrotne wywoływanie metod klasy bazowej void o_z_ramka::_rysuj() { //tu rysowanie właściwe dla ozramka } void o_z_ramka::rysuj() { okno::_rysuj(); _rysuj(); _rysuj();} void o_z_ramka_i_menu::_rysuj() { //tu rysowanie właściwe dla ozramka } void o_z_ramka_i_menu::rysuj() {okno::_rysuj(); o_z_ramka::_rysuj(); o_z_ramka::_rysuj(); o_z_menu::_rysuj(); o_z_menu::_rysuj(); _rysuj _rysuj}
Klasa bazowa wirtualna i nie- wirtualna okno okno okno okno o_z ramkąo_z_menu o_z_ramka_i_menu o_z_ramka_i_menu class okno {}; class o_z_ramka: public virtual okno {}; class o_z_menu: public okno {}; // okno dziedziczone nie-wirtualnie class o_z_ramka_i_menu // tu nie wolno bez virtual: public okno :public o_z_ramka, public o_z_menu {}; :public o_z_ramka, public o_z_menu {}; dana klasa może być jednocześnie dziedziczona wirtualnie i nie-wirtualnie dana klasa może być jednocześnie dziedziczona wirtualnie i nie-wirtualnie UWAGA: bezpośredni przodek nie- wirtualny nie może jednocześnie być przodkiem niebezpośrednim – niejednoznaczność UWAGA: bezpośredni przodek nie- wirtualny nie może jednocześnie być przodkiem niebezpośrednim – niejednoznaczność