LEKCJA 28: DZIEDZICZENIE Z�O�ONE. ________________________________________________________________ W trakcie tej lekcji dowiesz si�, jak mo�na odziedziczy� wiele cech po wielu r�nych przodkach. ________________________________________________________________ Je�li zechcemy dziedziczy� dalej wed�ug schematu dziadek-ojciec-syn-wnuk...? Nic nie stoi na przeszkodzie. Przy okazji zwr�� uwag�, �e nast�pne pokolenia s� coraz bardziej z�o�one (tak by� nie musi, ale mo�e). W przyk�adzie poni�ej dziedziczymy wed�ug schematu Punkt-Okr�g-Elipsa. [P100.CPP] //Przyklad dziedziczenia "wielopokoleniowego" #include "stdio.h" #include "conio.h" struct punkt //BAZOWY typ struktur - punkt(x, y) { int x; //wspolrzedne punktu na ekranie int y; }; struct kolo: punkt //Str. pochodna - kolo(x, y, R) { int promien; //wspolrzedne srodka x,y dziedziczymy }; struct elipsa: kolo //dziedziczymy x,y i promien { int mniejszy_promien; //Str. pochodna elipsa(x, y, R, r) }; punkt P; //deklarujemy trzy struktury kolo C; elipsa E; main() { clrscr(); P.x = C.x = E.x = 1; //Nadajemy wartosci polom struktur P.y = C.y = E.y = 2; C.promien = E.promien = 4; E.mniejszy_promien = 3; //Sprawdzamy zawartosc pol struktur printf("%d %d %d %d %d %d \n", P.x, C.x, E.x, P.y, C.y, E.y); printf("%d %d %d", C.promien, E.promien, E.mniejszy_promien ); getch(); return 0; } Mo�na dziedziczy� po wi�cej ni� jednym przodku tak�e w inny spos�b. Kwadrat, dla przyk�adu, dziedziczy cechy po prostok�tach i po rombach jednocze�nie (jest jednocze�nie szczeg�lnym przypadkiem prostok�ta i szczeg�lnym przypadkiem rombu). Typ pochodny w tym wypadku, zamiast "dziadka" i "ojca" powinien mie� DWU RӯNYCH OJC�W (!). W C++ takie dziedziczenie po dwu r�nych typach bazowych jednocze�nie nazywa si� DZIEDZICZENIEM WIELOBAZOWYM (ang. multi-base inheritance). A oto przyk�ad takiego dziedziczenia. [P101.CPP] #include <iostream.h> struct BAZOWA1 { //Struktura bazowa pierwsza public: void Funkcja_a(void); }; struct BAZOWA2 { //Struktura bazowa druga public: void Funkcja_b(void); }; struct POCHODNA : BAZOWA1, BAZOWA2 //Lista "przodkow" { public: void Funkcja_c(void); }; void BAZOWA1::Funkcja_a(void){cout << "To ja F_a().\n";} void BAZOWA2::Funkcja_b(void){cout << "To ja F_b().\n";} void POCHODNA::Funkcja_c(void){cout << "To ja F_c().\n";} void main() { POCHODNA dziecko; //Dekl. strukt. typu pochodnego dziecko.Funkcja_a(); dziecko.Funkcja_b(); dziecko.Funkcja_c(); } S�owo public jest w strukturach zb�dne. Zosta�o u�yte wy��cznie z pobudek "dydaktycznych" - dla zwr�cenia uwagi na status funkcji - cz�onk�w struktury. Zar�wno pokole� w schemacie dziadek-ojciec-syn, jak i struktur (klas) bazowych w schemacie baza_1-baza_2-....-baza_n mo�e by� wi�cej ni� 2. DZIEDZICZENIE KLAS. Oto "klasowo-obiektowa" wersja poprzedniego programu przyk�adowego ze s�onikiem Cholerykiem. Typy struktur Zwierzak i Slon nazwiemy klasami, (odpowiednio - klas� bazow� i klas� pochodn�) a struktur� Slon Choleryk nazwiemy obiektem. [P102.CPP] #include <iostream.h> class Zwierzak //Klasa bazowa (base class) { public: 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"; } class Slon : public Zwierzak { public: 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 Obiekt; /* obiekt Obiekt klasy Slon */ Obiekt.nogi = 4; Obiekt.flaga_ssak = 1; cout << "\nNogi odziedziczylem: " << Obiekt.nogi; cout << "\nA teraz kolejno funkcje: \n"; Obiekt.jedz(); Obiekt.spij(); Obiekt.oddychaj(); Obiekt.trabi(); Obiekt.tupie(); if(Obiekt.flaga_ssak) cout << "Jestem ssakiem !"; } Pami�taj�c o problemie domy�lnego statusu cz�onk�w struktur/public i klas/private) mo�emy przej�� do klas i obiekt�w. O KLASACH SZCZEGӣOWO. Aby wykaza� mo�liwo�� modularyzacji programu zaprojektujemy modu� w postaci pliku nag��wkowego. Modu� b�dzie zawiera� definicj� naszej prywatnej klasy obiekt�w ZNAK. Zaczynamy od danych, kt�re b�d� nam potrzebne do tworzenia w programach (r�nych !) obiekt�w typu Znak. class ZNAK { char znak_dany; //Kod ASCII znaku ... Aby obiekt zosta� zainicjowany (tzn. wiedzia� jakim znakiem ma by� w danym programie) dodamy do definicji klasy jednoparametrowy konstruktor class ZNAK { char znak_dany; public: ZNAK(...); ... Dane mog� by� prywatne, natomiast konstruktor i funkcje-metody powinny by� publiczne, by mo�na by�o wywo�ywa� je w programach. Konstruktor b�dziemy wywo�ywa� w programach tak: ZNAK Obiekt('a'); Znaczy to: Utw�rz w RAM obiekt klasy ZNAK pod nazw� "Obiekt" i wyt�umacz mu, �e jest znakiem 'a'. Konstruktor powinien pobiera� od programu jeden argument typu char i przekazywa� go obiektowi klasy ZNAK na jego pole danych znak_dany. Definicja konstruktora b�dzie zatem wygl�da� tak: ZNAK::ZNAK(char x) { znak_dany = x; } Zakres dopuszczalnych znak�w zaw�zimy np. do kod�w ASCII 65...90 (od A do Z). Je�li u�ytkownik "nie trafi", ustawimy zawsze "*" (asterisk). Dodatkowo, dla "elegancji" zamienimy ewentualne ma�e litery na du�e. ZNAK::ZNAK(char x) { znak_dany = x; if(znak_dany < 65 || znak_dany >122) znak_dany = '*'; if(znak_dany > 97) znak_dany -= 32; } A je�li u�ytkownik nie zechce poda� �adnego znaku i zda si� na domy�lno�� obiektu? �aden problem, wystarczy do klasy ZNAK doda� bezparametrowy konstruktor domy�lny. Konstruktory domy�lne spe�niaj� w C++ tak� w�a�nie rol�: class ZNAK { char znak_dany; public: ZNAK(char); //Konstruktor zwykly ("jednoznakowy") ZNAK(void); //Konstruktor domy�lny (bezparametrowy) ... S�owo void (tu opcjonalne) mo�e nie wyst�pi�. Aby "k�u�o w oczy", kt�ry konstruktor jest konstruktorem domy�lnym (ang. default konstructor), wi�kszo�� programist�w zapisuje to tak: class ZNAK { char znak_dany; public: ZNAK(char); ZNAK(); //Z daleka wida�, �e nic nie ma ! ... Definicja konstruktora bezparametrowego b�dzie wygl�da� tak: ZNAK::ZNAK() { znak_dany = 'X'; } W zale�no�ci od sposobu zadeklarowania obiektu w programie C++ wywo�a automatycznie albo konstruktor ZNAK(char), albo konstruktor domy�lny ZNAK(): ZNAK obiekt; //Nie sprecyzowano jaki, konstruktor domy�lny ZNAK obiekt('m'); //Wiadomo jaki, konstruktor jednoparametrowy Dzi�ki temu, �e C++ "pedantycznie" sprawdza przed wywo�aniem funkcji zgodno�� typ�w argument�w przekazywanych do funkcji (konstruktor to te� funkcja) i por�wnuje typ argument�w z �yczeniem programisty wyra�onym w prototypie - bezb��dnie rozpozna (mimo identycznej nazwy), kt�r� funkcj� nale�y zastosowa�. Dodajmy do klasy ZNAK deklaracje (prototypy) funkcji-metod: class ZNAK { char znak_dany; public: ZNAK(char); ZNAK(); void Pokaz_sie(); void Znikaj(); void Skacz(); }; i zdefiniujmy te metody. void ZNAK::Pokaz_sie(void) { cout << znak_dany << '\a'; } void ZNAK::Znikaj(void) { cout << "\b" << ' '; //'\b' == Back Space } void ZNAK::Skacz(void) { for(int i = 0; i < 100; i++) { gotoxy(rand()%50, rand()%50); cout << znak_dany; getch(); } } Je�li implementacj� klasy ZNAK umie�cimy w pliku nag��wkowym A:\ZNAK.H //_____________________________________________________________ # include <stdlib.h> # include <conio.h> # include <iostream.h> class ZNAK { char znak_dany; public: ZNAK(char); ZNAK(); void Pokaz_sie(); void Znikaj(); void Skacz(); }; ZNAK::ZNAK() { znak_dany = 'X'; } ZNAK::ZNAK(char x) { znak_dany = x; if(znak_dany < 65 && znak_dany >122) znak_dany = '*'; if(znak_dany > 97) znak_dany -= 32; } void ZNAK::Pokaz_sie(void) { cout << znak_dany << '\a'; } void ZNAK::Znikaj(void) { cout << "\b" << ' '; //'\b' == Back Space } void ZNAK::Skacz(void) { for(int i = 0; i < 100; i++) { gotoxy(rand()%50, rand()%50); cout << znak_dany; getch(); } } //_____________ koniec pliku A:\INCLUDE\ZNAK.H _________________ to nasz program mo�e wygl�da� tak: [P103.CPP] # include <a:\znak.h> void main() { char litera; clrscr(); cout << '\n' << "Podaj znak: "; cin >>...
ZAZZY