Dedinos a spriatelen triedy Osnova prednky n n

  • Slides: 54
Download presentation
Dedičnosť a spriatelené triedy

Dedičnosť a spriatelené triedy

Osnova prednášky n n n Princípy a typy dedičnosti Konštruktory, deštruktory a dedičnosť Viacnásobná

Osnova prednášky n n n Princípy a typy dedičnosti Konštruktory, deštruktory a dedičnosť Viacnásobná dedičnosť Dedičnosť a smerníky Virtuálne funkcie Spriatelené triedy

Dedičnosť n n n V objektovo orientovanom programovaní môže mať každá trieda svojho potomka

Dedičnosť n n n V objektovo orientovanom programovaní môže mať každá trieda svojho potomka - odvodenú triedu, ktorá od nej preberie všetky dáta a metódy (okrem konštruktora a deštruktora). Odvodená trieda môže mať okrem zdedených dát a metód svoje vlastné , ktorými sa odlíši od svojho rodiča. Metódy, ktoré odvodená trieda zdedila, môže mať upravené podľa svojich potrieb. Programátor má možnosť ovplyvňovať prístup odvodenej triedy k dátam a metódam svojho rodiča. Teda aj keď odvodená trieda niečo zdedí, nemusí jej byť dovolené to používať. Každá trieda môže mať ľubovoľne veľa potomkov aj rodičov.

Chránené dáta a metódy n n Doteraz sme sa stretli len s dvomi typmi

Chránené dáta a metódy n n Doteraz sme sa stretli len s dvomi typmi dát a metód: verejnými (public) a súkromnými (private). Vďaka možnosti dedenia ale vzniká nová požiadavka – niektoré dáta a metódy by mali byť síce súkromné, ale odvodené triedy by k nim mali mať prístup. Takéto dáta a metódy sa nazývajú chránené (protected). Ak sa teda v programe bude vyskytovať aj dedičnosť, vo všeobecnosti bude štruktúra triedy vyzerať takto: class meno_triedy { private: . . . protected: . . . public: . . . };

Typy dedičnosti n n n Tak ako dáta a metódy, aj dedičnosť môže byť

Typy dedičnosti n n n Tak ako dáta a metódy, aj dedičnosť môže byť troch rôznych druhov: 1. Súkromná (private) 2. Chránená (protected) 3. Verejná (public) Súkromná dedičnosť znamená, že všetky dáta a metódy zdedené od rodičovskej triedy budú v odvodenej triede súkromné. Chránená dedičnosť znamená, že verejné a chránené dáta a metódy rodičovskej triedy budú v odvodenej triede chránené. Súkromné dáta a metódy zostávajú súkromnými. Verejná dedičnosť znamená, že prístupnosť dát a metód sa presne zachováva – verejné budú verejnými, chránené chránenými a súkromné súkromnými. Štandardná dedičnosť v C++ je súkromná. Ak si programátor praje iný typ, musí to explicitne zadať.

Typy dedičnosti n Prehľad typov dedičnosti v C++: DEDIČNOSŤ SÚKROMNÉ ÚDAJE CHRÁNENÉ ÚDAJE VEREJNÉ

Typy dedičnosti n Prehľad typov dedičnosti v C++: DEDIČNOSŤ SÚKROMNÉ ÚDAJE CHRÁNENÉ ÚDAJE VEREJNÉ ÚDAJE SÚKROMNÁ SÚKROMNÉ CHRÁNENÁ SÚKROMNÉ CHRÁNENÉ VEREJNÁ SÚKROMNÉ CHRÁNENÉ VEREJNÉ

Dedičnosť n n Vytvorenie odvodenej triedy: class odvodená_trieda: typ_dedičnosti rodičovská_trieda {. . . };

Dedičnosť n n Vytvorenie odvodenej triedy: class odvodená_trieda: typ_dedičnosti rodičovská_trieda {. . . }; Príklad: Rodič a dieťa Vytvoríme dve triedy – Rodič a Dieťa, pričom Dieťa bude potomkom triedy Rodič bude mať nasledujúce údaje: meno, priezvisko, bydlisko, číslo účtu a stav účtu. Dieťa bude mať všetky uvedené údaje a navyše ešte meno a priezvisko rodiča. Na tomto príklade si vyskúšame rôznu prístupnosť dát a rôzne typy dedičnosti.

