LEKCJA 27: O DZIEDZICZENIU. ________________________________________________________________ W trakcie tej lakcji dowiesz się na czym polega dziedziczenie. ________________________________________________________________ Dziedziczenie (ang inheritance) jest próbą naśladowania w technice programowania najcenniejszego bodaj wynalazku Matki Natury - zdolności przekazywania cech. Jeśli wyobrazimy sobie typy struktur konik, lew, słoń, czy krokodyl, to jest oczywiste, że struktury te będą posiadać wiele wspólnych cech. Wspólnymi cechami mogą być zarówno wspólne dane (parametry) - np. nogi = 4; jak i wspólne wykonywane przez nie funkcje - np. jedz(), śpij(), oddychaj() itd.. Mogą występować oczywiście i różnice, ale wiele danych i funkcji okaże się wspólnych. LOGIKA DZIEDZICZENIA. Rozwijając dalej myśl naszkicowaną we wstępie, w kategoriach obiegowego języka naturalnego można rzec, że słoń Trombalski byłby tu strukturą typu formalnego Słoń. Funkcjami wewnętrznymi słonia Trombalskiego i np. krokodyla Eugeniusza mogłyby być wspólne czynności tych struktur (lub obiektów): jedz() śpij() oddychaj() Projektanci C++ wpadli na pomysł naśladowania mechanizmu dziedziczenia. Zamiast tworzyć te wszystkie struktury oddzielnie, możemy zdefiniować w C++ jeden ogólny typ struktur (ang. generic structure), nazywany inaczej STRUKTURĄ BAZOWĄ (ang. base structure). Wszystkie wymienione wyżej struktury (słoń, krokodyl, itp.) stałyby się wtedy strukturami pochodnymi (ang. derived structures). Nasza struktura bazowa mogłaby nazywać się znów np. Zwierzak. Ponieważ niektóre funkcje są wspólne dla wszystkich struktur (wszystkie Zwierzaki muszą jeść, spać, itp.), moglibyśmy przyjąć, że każda struktura pochodna od bazowego typu Zwierzak musi zawierać funkcje jedz(), spij() i oddychaj(). Jeśli zdefiniujemy strukturę bazową Zwierzak i zadeklarujemy w tej klasie funkcje jedz(), spij() i oddychaj(), możemy spodziewać się, że struktura pochodna słoń powinna odziedziczyć funkcje - cechy po strukturze bazowej Zwierzak. . Słoń może oczywiście mieć i swoje odrębne cechy - dane i funkcje - np.: Slon.flaga_ssak Slon.trabie() Slon.tupie() "Gramatyka" C++ przy opisywaniu wzajemnego pokrewieństwa struktur (i klas) wygląda następująco: struct NazwaStrukturyPochodnej : NazwaStrukturyBazowej { private: Lista danych i funkcji prywatnych public: Lista danych i funkcji publicznych } Lista struktur danego typu; a dla klas i obiektów: class NazwaKlasyPochodnej : dostęp NazwaKlasyBazowej { Lista danych i funkcji prywatnych public: Lista danych i funkcji publicznych } Lista obiektow danej klasy; Bazowy typ struktur w C++ wyglądałaby tak: struct Zwierzak { void jedz(); void spij(); void oddychaj(); }; Jeśli chcemy zasygnalizować, że pochodny typ struktur Slon ma odziedziczyć coś po typie bazowym Zwierzak, musimy w definicji klasy pochodnej podać nazwę klasy bazowej (jeśli mamy dziedziczyć - należy wskazać po kim): struct Slon : Zwierzak { int trabie(); int tupie(); }; Przed nazwą typu struktury (klasy) bazowej (tu: Zwierzak) może pojawić się słowo określające zasady dostępu do danych i funkcji (tu: public). [!!!] RÓŻNIE MOŻNA DZIEDZICZYĆ... ________________________________________________________________ * Jeśli użyjemy w tym miejscu słowa public (przy strukturach domyślne), to atrybuty dostępu zostaną odziedziczone wprost. Oznacza to, że to, co było prywatne w strukturze bazowej zostanie przeniesione jako prywatne do struktury pochodnej, a to, co było publiczne w strukturze bazowej zostanie przeniesione jako publiczne do struktury pochodnej. * Jeśli natomiast użyjemy w tym miejscu słowa private, to wszystko, co struktura pochodna odziedziczy po strukturze bazowej stanie się w strukturze pochodnej prywatne. ________________________________________________________________ Opracowanie przykładowego programu ilustrującego mechanizm dziedziczenia rozpoczniemy od zdefiniowania bazowego typu struktur i struktury pochodnej. struct Zwierzak { int nogi; <-- dane void jedz(); <-- funkcje void spij(); void oddychaj(); }; struct Slon : Zwierzak { int flaga_ssak; int trabie(); int tupie(); }; Zdefiniujemy teraz wszystkie funkcje należące do powyższych struktur. Funkcje będą tylko zgłaszać się na ekranie napisem, by prześledzić kolejność ich wywołania. void Zwierzak::jedz(void) { cout << "Jem conieco...\n"; } void Zwierzak::spij(void) { cout << "Cosik mi sie sni...\n"; } void Zwierzak::oddychaj(void) { cout << "Dyszę cieżko...\n"; } void Slon::trabi(void) { cout << "Tra-ta-ta...\n"; } void Slon::tupie(void) { cout << "Kroczem...na zachód\n"; } Aby przekonać się, co struktura typu Slon rzeczywiście odziedziczy "po przodku", zredagujemy program główny. # include ... void main() { Slon Choleryk; //Deklaracja struktury ... cout << "\nNogi odziedziczylem: " << Choleryk.nogi; cout << "\nA teraz kolejno funkcje: \n"; Choleryk.jedz(); Choleryk.spij(); Choleryk.oddychaj(); Choleryk.trabi(); Choleryk.tupie(); } Mimo, że tworząc strukturę Słoń nie zadeklarowaliśmy w jej składzie ani funkcji jedz(), ani spij(), ani danych nogi, możemy zastosować funkcję Choleryk.jedz(), ponieważ Choleryk odziedziczył tę funkcję po strukturze bazowej Zwierzak. Dzięki dziedziczeniu możemy posługiwać się danymi i funkcjami należącymi do obu typów struktur - bazowego: Zwierzak i pochodnego: Slon. [???] A CO Z UNIAMI ? _______________________________________________________________ Unie nie mogą brać udziału w dziedziczeniu. Unia nie może być ani typem bazowym ani typem pochodnym. _______________________________________________________________ Program w całości będzie wyglądał tak: [P099.CPP] # include struct Zwierzak { int nogi; void jedz(); void spij(); void oddychaj(); }; void Zwierzak::jedz(void) { cout << "Jem conieco...\n"; } void Zwierzak::spij(void) { cout << "Cosik mi sie sni...\n"; } void Zwierzak::oddychaj(void) { cout << "Dysze ciezko...\n"; } struct Slon : Zwierzak { int flaga_ssak; void trabi(); void tupie(); }; void Slon::trabi(void) { cout << "Tra-ta-ta...\n"; } void Slon::tupie(void) { cout << "Kroczem...na wschod\n"; } void main() { Slon Choleryk; Choleryk.nogi = 4; Choleryk.flaga_ssak = 1; cout << "\nNogi odziedziczylem: " << Choleryk.nogi; cout << "\nA teraz kolejno funkcje: \n"; Choleryk.jedz(); Choleryk.spij(); Choleryk.oddychaj(); Choleryk.trabi(); Choleryk.tupie(); if(Choleryk.flaga_ssak == 1) cout << "SSak!"; }