Search
Ve cvičení se seznámíme s jednoduchými příklady dědění.
Příklad z minulého cvičení lze velmi pěkně zobecnit tak, že Kocka i Mys budou potomky jedné bázové třídy Zviratko. Tato třída má dva atributy, zdravi a kalorie. Zdraví bude potomkům, kteří volají konstruktor bázové třídy, vždy nastaveno na maximální hodnotu, tj. 100. Kalorie mohou být nastaveny parametrem konstruktoru, nebo lze využít defaultní hodnotu parametru konstruktoru bázové třídy.
Kocka
Mys
Zviratko
zdravi
kalorie
Kromě atributů a konstruktoru obsahuje bázová třída ještě metodu pro tisk informací o instanci. Tato metoda je pak využita v přetíženém proudovém operátoru. Protože je druhým argumentem operátoru reference na bázovou třídu, lze pak využít tento operátor v derivovaných třídách.
friend
info
std::stringstream
class Zviratko { int zdravi, kalorie; public: Zviratko (int in_kalorie = 1000): kalorie(in_kalorie), zdravi(100) {} std::string info () { std::stringstream ss; ss << "# zviratko | kalorie: " << setw(6) << this->kalorie << " | zdravi: " << setw(4) << this->zdravi; return ss.str(); } }; void operator<< (std::ostream & s, Zviratko & z) { s << z.info() << std::endl; }
Derivované třídy jsou pak definované následovně:
class Mys: public Zviratko { public: Mys (): Zviratko (100) {} }; class Kocka: public Zviratko { public: Kocka (): Zviratko () {} };
Kontrola inicializace objektů pak může být velmi jednoduchá:
Kocka k; Mys m; std::cout << "mys: " << m; std::cout << "kocka: " << k;
Bázová třída by mohla mít metodu, která zajistí interakci mezi instancemi derivovaných tříd.
void Zviratko::konzumace (Zviratko &z) { this->kalorie += z.kalorie; std::cout << "! nekdo nekoho sezral" << std:endl; }
Použití reference (nebo ukazatele) zajistí, že argumenty funkce mohou být instance bázové i derivovaných tříd. Interakce pak může probíhat napříč instancemi.
Použití metody bázové třídy může být v určitých situacích nedostatečné, např. pokud je třeba omezit interakci pouze na instance vybraných tříd. Můžeme se pokusit vytvořit variantní metody pro derivované třídy (ne nutně ve všech třídách, je možné, že některé instance využijí metodu definovanou v bázové třídě).
void Kocka::konzumace (Zviratko &z) { this->kalorie += z.kalorie; std::cout << "! kocka neco sezrala" << std::endl; }
Zde ale narazíme na dvě zásadní komplikace:
protected
public
// deklarace přátelské třídy class Mys; class Zviratko { // privatni atributy public: // verejne atributy a metody friend Mys; }; class Mys : public Zviratko { // definice třídy };
Po přepsání metody konzumace ve třídě Kocka se pak tyto metody chovají rozdílně podle toho, zda jsou volány v rámci instance třídy Kocka nebo Mys.
konzumace
Pokud má příklad modelovat reálný problém, je třeba se zamyslet nad tím, zda je možná interakce mezi instancemi téže třídy. Na rozdíl od C umožňuje C++ za běhu zjistit datový typ, např. pomocí operátoru typeid, deklarovaném v hlavičkovém souboru <typeinfo>. Variant, jak využít tento operátor, je celá řada, nabízí se třeba využití metody rodičovské třídy, kterou mohou (nebo nemusí) zavolat metody derivovaných tříd a případně ji doplnit nejakou vhodnou informací.
C
C++
typeid
<typeinfo>
void Zviratko::konzumace (Zviratko &z) { if (typeid(*this) == typeid(z)) { std::cout << "! kanibalismus mezi zviratky netolerujeme" << std::endl; return; } this->kalorie += z.kalorie; } void Kocka::konzumace (Zviratko &z) { Zviratko::konzumace (z); std::cout << "! kocka neco sezrala" << std::endl; }