Č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