410 likes | 496 Views
ÚVOD DO CPP 7. Dědičnost - pokračování konstruktory a destruktory v procesu dědění vícenásobná dědičnost virtuální základní třídy. 7. Dědičnost - pokračování 7.1 - řízení přístupu k základní třídě 7.2 - chráněné členy třídy 7.3 - konstruktory a destruktory v procesu dědění
E N D
ÚVOD DO CPP 7 Dědičnost - pokračování konstruktory a destruktory v procesu dědění vícenásobná dědičnost virtuální základní třídy
7. Dědičnost - pokračování 7.1 - řízení přístupu k základní třídě 7.2 - chráněné členy třídy 7.3 - konstruktory a destruktory v procesu dědění 7.4 - vícenásobná dědičnost 7.5 - virtuální základní třídy
Dědičnosti představuje jeden ze základních nástrojů objektového programování. Umožňuje vytvářet z existujících tříd odvozené třídy, které dědí vlastnosti svých základních, bázových tříd. Terminologie: Místo označení odvozená třída se používá názvu dceřina třída, potomek . Základní třída se nazývá bázová třída, rodičovská třída a pod. Terminologie není zcela ustálená. Při procesu dědění není nutné mít přímo zdrojový text metod rodičovských tříd, ale pouze jejich deklarace v hlavičkovém souboru. Přeložené funkce mohou být uloženy v knihovně a lze je připojit až při sestavování programu.
Nedědí se: • - konstruktor. Je však možné explicitní vyvolání konstruktoru základní třídy • - destruktor. Je automaticky vyvoláván v destruktoru odvozené třídy. • - přetížený operátor přířazení = . Pokud není nově explicitně definován • pro odvozenou třídu, bude použit operátor předka. • Využití zděděných prvků v konstrukci odvozené třídy. • Zděděné prvky se dělí podle přístupového práva na: • Nepřístupné - jsou to zděděné soukromé prvky základní ( rodíčovské ) třídy • .Práce s těmito prvky je možná pouze voláním zděděných přístupových metod • zakladní třídy. • Není možné je přímo využívat ve vlastních metodách odvozené třídy. • 2)Chráněné - jsou využitelné ve vlastních metodách odvozené třídy. • Nejsou přístupné z vnějšku. • 3) Veřejné - jsou využitelné ve vlastních metodách odvozené třídy. • Jsou přístupné i z vnějšku. • V deklaraci odvozené třídy je možné doplnit libovolně další prvky a v její • implementaci lze využívat zděděné přístupné prvky( tj. chráněné a veřejné ).
Řízení přístupu k základní třídě, základní pojmy. Třída, která se dědí , je základní třída. Třída, vzniklá děděním je odvozená třída. Odvozená třída používá obecný formát specifikace : class jméno_odvozené_třídy :specifikátor_přístupu jméno základní třídy { //..atributy, metody... } ; Specifikátor_přístupu určuje, jak jsou členy základní třídy děděny odvozenou třídou. Většinou bývá specifikátor přístupu explicitně uveden. Není-li specifikátor přístupu uveden, považuje se implicitně za private. Specifikátor přístupu je jedním z klíčových slov - public - protected - private
7.2 Zavedení CHRÁNĚNÝCH ČLENŮ, přístup protected. Důvod zavedení chráněných členů : Metody odvozené třídy nemají přístup k privátním členům základní třídy. Jestliže odvozená třída potřebuje přístup k některému z členů základní třídy, musí být tento člen veřejný. Požadavek: Ponechat členu základní třídy přítupové právo private, ale dovolit k němu přístupmetodám odvozené třídy. Tomuto požadavku odpovídá zavedení přístupového specifikátoru protected pro chráněný člen. Je ekvivalentní specifikátoru private s tou výjimkou, že chráněné členy ( protected) základní třídy jsou přístupné i metodám těch tříd, které byly z této základní třídy odvozeny. Vně základní nebo odvozené třídy nejsou chráněné členy ( protected ) přístupné. Specifikátor přístupu protected může být uveden kdekoliv v deklaraci třídy, avšak jeho typické umístění je po deklaraci privátních členů, před členy veřejnými. Syntaxe: class jméno_třídy { // privátní členy protected: // chráněné členy public: // veřejné členy } ;
Přístupová práva ke členům třídy - souhrn : Rekapitulace tří možných specifikací přístupu ke členům třídy: 1) public: veřejné členy může používat kterákoliv funkce nebo inicializátor 2) private: soukromé, privátní členy může používat pouze: - členské funkce ( metoda ) - členské inicializátory - přátelé třídy, t.j. funkce a třídy deklarované v ní jako friend Specifikace private je ve třídách implicitní. 3) protected: chráněné členy mohou používat pouze - členské funkce - členské inicializátory - přátelé třídy a také - členské funkce ( metody ) tříd odvozených z této třídy a jejich přátelé.
DĚDĚNÍ - změna přístupových práv v závislosti na způsobu dědění, odvození třídy nevirtuálním děděním základní třídy . Základní třída může být děděna třemi způsoby a to se specifikátorem přístupu 1) public 2) protected 3) private. Způsob 1) - syntaxe: class Odvozena : public Zakladni { /*...*/ }; - veřejné členy základní třídy se stávají veřejnými členy třídy odvozené - chráněné členy základní třídy se stávají chráněnými členy třídy odvozené - k privátním členům základní třídy nemá odvozená třída přístup.( metody) Platí: Předek základní třída<----------- odvozená třída členy: protected .......................zůstávají protected public .............................zůstávají public private nemá přístup Třída základní je veřejným předkem třídy odvozené.
Způsob 2) Synaxe: class Odvozena : private Zakladni { /*...*/ }; - veřejné členy základní třidy se stávají soukromými ( privátními ) členy třídy odvozené - chráněné členy základní třídy se stávají soukromými ( privátními ) členy třídy odvozené. - k privátním členům základní třídy nemá třída odvozená přístup. Třída základní je privátním předkem třídy odvozené. Platí: základní třída<----------- odvozená třída protected ..................... private public............................private private nemá přístup
Způsob 3) Syntaxe: class Odvozena : protected Zakladni { /*...*/ }; - veřejné členy základní třídy se stávají chráněnými členy třídy odvozené - chráněné členy základní třídy se stávají chráněnými členy třídy odvozené. - k privátním členům třídy základní nemá třída odvozená přístup. Třída základní je chráněným předkem třídy odvozené. Platí: základní třída<----------- odvozená třída protected......................protected public.......................... protected private nemá přístup
Příklad přístupu ke členům třídy. Základní třída je děděna jako veřejná public. class Zakladni_trida{ private: void privatni_metoda() { /*...*/} protected: void chranena_metoda() { /*...*/} public : void verejna_metoda() { /*...*/} }; // end Zakladni_trida…………………………… class Odvozena_trida : public Zakladni_trida { public: void fce() { // privatni_metoda(); //fce() ji nemuže použít, fce() není // metodou základní třídy, nemá k ní přístup chranena_metoda(); // OK -může ji použít, protože fce()je //metodou třídy odvozené verejna_metoda();//OK - fce() ji může použít, je veřejným členem třídy Zakladni_trida }// end fce( ) }; // end Odvozena_trida……………………………………………
class Dalsi_odvozena : public Odvozena_trida { public: void funkcedalsi(){ chranena_metoda(); //OK - funkcedalsi() je členská fce //třídy nepřímo odvozené (indirekt) ze třídy Zakladni_trida. } };// end třídy Dalsi_odvozena…………………………………………………… int main() { Odvozena_trida Ob; //Ob. privatni_metoda(); // nelze //Ob. chranena_metoda(); // nelze, ekvivalent private Ob. verejna_metoda(); /*OK, veřejný člen v Zakladni_trida i v Odvozena_trida při dědění public */ Dalsi_odvozena Obdalsi; Obdalsi.funkcedalsi(); //OK veřejná v Dalsi_odvozena Obdalsi.fce(); /* OK fce( ) je veřejná ve třídě Odvozena_trida */ Obdalsi.verejna_metoda(); //OK veřejná ve třídě Zakladni return 0; }
Příklad - přístup vnitřní, metody vůči atributům. viz metoda zobraz_soucet() { atribut x, atribut y} Zakladni třída je veřejně přístupným předkem třídy Odvozena. Privátní atribut x základní třídy je ve třídě odvozené pro její metody nedostupný, zůstává privátní, nezáleží na tom, jak byl děděn. Proto je chybný přímý přístup metody zobraz_soucet() k privátní proměnné x základní třídy. */ class Zakladni { int x; // standardně privátní public: void nastav_x(int n) { x=n;} void zobraz_x() { cout << x << '\n';} };// end Zakladni………………………………… class Odvozena : public Zakladni{ int y; // standardně privátní public: void nastav_y( int n) { y=n;} void zobraz_y() { cout << y << '\n';} //ale!!!! void zobraz_soucet() { cout << x+y << '\n'; } /*chyba !! metoda třídy Odvozena se pokouší o přímý přístup k privátní proměnné x třídy Zakladni ............... */ };
REDEKLARACE Redeklarace umožňuje změnit přístupová práva pro zděděnou složku. Syntaxe: jméno_třídy_původu:: jmeno fce V odvozené třídě pro ni stačí uvést odpovídající modifikátor přístupových práv, včetně úplného odkazu t.j.včetně jména rodičovské třídy. Nelze deklarovat libovolný přístup, lze pouzeobnovit minulá přístupová práva. Příklad redeklarace přístupu k zobraz_x( ) : Redeklarace z private na public. Proto můžeme metodu volat v těle funkce main( ). příklad: Class odvozena{ Private: int x; public: /* REDEKLARACE přístupového specifikátoru metody zobraz_x() a metody nastav_x() z private zpět na public */ Zakladni::zobraz_x; Zakladni::nastav_x; }; Příklad: /* základní třída je děděna se specifikátorem private.Program ukazuje možnost změnit specifikátor přístupu, vyvolaný děděním, private, pomocí redeklarece v odvozené třídě na přístupový specifikátor public */
class Zakladni{ int x; public: void nastav_x(int n) { x=n;} void zobraz_x() { cout << x << '\n';} };// end zakladni.................................. //ODVOZENÁ třída dědí PRIVÁTNĚ: class Odvozena : private Zakladni{ int y; public: /* REDEKLARACE přístupového specifikátoru metody zobraz_x() a metody nastav_x() z private zpět na public */ Zakladni::zobraz_x; Zakladni::nastav_x; void nastav_y( int n) { y=n;} void zobraz_y() { cout << y << '\n'; } };//end odvozene
int main() { Odvozena objekt; /* BEZ REDEKLARACE : objekt.nastav_x(10); CHYBA ! nastav_x() se stala následkem dědění se specifikátorem přístupu private privátní ve třídě odvozená a není proto z vnějšku přístupná.Totéž platí pro zobraz_x(). ........................................................................... Přístup k metodě je ŘEŠENÝ REDEKLARACÍ přístupového specifikátoru ve třídě Odvozena (viz redeklarace ze specifikátoru private metod zobraz_x() a nastav(x) ve tride Odvozena na specifikátor public:) */ objekt.nastav_x(10);// OK - Chyba odstraněna // redeklarací objekt.nastav_y(20); cout<< "Vypis:\n"; cout <<"x = "; objekt.zobraz_x();// OK - Chyba odstraněna // redeklarací cout << "y = "; objekt.zobraz_y(); system("Pause"); return 0; }
7.3 DĚDIČNOST - konstruktory a destruktory Konstruktory základní i odvozené třídy jsou spouštěny v pevně stanoveném pořadí. Konstruktor základní třídy je spouštěn před konstruktorem třídy odvozené. To umožňuje provést v základní třídě inicializaci, která musí být dokončena před případnou inicializací třídy odvozené. Pro destruktory platí opačné pořadí. Destruktor odvozené třídy musí být volán před destruktorem základní třídy. Obrácený postup není možný. Je to logický postup, protože základní třída tvoří základ třídy odvozené. Destruktor odvozené třídy musí být vyvolán před tím, než objekt přestává existovat.
Příkad: Spouštění konstruktorů a destruktorů základní a odvozené třídy . */ class Zakladni { public: // konstruktor Zakladni() { cout<<"Konstruktor zakladni tridy\n"; } // destruktor ~Zakladni() { cout << "Destruktor zakladni tridy\n"; getchar(); } };// end základní třídy…………………………………………………… class Odvozena : public Zakladni { public: //konstruktor Odvozena() { cout << "Konstruktor odvozene tridy\n"; } //destruktor ~ Odvozena() { cout << "Destruktor odvozene tridy\n"; } };// end odvozené třídy ……………………………………………………..
int main() { Odvozena objekt; system("Pause"); return 0; } /* vypis: Konstruktor zakladni tridy Konstruktor odvozene tridy Destruktor odvozene tridy Destruktor zakladni tridy */
Předávání argumentů konstruktorům základní a odvozené třídy. 1)Předání argumentů konstruktoru odvozené třídy : Nejjednodužší je předání argumentů konstruktoru odvozené třídy. V tomto případu se argumenty předávají obvyklým způsobem. Příklad : /* předání argumentu konstruktoru odvozené třídy class Odvozena. */ class Zakladni { public: // konstruktor: Zakladni() { cout << "Konstruktor zakladni tridy\n"; } // destruktor ~Zakladni(){ cout << "Destruktor zakladni tridy\n"; getchar(); } }; // end zakladni……………………………………………………
class Odvozena : public Zakladni { int j; public: //konstruktor - předání argumentu Odvozena(int n) { cout << "Konstruktor odvozené třídy\n"; j = n; } //destruktor ~ Odvozena() { cout << "Destruktor odvozene tridy\n"; } //clenska: void zobraz_j() { cout << j << '\n'; } };// end odvozena
int main() { Odvozena objekt( 20); objekt.zobraz_j(); system("Pause"); return 0; } /* výpis: Konstruktor zakladni tridy Konstruktor odvozene tridy 20 Destruktor odvozene tridy Destruktor zakladni tridy */
2)Předání argumentu konstruktoru základní třídy : Předání argumentu konstruktoru základní třídy se provádí pomocí rozšířeného formátu deklarace konstruktoru odvozené třídy. Syntaxe deklarace konstruktoru : jméno_odvoz_třídy(seznam argumentů):jméno_zaklad_třídy(seznam argumentů) { // tělo konstruktoru odvozené třídy } Tím je umožněno dopředné předání argumentu do základní třídy. Příklad: předání stejného argumentu konstruktoru základní a odvozené třídy : class Zakladni { int i; public: // konstruktor: Zakladni(int n) { cout << "Konstruktor zakladni tridy\n"; i = n; }
// destruktor ~Zakladni() { cout << "Destruktor zakladni tridy\n"; getchar(); } //členská void zobraz_i() { cout <<"i= "<< i << '\n';} };// end zakladni................................................ class Odvozena : public Zakladni { int j; public: //konstruktor - předání argumentu do základní třídy: Odvozena(int n) : Zakladni(n) { cout << "Konstruktor odvozene tridy\n"; j = n; } //destruktor ~ Odvozena() { cout << "Destruktor odvozene tridy\n"; getchar(); }
//clenska void zobraz_j() { cout <<"j= " << j <<'\n'; } };// end odvozena int main() { Odvozena objekt( 20);// pro obě třídy stejná inicializace objekt.zobraz_j(); objekt.zobraz_i(); getchar(); return 0; } /* vypis: Konstruktor zakladni tridy Konstruktor odvozene tridy j = 20 i = 20 Destruktor odvozene tridy Destruktor zakladni tridy */
Nejčastější případ - předání rozdílných parametrů konstruktorům odvozené a základní třídy : Předání argumentu konstruktorům odvozené i základní třídy. Argumenty konstruktorů jsou pro obě třídy rozdílné : class Zakladni { int i; public: // konstruktor Zakladni(int n ) { cout << "Konstruktor zakladni tridy\n"; i = n; } // destruktor ~Zakladni() { cout << "Destruktor zakladni tridy\n"; getchar(); } //clenska void zobraz_i() { cout <<"i= "<< i << '\n';} };// end zakladni
class Odvozena : public Zakladni { int j; public: //konstruktor - předání argumentu do základní třídy: Odvozena(int n, int m): Zakladni( m ) { cout << "Konstruktor odvozene tridy\n"; j = n; } //destruktor ~ Odvozena() { cout << "Destruktor odvozene tridy\n"; } //clenska void zobraz_j() { cout <<"j= " << j <<'\n'; } };// end odvozena
int main() { Odvozena objekt( 20, 30); // 20 Odvozena, 30 Zakladni objekt.zobraz_j(); objekt.zobraz_i(); getchar(); return 0; } /* vypis: Konstruktor zakladni tridy Konstruktor odvozene tridy j = 20 i = 30 Destruktor odvozene tridy Destruktor zakladni tridy */
7.4 Vícenásobná dědičnost. • Dědit lze dvojím způsobem: • Postupným děděním, kdy odvozená třída může být použita jako základní • třída pro další odvozenou třídu atd... • Příklad postupného dědění třídy1 a třídy2 třídou 3 - kostra programu: • class základní { // třída1 • int x; • //............. • //konstruktor • základní( x)....... • } ; • class odvozená1:public základní { // třída2 • int y; • //......... • //konstruktor • odvozena1( y, x):základní( x){........ • } ; • class odvozená2: public odvozená1 { //třída3 • int z; • //........... • //konstruktor • odvozená2( z, y, x) : odvozená1( y, x ) • } ;
Konstruktory jsou volány postupně od základní třída1 až po třída3, destruktory jsou volány v obráceném pořadí. Odvozená třída2 ze třídy1 je použita jako základní třída pro další odvozenou třídu3. Odvozená třída3 dědí třídu základní nepřímo - indirect. Třída3, má přístup k veřejným členům třídy1 a třídy2.
#include<iostream> using namespace std; class Trida1{ int a; public: //konstruktor:................................ Trida1( int x){ a = x;} //................................................... nt predej_a() { return a;} };// end trida1 //přímé dědění základní třídy: class Trida2 : public Trida1 { int b; public: //konstruktor:........................................................ Trida2(int x, int y): Trida1(y) //volání konstruktoru //základní třídy Trida1(y); { b = x; } //....................................................................... int predej_b() { return b; } };// end trida2 //Trida3 dědí odvozenou třídu2 a nepřímo základní třídu: class Trida3: public Trida2 { int c; public: //konstruktor: Trida3( int x, int y, int z) : Trida2(y,z) { //volá //konstruktor třídy2 c=x; } /* Protože Trida3 dědí public,Třída3 má přístup k veřejným členům Tridy1 a Tridy2 */ //Členské: void zobraz() { cout << predej_a() << ' '<< predej_b() << ' '; cout << c << '\n'; } };//end Trida3 int main() { Trida3 objekt(1,2,3); cout << "Vypis objekt3.zobraz() = "; objekt.zobraz(); //predej_a() a predej_b() jsou zde verejne cout<<"Vypis objekt.predej_a() = "; cout<< objekt.predej_a(); cout << '\n'; cout <<"Vypis objekt.predej_a() = "; cout<< objekt.predej_b(); cout <<'\n'; system("Pause); return 0; }