Dedičnosť n Trieda Rodič: class Rodic { private: int stav_uctu; protected: string meno, priezvisko,

Dedičnosť n Trieda Rodič: class Rodic { private: int stav_uctu; protected: string meno, priezvisko, bydlisko; string cislo_uctu; public: Rodic() {} Rodic(string m, string p, string b, string cu, int su): meno(m), priezvisko(p), bydlisko(b), cislo_uctu(cu), stav_uctu(su) {} string Meno() {return meno; } string Priezvisko() {return priezvisko; } string Bydlisko() {return bydlisko; } string Cislo. Uctu() {return cislo_uctu; } int Stav. Uctu() {return stav_uctu; } };

Dedičnosť n n Trieda Dieťa: class Dieta: private Rodic { private: string meno_rodica, priezvisko_rodica;

Dedičnosť n n Trieda Dieťa: class Dieta: private Rodic { private: string meno_rodica, priezvisko_rodica; public: Dieta(string m, string p, string b, string c_u, string m_r, string p_r); string Meno. Rodica() {return meno_rodica; } string Priezvisko. Rodica() {return priezvisko_rodica; } void Predstav. Sa(); }; V tomto prípade je dedičnosť typu private, to znamená, že všetko, čo Dieťa zdedí od triedy Rodič, bude súkromné. To znamená, že mimo triedy (napr. v hlavnom programe), nemá Dieťa prístupné žiadne dáta, ale nemôže použiť ani metódy Meno(), Priezvisko(), Bydlisko(), ČísloÚčtu() a StavÚčtu(). Pre svoje súkromné účely však môže všetky tieto metódy použiť. Pridáme teda do triedy Dieťa metódu Predstav. Sa(), pomocou ktorej Dieťa oznámi svoje meno, priezvisko a bydlisko. Číslo účtu a ani jeho stav nebude môcť dieťa oznamovať. Navyše, stav_účtu nie je pre Dieťa vôbec prístupný, keďže je to súkromný údaj triedy Rodič. Dieťa teda nemôže s ním nijako narábať.

Dedičnosť n Definujeme metódy triedy Dieťa: Dieta: : Dieta(string m, string p, string b,

Dedičnosť n Definujeme metódy triedy Dieťa: Dieta: : Dieta(string m, string p, string b, string c_u, string m_r, string p_r) { meno=m; priezvisko=p; bydlisko=b; cislo_uctu=c_u; meno_rodica=m_r; priezvisko_rodica=p_r; } void Dieta: : Predstav. Sa() { cout<<"Volam sa "<<meno<<" "<<priezvisko<<endl; cout<<"Moje bydlisko je "<<bydlisko<<endl; } Všimnime si, že stav_účtu nie je v konštruktore vôbec inicializovaný. Dieťa nemá v tomto prípade oprávnenie ho akokoľvek používať.

Dedičnosť n Hlavný program: Rodic Otec("Pavol", "Horvath", "Piestany", "012011/0100", 150000); Dieta Syn("Misko", "Horvath", "Piestany",

Dedičnosť n Hlavný program: Rodic Otec("Pavol", "Horvath", "Piestany", "012011/0100", 150000); Dieta Syn("Misko", "Horvath", "Piestany", "012011/0100", "Pavol", "Horvath"); cout<<"Volam sa "<<Otec. Meno()<<" "<<Otec. Priezvisko()<<endl; cout<<"Moje bydlisko je "<<Otec. Bydlisko()<<endl; cout<<"Moje cislo uctu je "<<Otec. Cislo. Uctu()<<endl; cout<<"Na ucte mam "<<Otec. Stav. Uctu()<<" Sk"<<endl; Syn. Predstav. Sa(); cout<<"Moj otec je "<<Syn. Meno. Rodica()<<" "<<Syn. Priezvisko. Rodica()<<endl; Výstup: Volam sa Pavol Horvath Moje bydlisko je Piestany Moje cislo uctu je 012011/0100 Na ucte mam 150000 Sk Volam sa Misko Horvath Moje bydlisko je Piestany Moj otec je Pavol Horvath

Dedičnosť n n Teraz na ten istý prípad použijeme dedičnosť typu public. To znamená,

Dedičnosť n n Teraz na ten istý prípad použijeme dedičnosť typu public. To znamená, že Dieťa nebude potrebovať metódu Predstav_sa(), pretože bude môcť oznámiť údaje v hlavnom programe rovnako Rodič. Súkromné dáta triedy Rodič, teda stav_účtu, zostanú však preň naďalej nedostupné. Môže síce použiť metódu StavÚčtu(), nemá to však zmysel, keďže nemôže túto hodnotu inicializovať ani s ňou inak narábať. Trieda Dieťa bude teda vyzerať takto: class Dieta: public Rodic { private: string meno_rodica, priezvisko_rodica; public: Dieta(string m, string p, string b, string c_u, string m_r, string p_r); string Meno. Rodica() {return meno_rodica; } string Priezvisko. Rodica() {return priezvisko_rodica; } };

Dedičnosť n Hlavný program: Rodic Otec("Pavol", "Horvath", "Piestany", "012011/0100", 150000); Dieta Syn("Misko", "Horvath", "Piestany",

Dedičnosť n Hlavný program: Rodic Otec("Pavol", "Horvath", "Piestany", "012011/0100", 150000); Dieta Syn("Misko", "Horvath", "Piestany", "012011/0100", "Pavol", "Horvath"); cout<<"Volam sa "<<Otec. Meno()<<" "<<Otec. Priezvisko()<<endl; cout<<"Moje bydlisko je "<<Otec. Bydlisko()<<endl; cout<<"Moje cislo uctu je "<<Otec. Cislo. Uctu()<<endl; cout<<"Na ucte mam "<<Otec. Stav. Uctu()<<" Sk"<<endl; cout<<"Volam sa "<<Syn. Meno()<<" "<<Syn. Priezvisko()<<endl; cout<<"Moj otec je "<<Syn. Meno. Rodica()<<" "<<Syn. Priezvisko. Rodica()<<endl; cout<<"Moj otec ma cislo uctu "<<Syn. Cislo. Uctu()<<endl; cout<<"Stav jeho uctu je "<<Syn. Stav. Uctu()<<endl;

Konštruktor a dedičnosť n n n V predchádzajúcom príklade obsahovala trieda Rodič okrem konštruktora

Konštruktor a dedičnosť n n n V predchádzajúcom príklade obsahovala trieda Rodič okrem konštruktora s parametrami aj prázdny konštruktor. Malo to svoje opodstatnenie, ktoré si teraz objasníme. Stačí si uvedomiť, aký je mechanizmus vytvárania odvodenej triedy. Keďže odvodená trieda má svojho rodiča, najprv sa zavolá koštruktor rodiča a až potom konštruktor potomka. Keďže rodičovská trieda môže mať niekoľko konštruktorov, musíme povedať, ktorý z nich sa má zavolať. Ak priamo neuvedieme, ktorý konštruktor chceme, automaticky sa zavolá prázdny konštruktor, ktorý musí byť k dispozícii. Ak do každého konštruktora pridáme výpis informácie, o ktorý konštruktor ide, dostaneme takýto výstup:

Konštruktor a dedičnosť n n n Ako sme už naznačili, v konštruktore potomka môžeme

Konštruktor a dedičnosť n n n Ako sme už naznačili, v konštruktore potomka môžeme využiť ľubovoľný konštruktor rodiča. Príklad: Konštruktor triedy Dieťa s využitím konštruktora triedy Rodič Keďže Dieťa má niekoľko parametrov prebratých od svojej rodičovskej triedy, nemusíme ich priamo inicializovať v jeho konštruktore. Môžeme použiť takýto zápis: Dieta: : Dieta(string m, string p, string b, string c_u, string s_u, string m_r, string p_r): Rodic(m, p, b, c_u, s_u) { cout<<"konstruktor dietata"<<endl; meno_rodica= m_r; priezvisko_rodica=p_r; } Týmto spôsobom sa nastavia všetky údaje, ktoré má aj Rodič. Zostáva nastaviť už len zvyšné údaje. Všimnime si, že týmto spôsobom sa podarí nastaviť dokonca aj údaj stav_účtu, keďže jeho inicializácia prebieha v tele rodičovskej triedy.

Konštruktor a dedičnosť n V tomto prípade dostaneme takýto výstup (pri použití dedičnosti typu

Konštruktor a dedičnosť n V tomto prípade dostaneme takýto výstup (pri použití dedičnosti typu public):

Deštruktor a dedičnosť n n Kým pri vytváraní objektu odvodenej triedy sa najskôr zavolá

Deštruktor a dedičnosť n n Kým pri vytváraní objektu odvodenej triedy sa najskôr zavolá konštruktor rodičovskej triedy a až potom konštruktor podtriedy, pri zániku objektu je postup presne opačný – najskôr sa zavolá deštruktor potomka a až potom deštruktor rodičovskej triedy. Schéma vytvárania a zániku objektu odvodenej triedy je teda takáto: konštruktor rodičovskej triedy konštruktor odvodenej triedy deštruktor rodičovskej triedy

Deštruktor a dedičnosť n Príklad: Triedy Rodič a Dieťa s deštruktormi Do tried Rodič

Deštruktor a dedičnosť n Príklad: Triedy Rodič a Dieťa s deštruktormi Do tried Rodič a Dieťa doplníme jednoduché deštruktory, ktoré len oznámia, že objekt zaniká: ~Rodic() {cout<<"Zanika objekt triedy Rodic"<<endl; } ~Dieta() {cout<<"Zanika objekt triedy Dieta"<<endl; } Dostaneme výstup:

Viacnásobná dedičnosť n Rozlišujeme dva typy viacnásobnej dedičnosti: 1. Viacúrovňová dedičnosť – odvodená trieda

Viacnásobná dedičnosť n Rozlišujeme dva typy viacnásobnej dedičnosti: 1. Viacúrovňová dedičnosť – odvodená trieda sa stane rodičovskou triedou pre ďalšiu odvodenú triedu 2. Dedičnosť od viacerých rodičov – odvodená trieda dedí naraz od dvoch alebo viacerých tried Samozrejme tieto typy dedičnosti môžu byť rôzne kombinované

Viacúrovňová dedičnosť n n V tomto prípade sa odvodená trieda stane rodičom ďalšej triedy,

Viacúrovňová dedičnosť n n V tomto prípade sa odvodená trieda stane rodičom ďalšej triedy, pričom postup sa môže opakovať. Konštruktory sa spúšťajú od triedy, ktorá je v hierarchii najvyššie, po tú, ktorá je najnižšie. Deštruktory sa spúšťajú v opačnom poradí. Príklad: Supermarket Vytvoríme program, ktorý na základe kódu vyhľadá príslušný tovar a podá o ňom informácie. Budeme uvažovať triedy: 1. Trieda Tovar – bude obsahovať kód tovaru, názov a cenu 2. Trieda Potravina – bude potomkom triedy Tovar, bude navyše obsahovať dátum spotreby 3. Trieda Zelenina – bude potomkom triedy Potravina a navyše bude obsahovať údaj o triede akosti Aby sme mohli sledovať priebeh vytvárania a zániku objektov, vytvoríme v týchto triedach aj deštruktory, ktorých úlohou bude len informovať o tom, ktorý deštruktor sa práve spúšťa.

Viacúrovňová dedičnosť n }; Naskôr definujeme triedu Tovar: class Tovar { protected: int kod;

Viacúrovňová dedičnosť n }; Naskôr definujeme triedu Tovar: class Tovar { protected: int kod; string nazov; float cena; public: Tovar(int k, string n, float c): kod(k), nazov(n), cena(c) {cout<<"Vytvara sa objekt triedy Tovar"<<endl; } ~Tovar() {cout<<"Zanika objekt triedy Tovar"<<endl; } int Kod() {return kod; } string Nazov() {return nazov; } float Cena() {return cena; }

Viacúrovňová dedičnosť n Trieda Potravina: class Potravina: public Tovar { protected: string datum_spotreby; public:

Viacúrovňová dedičnosť n Trieda Potravina: class Potravina: public Tovar { protected: string datum_spotreby; public: Potravina(int k, string n, float c, string ds): Tovar(k, n, c), datum_spotreby(ds) {cout<<"Vytvara sa objekt triedy Potravina"<<endl; } ~Potravina() {cout<<"Zanika objekt triedy Potravina"<<endl; } string Datum. Spotreby() {return datum_spotreby; } };

Viacúrovňová dedičnosť n Trieda Zelenina: class Zelenina: public Potravina { private: int trieda; public:

Viacúrovňová dedičnosť n Trieda Zelenina: class Zelenina: public Potravina { private: int trieda; public: Zelenina(int k, string n, float c, string ds, int t): Potravina(k, n, c, ds), trieda(t) {cout<<"Vytvara sa objekt triedy Zelenina"<<endl; } ~Zelenina() {cout<<"Zanika objekt triedy Zelenina"<<endl; } int Trieda() {return trieda; } };

Viacúrovňová dedičnosť n Hlavný program: int main() { Zelenina z[3]={Zelenina(123, "Paprika", 45, "12. 9.

Viacúrovňová dedičnosť n Hlavný program: int main() { Zelenina z[3]={Zelenina(123, "Paprika", 45, "12. 9. 2007", 1), Zelenina(540, "Uhorka", 17, "15. 9. 2007", 2), Zelenina(673, "Kapusta", 34, "22. 9. 2007", 1)}; int kod; cout<<"Zadaj kod: "<<endl; cin>>kod; for (int i=0; i<3; i++) if (z[i]. Kod()==kod) break; if (i==3) cout<<"Tovar s takymto kodom nemame na sklade"<<endl; else { cout<<z[i]. Nazov()<<endl<<z[i]. Cena()<<" Sk/kg"<<endl; cout<<"Datum spotreby: "<<z[i]. Datum. Spotreby()<<endl; cout<<"Trieda: "<<z[i]. Trieda()<<endl; } return 0; }

Viacúrovňová dedičnosť n Výstup:

Viacúrovňová dedičnosť n Výstup:

Dedičnosť od viacerých rodičov n n V tomto prípade trieda dedí dáta a metódy

Dedičnosť od viacerých rodičov n n V tomto prípade trieda dedí dáta a metódy od viacerých tried naraz. Syntax: class odvodená_trieda: typ_dedičnosti 1 trieda 1, typ_dedičnosti 2 trieda 2. . . {. . . } Konštruktory sa v tomto prípade spúšťajú v poradí zľava doprava, deštruktory sprava doľava. Príklad: Spiderman Vytvoríme triedy: Človek – s údajmi meno, vek, zamestnanie Zviera – s údajmi druh, počet končatín a informáciou o schopnostiach (lietanie, plávanie, lezenie po stenách) Spiderman – potomok tried Človek a Zviera, navyše s informáciou, či bol hrdinom filmu

Dedičnosť od viacerých rodičov n Trieda Človek: class Clovek { protected: string meno; int

Dedičnosť od viacerých rodičov n Trieda Človek: class Clovek { protected: string meno; int vek; string zamestnanie; public: Clovek(string m, int v, string z): meno(m), vek(v), zamestnanie(z) {cout<<"Vytvara sa novy Clovek"<<endl; } ~Clovek() {cout<<"Zanika Clovek"<<endl; } string Meno() {return meno; } int Vek() {return vek; } string Zamestnanie() {return zamestnanie; } };

Dedičnosť od viacerých rodičov n Trieda Zviera: class Zviera { protected: string druh; int

Dedičnosť od viacerých rodičov n Trieda Zviera: class Zviera { protected: string druh; int pocet_koncatin; bool lietanie, plavanie, lezenie; public: Zviera(string d, int pk, bool li, bool p, bool le): druh(d), pocet_koncatin(pk), lietanie(li), plavanie(p), lezenie(le) {cout<<"Vytvara sa nove Zviera"<<endl; } ~Zviera() {cout<<"Zanika Zviera"<<endl; } string Druh() {return druh; } int Pocet. Koncatin() {return pocet_koncatin; } void Vymenuj. Schopnosti(); };

Dedičnosť od viacerých rodičov n n Definujeme metódu Vymenuj. Schopnosti(): void Zviera: : Vymenuj.

Dedičnosť od viacerých rodičov n n Definujeme metódu Vymenuj. Schopnosti(): void Zviera: : Vymenuj. Schopnosti() { if (lietanie) cout<<"Viem lietat"<<endl; if (plavanie) cout<<"Viem plavat"<<endl; if (lezenie) cout<<"Viem liezt po stenach"<<endl; } Trieda Spiderman: class Spiderman: public Clovek, public Zviera { private: bool film; public: Spiderman(string m, int v, string z, string d, int pk, bool li, bool p, bool le, bool f): Clovek(m, v, z), Zviera(d, pk, li, p, le), film(f) {cout<<"Vznika novy druh Spiderman"<<endl; } ~Spiderman() {cout<<"Spiderman zanika"<<endl; } void Film() {if (film) cout<<"Bol som hrdinom filmu!"<<endl; } };

Dedičnosť od viacerých rodičov int main() { Spiderman SM("Peter Parker", 30, "novinar", "pavuk", 4,

Dedičnosť od viacerých rodičov int main() { Spiderman SM("Peter Parker", 30, "novinar", "pavuk", 4, false, true); cout<<"Volam sa "<<SM. Meno()<<endl; cout<<"Mam "<<SM. Vek()<<" rokov"<<endl; cout<<"Zivim sa ako "<<SM. Zamestnanie()<<endl; cout<<"Zaroven som "<<SM. Druh()<<endl; cout<<"Pocet mojich koncatin je "<<SM. Pocet. Koncatin()<<endl; SM. Vymenuj. Schopnosti(); SM. Film(); return 0; }

Dedičnosť od viacerých rodičov n Výstup:

Dedičnosť od viacerých rodičov n Výstup:

Dedičnosť od viacerých rodičov n n Nie je povolené, aby mali obidve rodičovské funkcie

Dedičnosť od viacerých rodičov n n Nie je povolené, aby mali obidve rodičovské funkcie parameter s rovnakým menom alebo funkciu s rovnakým menom! Príklad: Trieda Zviera s údajom vek Do triedy Zviera doplníme údaj vek spolu s metódou Vek(): class Zviera { protected: . . . int vek; public: . . . int Vek() {return vek; } };

Zhrnutie n n n Každá trieda môže mať ľubovoľne veľa potomkov aj rodičovských tried

Zhrnutie n n n Každá trieda môže mať ľubovoľne veľa potomkov aj rodičovských tried Odvodená trieda dedí od svojho rodiča všetko okrem konštruktora a deštruktora Odvodená trieda môže mať vlastné dáta a metódy, ktorými sa líši od rodičovskej triedy. Obsah metód, ktoré sú zdedené, sa môže tiež upraviť podľa potrieb odvodenej triedy. Pri vytváraní objektu odvodenej triedy sa najprv spustí konštruktor rodičovskej triedy, potom konštruktor odvodenej triedy. Pri deštruktoroch je postup opačný. Viacnásobná dedičnosť môže byť dvoch typov: viacúrovňová dedičnosť a dedičnosť od viacerých rodičov

Dedičnosť a smerníky n n V súvislosti s dedičnosťou prichádza na scénu doteraz nevyužitá

Dedičnosť a smerníky n n V súvislosti s dedičnosťou prichádza na scénu doteraz nevyužitá vlastnosť smerníkov na objekty – ak je smerník deklarovaný ako smerník na objekt rodičovskej triedy, môže byť použitý aj tak, aby ukazoval na objekt odvodenej triedy. Obmedzením v tomto prípade je, že cez takýto smerník môžeme pristupovať len k takým dátam a metódam, ktoré mala aj rodičovská trieda. Príklad: Smerník na objekt triedy Tovar Použijeme smerník na objekt triedy Tovar na prístup k objektu triedy Zelenina: Zelenina z[3]={Zelenina(123, "Paprika", 45, "12. 9. 2007", 1), Zelenina(540, "Uhorka", 17, "15. 9. 2007", 2), Zelenina(673, "Kapusta", 34, "22. 9. 2007", 1)}; Tovar *smernik=z; cout<<"Smernik ukazuje na tuto zeleninu: "<<endl; cout<<smernik->Nazov()<<endl<<smernik->Cena()<<" Sk/kg"<<endl;

Dedičnosť a smerníky n n Výstup: Smernik ukazuje na tuto zeleninu: Paprika 45 Sk/kg

Dedičnosť a smerníky n n Výstup: Smernik ukazuje na tuto zeleninu: Paprika 45 Sk/kg Ak by sme však chceli pristupovať k iným údajom ako názov a cena, kompilátor by vyhlásil chybu: Tovar *smernik=z; cout<<"Smernik ukazuje na tuto zeleninu: "<<endl; cout<<smernik->Nazov()<<endl<<smernik->Cena()<<" Sk/kg"<<endl; cout<<"Datum spotreby: "<<smernik->Datum. Spotreby()<<endl;

Virtuálne funkcie n n Virtuálna funkcia je funkcia, ktorá je deklarovaná v rámci rodičovskej

Virtuálne funkcie n n Virtuálna funkcia je funkcia, ktorá je deklarovaná v rámci rodičovskej triedy a je predefinovaná odvodenou triedou. V odvodenej triede môže mať teda funkcia úplne iný obsah. Platí pravidlo, že predefinovaná funkcia musí mať rovnaký počet aj typ parametrov aj rovnakú návratovú hodnotu ako funkcia rodičovskej triedy. Trieda Mama virtuálna metóda Metóda Upeč_koláč: 4 vajíčka 300 g múky 2 dl oleja 200 g cukru prášok do pečiva ovocie Trieda Dcéra Metóda Upeč_koláč: 4 vajíčka 300 g múky 100 g masla 2 dl mlieka 200 g cukru prášok do pečiva ovocie

Virtuálne funkcie n n n Ak chceme funkciu označiť ako virtuálnu, dosiahneme to vložením

Virtuálne funkcie n n n Ak chceme funkciu označiť ako virtuálnu, dosiahneme to vložením kľúčového slova virtual pred deklaráciu funkcie. Toto označenie je potrebné len v rodičovskej triede. Pri definovaní funkcie (mimo tela triedy) už kľúčové slovo virtual neuvádzame! Predefinovanie virtuálnej triedy odvodenou triedou sa inak nazýva aj prehodnotenie (overriding). Virtuálne funkcie sú vlastne ďalšou formou polymorfizmu, keďže funkcia s jedným menom môže pre každú triedu vykonávať rôzne inštrukcie. Príklad: Supermarket s výpisom informácií o tovare Do tried Tovar, Potravina a Zelenina doplníme funkciu Info, ktorá vypíše informácie o danom tovare. Vypísané informácie sa budú pre každú triedu líšiť. V triede Tovar a Potravina bude funkcia deklarovaná ako virtuálna, v triede Zelenina to už tak nemusí byť.

Virtuálne funkcie n Trieda Tovar: class Tovar { protected: int kod; string nazov; float

Virtuálne funkcie n Trieda Tovar: class Tovar { protected: int kod; string nazov; float cena; public: Tovar(int k, string n, float c): kod(k), nazov(n), cena(c) {cout<<"Vytvara sa objekt triedy Tovar"<<endl; } ~Tovar() {cout<<"Zanika objekt triedy Tovar"<<endl; } int Kod() {return kod; } string Nazov() {return nazov; } float Cena() {return cena; } virtual void Info(); };

Virtuálne funkcie n Trieda Potravina: class Potravina: public Tovar { protected: string datum_spotreby; public:

Virtuálne funkcie n Trieda Potravina: class Potravina: public Tovar { protected: string datum_spotreby; public: Potravina(int k, string n, float c, string ds): Tovar(k, n, c), datum_spotreby(ds) {cout<<"Vytvara sa objekt triedy Potravina"<<endl; } ~Potravina() {cout<<"Zanika objekt triedy Potravina"<<endl; } string Datum. Spotreby() {return datum_spotreby; } virtual void Info(); };

Virtuálne funkcie n Trieda Zelenina: class Zelenina: public Potravina { private: int trieda; public:

Virtuálne funkcie n Trieda Zelenina: class Zelenina: public Potravina { private: int trieda; public: Zelenina(int k, string n, float c, string ds, int t): Potravina(k, n, c, ds), trieda(t) {cout<<"Vytvara sa objekt triedy Zelenina"<<endl; } ~Zelenina() {cout<<"Zanika objekt triedy Zelenina"<<endl; } int Trieda() {return trieda; } void Info(); };

Virtuálne funkcie n Definícia funkcie Info() pre každú triedu: void Tovar: : Info() {

Virtuálne funkcie n Definícia funkcie Info() pre každú triedu: void Tovar: : Info() { cout<<"Tovar s kodom "<<kod<<endl; cout<<nazov<<endl<<cena<<" Sk"<<endl; } void Potravina: : Info() { cout<<"Tovar s kodom "<<kod<<endl; cout<<nazov<<endl<<cena<<" Sk"<<endl; cout<<"Datum spotreby: "<<datum_spotreby<<endl; } void Zelenina: : Info() { cout<<"Tovar s kodom "<<kod<<endl; cout<<nazov<<endl<<cena<<" Sk/kg"<<endl; cout<<"Datum spotreby: "<<datum_spotreby<<endl; cout<<"Trieda: "<<trieda<<endl; }

Virtuálne funkcie n Tovar t(309, "Chladnicka", 15000); Potravina p(598, "Udeny losos", 135, "19. 9.

Virtuálne funkcie n Tovar t(309, "Chladnicka", 15000); Potravina p(598, "Udeny losos", 135, "19. 9. 2007"); Zelenina z(439, "Cibula", 20, "16. 9. 2007", 2); t. Info(); p. Info(); z. Info();

Virtuálne funkcie n Funkcie Info() pre každú triedu môžeme prepísať aj tak, že využijeme

Virtuálne funkcie n Funkcie Info() pre každú triedu môžeme prepísať aj tak, že využijeme príslušnú funkciu rodičovskej triedy: void Tovar: : Info() { cout<<"Tovar s kodom "<<kod<<endl; cout<<nazov<<endl<<cena<<" Sk"<<endl; } void Potravina: : Info() { Tovar: : Info(); cout<<"Datum spotreby: "<<datum_spotreby<<endl; } void Zelenina: : Info() { Potravina: : Info(); cout<<"Trieda: "<<trieda<<endl; }

Virtuálne funkcie a smerníky n n n Predchádzajúci príklad by bol fungoval, aj keby

Virtuálne funkcie a smerníky n n n Predchádzajúci príklad by bol fungoval, aj keby funkcia Info() nebola označená ako virtuálna. Skutočný význam virtuálnych funkcií sa ukáže napr. pri použití smerníkov. Už sme si hovorili, že smerník na rodičovskú triedu môže v skutočnosti ukazovať aj na odvodenú triedu. Ak však chceme zavolať metódu, ktorá je v odvodenej triede predefinovaná, musí byť táto metóda v rodičovskej triede označená ako virtuálna, aby program vedel správne rozoznať, ktorú verziu má použiť. Majme napr. smerník na triedu Tovar, ktorý bude ukazovať na objekt triedy Zelenina: Zelenina z(439, "Cibula", 20, "16. 9. 2007", 2); Tovar *smernik=&z; Ak chceme zavolať funkciu Info(), t. j. smernik->Info(); program má na výber funkciu Info triedy Tovar a triedy Zelenina. Správnu verziu (teda funkciu triedy Zelenina) zavolá vďaka tomu, že je funkcia Info v triede Tovar označená ako virtuálna.

Virtuálne funkcie a smerníky n Príklad: Supermarket s poľom smerníkov Vytvoríme pole smerníkov na

Virtuálne funkcie a smerníky n Príklad: Supermarket s poľom smerníkov Vytvoríme pole smerníkov na objekt typu Tovar, pričom ale smerníky budú môcť ukazovať aj na objekty typu Potravina a Zelenina. Potom si dáme vypísať informácie o všetkých prvkoch poľa. Tovar *t[3]= {new Tovar(309, "Chladnicka", 15000), new Potravina(598, "Udeny losos", 135, "19. 9. 2007"), new Zelenina(439, "Cibula", 20, "16. 9. 2007", 2)}; for (int i=0; i<3; i++) t[i]->Info(); Ak by funkcia Info() nebola deklarovaná ako virtuálna, zavolala by sa vždy funkcia patriaca triede Tovar. Dostali by sme nasledujúci výstup:

Virtuálne funkcie a smerníky n Ak však máme funkciu Info() deklarovanú ako virtuálnu (minimálne

Virtuálne funkcie a smerníky n Ak však máme funkciu Info() deklarovanú ako virtuálnu (minimálne v rodičovskej triede Tovar), program je schopný rozoznať, ktorú funkciu Info() má zavolať. Dostaneme teda výstup:

Virtuálny deštruktor n n Zatiaľ čo konštruktor nemôže byť virtuálny, deštruktor môže. V niektorých

Virtuálny deštruktor n n Zatiaľ čo konštruktor nemôže byť virtuálny, deštruktor môže. V niektorých prípadoch je to dokonca žiadúce. Príklad: Supermarket s virtuálnymi deštruktormi Ak si v predchádzajúcom príklade dáme vypísať informácie o konštruktoroch a deštruktoroch, dostaneme nasledujúci výstup: Vidíme, že sa pre objekty všetkých tried zavolal len deštruktor triedy Tovar.

Virtuálny deštruktor n Ak chceme, aby sa zavolali správne deštruktory, musíme deštruktor v rodičovskej

Virtuálny deštruktor n Ak chceme, aby sa zavolali správne deštruktory, musíme deštruktor v rodičovskej triede Tovar označiť ako virtuálny: class Tovar { protected: int kod; string nazov; float cena; public: Tovar(int k, string n, float c): kod(k), nazov(n), cena(c) {cout<<"Vytvara sa objekt triedy Tovar"<<endl; } virtual ~Tovar() {cout<<"Zanika objekt triedy Tovar"<<endl; } int Kod() {return kod; } string Nazov() {return nazov; } float Cena() {return cena; } virtual void Info(); };

Virtuálny deštruktor n V tomto prípade sa už zavolajú správne deštruktory

Virtuálny deštruktor n V tomto prípade sa už zavolajú správne deštruktory

Zhrnutie n n n Smerník na objekt rodičovskej triedy môže byť použitý aj tak,

Zhrnutie n n n Smerník na objekt rodičovskej triedy môže byť použitý aj tak, aby ukazoval na objekt odvodenej triedy. Prístupné sú však potom len dáta a metódy, ktoré mala aj rodičovská trieda. Virtuálna funkcia je funkcia, ktorá existuje už v rodičovskej triede a v odvodenej triede môže byť jej obsah predefinovaný. Takéto predefinovanie sa nazýva aj prehodnotenie alebo overriding. Prehodnotená funkcia odvodenej triedy musí mať rovnaký počet aj typ parametrov a rovnakú návratovú hodnotu ako funkcia rodičovskej triedy Deštruktor môže byť virtuálny, konštruktor nie Virtuálne funkcie sa uplatňujú najmä pri využití spoločných smerníkov na objekt rodičovskej a odvodenej triedy

Spriatelené triedy n n Ak máme v triede súkromné dáta a chceme, aby iná

Spriatelené triedy n n Ak máme v triede súkromné dáta a chceme, aby iná trieda mala k nim prístup, môžeme to zabezpečiť tým, že ju označíme za spriatelenú (friend). Príklad: Trieda Dieťa ako spriatelená trieda triedy Rodič Do triedy Dieťa chceme doplniť funkciu Vyber. ZÚčtu, ktorá zmenší stav účtu o zadanú čiastku. Aby trieda Dieťa mohla pristupovať k údaju stav_účtu, musí byť v triede Rodič označená ako spriatelená: class Rodic { friend class Dieta; private: int stav_uctu; protected: string meno, priezvisko, bydlisko; string cislo_uctu; public: . . . };

Spriatelené triedy n Trieda Dieťa bude obohatená o novú metódu: class Dieta: public Rodic

Spriatelené triedy n Trieda Dieťa bude obohatená o novú metódu: class Dieta: public Rodic { private: string meno_rodica, priezvisko_rodica; public: Dieta(string m, string p, Rodic r); ~Dieta() {cout<<"Zanika objekt triedy Dieta"<<endl; } string Meno. Rodica() {return meno_rodica; } string Priezvisko. Rodica() {return priezvisko_rodica; } void Predstav. Sa(); void Vyber. ZUctu(int suma) {stav_uctu-=suma; } };

Spriatelené triedy n Hlavný program: Rodic Otec("Pavol", "Horvath", "Piestany", "012011/0100", 150000); Dieta Syn("Misko", "Horvath",

Spriatelené triedy n Hlavný program: Rodic Otec("Pavol", "Horvath", "Piestany", "012011/0100", 150000); Dieta Syn("Misko", "Horvath", Otec); cout<<"Volam sa "<<Otec. Meno()<<" "<<Otec. Priezvisko()<<endl; cout<<"Moje bydlisko je "<<Otec. Bydlisko()<<endl; cout<<"Moje cislo uctu je "<<Otec. Cislo. Uctu()<<endl; cout<<"Na ucte mam "<<Otec. Stav. Uctu()<<" Sk"<<endl; cout<<"Volam sa "<<Syn. Meno()<<" "<<Syn. Priezvisko()<<endl; cout<<"Moj otec je "<<Syn. Meno. Rodica()<<" "<<Syn. Priezvisko. Rodica()<<endl; cout<<"Moj otec ma cislo uctu "<<Syn. Cislo. Uctu()<<endl; cout<<"Stav jeho uctu je "<<Syn. Stav. Uctu()<<" Sk"<<endl; Syn. Vyber. ZUctu(130000); cout<<"Po mojom vybere mu na ucte ostalo "<<Syn. Stav. Uctu()<<" Sk"<<endl;

Spriatelené triedy n Dostaneme výstup: Volam sa Pavol Horvath Moje bydlisko je Piestany Moje

Spriatelené triedy n Dostaneme výstup: Volam sa Pavol Horvath Moje bydlisko je Piestany Moje cislo uctu je 012011/0100 Na ucte mam 150000 Sk Volam sa Misko Horvath Moj otec je Pavol Horvath Moj otec ma cislo uctu 012011/0100 Stav jeho uctu je 150000 Sk Po mojom vybere mu na ucte zostalo 20000 Sk