LEKCJA28.TXT

(10 KB) Pobierz
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 >>...
Zgłoś jeśli naruszono regulamin