Čo vieme o OOP
- trieda = štruktúra, ktorá obsahuje položky (stavové premenné) a metódy ... zapuzdrenie (enkapsulácia),
- objekty môžu byť statické alebo dynamické,
- konštruktory a deštruktory = metódy, ktoré sa automaticky zavolajú pri vytvorení alebo rušení objektu.
Jednoduché dedenie
Naučíme sa používať ďalšiu, veľmi dôležitú vlastnosť OOP - dedičnosť (inheritance) tried:
- dedičnosť umožňuje vytvorenie novej triedy z už existujúcej triedy,
- potom hovoríme, že sme odvodili novú objektovú triedu z nejakej základnej triedy,
- odvodená trieda (potomok, nasledovník) zdedí všetky položky aj metódy zo svojej základnej triedy (predka, rodiča),
- v odvodenej triede môže dodefinovať nové položky aj metódy,
- v odvodenej triede môže predefinovať správanie sa zdedených metód.
Ukážka:
class TA // základná trieda {
public:
int X; void Fnc(); };
class TB : public TA // trieda TB je odvodená od triedy TA {
public:
int Y; bool Test(); }
Trieda TB je odvodená od základnej triedy TA, a preto obsahuje všetky položky a metódy z triedy TA. Trieda TB má navyše aj jednu novú položku a metódu. Zhrňme si, čo trieda TB obsahuje:
- položka X ... je zdedená z triedy TA
- položka Y ... je nová položka triedy TB
- metóda Fnc() ... je zdedená z triedy TA
- metóda Test() ... je nová metóda TB.
Pri používaní objektu triedy TB môžeme:
TB b; b.X=1; b.Y=2; b.Fnc(); b.Test();
Pozor, objekt triedy TA nepozná ani položku Y, ani funkciu Test():
TA a; a.Y=2; ... chyba, položka neexistuje a.Test(); ... chyba, takáto metóda neexistuje
Hlavné výhody mechanizmu dedenia tried:
- objektové triedy môžeme "vylepšovať" tým, že z nich odvodíme nové triedy - ak nejaký programátor pripraví kvalitnú objektovú triedu, môžeme z nej odvodiť vlastné triedy,
- pri programovaní zdedených tried nemusíme mať k dispozícii zdrojový kód základnej triedy - na odvodenie novej triedy stačí poznať iba položky a metódy základnej triedy,
- vďaka dedičnosti môžeme využívať aj ďalšiu veľmi užitočnú vlastnosť OOP - polymorfizmus (mnohotvárnosť - o nej však nebudeme hovoriť).
Projekt Akvárium
Dedičnosť ukážeme na príklade projektu Akvárium:
- v akváriu žije niekoľko druhov tvorov: rybka, hadíky a potrava pre rybku,
- všetky živočíchy sa v akváriu pohybujú (použijeme časovač) a odrážajú od jeho okrajov,
- ak nájde ryba vo svojom okolí potravu, rybka ju zožerie a trochu vyrastie,
- potravu do budeme akvária "sypať ťahaním myšou.
Pri návrhu základnej triedy si všímame iba spoločné (všeobecné) vlastnosti a správanie sa objektov. Živočíchy majú niekoľko spoločných znakov:
- polohu a smer pohybu
- pri pohybe sa odrážajú od okrajov akvária.
Definujeme základnú triedu TTvor:
class TTvor {
public:
float X, Y, dX, dY; void Nastav(float nX, float nY, float ndX, float ndY); void Pohni(); };
void TTvor::Nastav(float nX, float nY, float ndX, float ndY) { X=nX; Y=nY; dX=ndX; dY=ndY; }
void TTvor::Pohni() { X=X+dX; Y=Y+dY; if (X400) dX=-dX; if (Y300) dY=-dY; }
Hierarchia tried
Vzťahy medzi základnou triedou TTvor a odvodenými triedami vyjadruje diagram:
TTvor
X, Y dX, dY Nastav Pohni
TPotrava THad TRyba Kresli
Tm Nastav Pohni Kresli
S Nastav Kresli JeBlizko Rast
Šípky v takomto diagrame sa kreslia od odvodenej triedy smerom k základnej lebo odvodená trieda "vidí" zdedené položky a metódy, ale naopak základná trieda "nevidí" do svojich potomkov - nemôže používať ich položky ani metódy. V reálnych projektoch často býva hierarchia tried oveľa komplikovanejšia a najmä, býva v nej viac "poschodí" (t.j. aj odvodené triedy môžu mať svojich potomkov).
Odvádzanie tried
Zo základnej triedy odvodíme tri nové triedy: TPotrava, THad a TRyba: class TPotrava : public TTvor {
public:
void Kresli(); };
void TPotrava::Kresli() { g->Pixels[X][Y]=clBlack; }
- trieda TPotrava zdedila všetky položky a metódy triedy TTvor, t.j.: X, Y, dX, dY, Nastav() a Pohni(),
- trieda TPotrava má zadefinovanú novú metódu Kresli().
Trieda THad má novú položku Tm, ktorá slúži pri vykresľovaní hadíka ako vlniacej sa sínusoidy. Ďalej potrebujeme:
- zmeniť inicializačnú metódu Nastav ... v nej vynulujeme premennú Tm,
- zmeniť metódu Pohni ... zmení čas Tm,
- dodefinovať metódu Kresli ... nakreslí funkciu sínus.
class THad : public TTvor {
private:
int Tm; // parameter pre kreslenie hadíka - sínusoidy
public:
void Nastav(float nX, float nY, float ndX, float ndY); void Pohni(); void Kresli(); };
void THad::Nastav(float nX, float nY, float ndX, float ndY) { TTvor::Nastav(nX, nY, ndX, ndY); Tm=0; }
void THad::Pohni() { TTvor::Pohni(); Tm=Tm+dX; }
void THad::Kresli() { g->Pen->Color=clRed; g->MoveTo(X-10, Y+2*sin(Tm/8.0-10/2.0)); for (int i=-10; iLineTo(X+i, Y+2*sin(Tm/8.0+i/2.0)); }
V metódach Nastav aj Posun využívame zdedené metódy zo základnej triedy TTvar. Aby kompilátor rozlíšil medzi volaním zdedenej metódy Nastav a rekurzívnym volaním metódy Nastav, uvádzame pri volaní zdedenej metódy aj názov základnej triedy: TTvor::Nastav(...).
Odvodená trieda TRyba bude mať:
- novú položku S, v ktorej sa uchováva informácia o veľkosti ryby,
- predefinovanú metódu Nastav - navyše umožní nastaviť aj veľkosť ryby,
- nové pomocné metódy: JeBlizko a Rast.
class TRyba : public TTvor {
private:
int S;
public:
void Nastav(float nX, float nY, float ndX, float ndY, int nS); void Kresli(); bool JeBlizko(TPotrava *P); // nachádza sa potrava v okolí ryby? void Rast(); };
void TRyba::Nastav(float nX, float nY, float ndX, float ndY, int nS) { TTvor::Nastav(nX, nY, ndX, ndY); S=nS; }
void TRyba::Kresli() { float r=sqrt(S); g->Pen->Color=clBlue; g->Brush->Color=clBlue; g->Ellipse(X-2*r, Y-r, X+2*r, Y+r); }
bool TRyba::JeBlizko(TPotrava *P) { float dx, dy; dx=P->X-X; dy=P->Y-Y; return dx*dx/(2*2)+dy*dyClientRect); r.Kresli(); for (i=0; i