Jazyk C++ I Polymorfismus AR 2013/2014 Jazyk C++ I
Úvod Metody s časnou vazbou jsou překládány jako obyčejné céčkovské funkce. Této metodě je předán jako první implicitní parametr this. Rozdíl mezi obyčejnou funkcí a metodou s pozdní vazbou je pouze v tom, že překladač u metody kontroluje, zda je volaná skutečně na správný objekt. AR 2013/2014 Jazyk C++ I 2
Časná vazba Překladač v době překladu ví přesně jaká metoda bude kdy vykonána. Překladač zná adresu podprogramu, na který má předat řízení včas. V době překladu. AR 2013/2014 Jazyk C++ I 3
Časná vazba class Base { void f() { cout << "Method of Base class\n"; } }; class Inherited : public Base { void g() { cout << "Method of Inherited class\n"; } } AR 2013/2014 Jazyk C++ I 4
Časná vazba Co když ale potřebujeme, aby se vykonání dané metody chovalo jinak než u předka? AR 2013/2014 Jazyk C++ I 5
Časná vazba Co když ale potřebujeme, aby se vykonání dané metody chovalo jinak než u předka? Můžeme přepsat metodu AR 2013/2014 Jazyk C++ I 6
Časná vazba class Base { void Early() { cout << "Method of Base class with early binding\n"; } }; class Inherited : public Base { void Early() { cout << "Method of Inherited class with early binding\n"; } } AR 2013/2014 Jazyk C++ I 7
Časná vazba Co když ale potřebujeme, aby se vykonání dané metody chovalo jinak než u předka? Můžeme přepsat metodu Použít pozdní vazbu AR 2013/2014 Jazyk C++ I 8
Pozdní vazba Překladač u metod volaných pozdní vazbou nerozhoduje na jakou instanci je metoda volaná. V době kompilace. Používá se u metod deklarovaných jako virtuální. AR 2013/2014 Jazyk C++ I 9
Pozdní vazba Virtuální metoda = metoda volaná pozdní vazbou. Klíčové slovo virtual před deklarací metody překladači přikazuje použít pozdní vazbu při volání metody. V definici mimo třídu se specifikátor virtual vyskytovat nesmí. AR 2013/2014 Jazyk C++ I 10
Pozdní vazba class Base { void Early() { /*... */} virtual void Late() { cout << "Method of Base class with late binding\n"; } }; class Inherited : public Base { void Early() { /*... */} virtual void Late() { cout << "Method of Inherited class with late binding\n"; } } AR 2013/2014 Jazyk C++ I 11
Pozdní vazba Každá instance, která má alespoň jednu virtuální metodu má v sobě navíc ukazatel na tzv. tabulku virtuálních metod (TVM). Ukazatel není volně přístupný. TVM obsahuje adresy virtuálních metod. O správnou inicializaci ukazatel na TVM se postará překladač. AR 2013/2014 Jazyk C++ I 12
Pozdní vazba Omezení: Metoda označená jako virtuální nemůže být současně označená jako inline. Konstruktor nemůže být virtuální. Instance musí být korektně inicializována. AR 2013/2014 Jazyk C++ I 13
Pozdní vazba Base* p = (Base*)malloc(sizeof(Inherited)); //p->late(); //not! p->early(); free(p); Output: Method of Base class with early binding AR 2013/2014 Jazyk C++ I 14
Pozdní vazba class Base { int _x; Base(const int& x) : _x(x) { } virtual ~Base() {}; }; class InheritedA : public Base { InheritedA(const int& x) : Base(x) {} virtual void Print() { cout << typeid(*this).name() << endl; } virtual ~InheritedA() { } }; AR 2013/2014 Jazyk C++ I 15
Pozdní vazba class InheritedB : public InheritedA { }; InheritedB(const int& x) : InheritedA(x) {} virtual void Print() { cout << typeid(*this).name() << endl; } virtual ~InheritedB() { } Base* ba = new InheritedA(1); Base* bb = new InheritedB(2); //ba->print();//not! //bb->print();//not! InheritedA* ia = dynamic_cast<inheriteda*>(ba); InheritedA* ib = dynamic_cast<inheriteda*>(bb); ia->print(); ib->print(); Output: class InheritedA class InheritedB AR 2013/2014 Jazyk C++ I 16
Abstraktní třídy Třída, která může být použita pouze jako předek jiné třídy a nelze od ní vytvářet instance. Třída se stává abstraktní třídou, jestli obsahuje alespoň jednu čistě virtuální metodu. AR 2013/2014 Jazyk C++ I 17
Abstraktní třídy Čistě virtuální metoda virtual prototype = 0; Prototype je informační deklarace nevirtuální metody. class Comparable { int CompareTo(const Comparable& seconde) = 0; } AR 2013/2014 Jazyk C++ I 18
Abstraktní třídy class AbstractClass { virtual void Print() = 0; ~AbstractClass() { } }; class ConcreteClass : public AbstractClass { ConcreteClass() { } virtual void Print(); ~ConcreteClass() { } }; void ConcreteClass::Print() { cout << typeid(concreteclass).name() << endl; } //AbstractClass* a1 = new AbstractClass; //not! AbstractClass* a2 = new ConcreteClass; a2->print(); Output: Class ConcreteClass AR 2013/2014 Jazyk C++ I 19
Abstraktní třídy class AbstractClass { }; virtual void Print() = 0; ~AbstractClass() { } void AbstractClass::Print() { cout << typeid(abstractclass).name() << '\n'; } class ConcreteClass : public AbstractClass { }; virtual void Print(); ~ConcreteClass() { } void ConcreteClass::Print() { AbstractClass::Print(); cout << typeid(concreteclass).name() << endl; } AR 2013/2014 Jazyk C++ I 20
Abstraktní třídy class DataObject { }; virtual void NextRecord() = 0; virtual void PriorRecord() = 0; virtual void AddRecord(const std::string& name) = 0; virtual void DeleteRecord(const std::string& name) = 0; virtual void ShowRecord() = 0; virtual void ShowAllRecords() = 0; AR 2013/2014 Jazyk C++ I 21
Paměťová reprezentace class TA { int a, b; virtual void f(); virtual void g(); }; TA A1, A2; AR 2013/2014 Jazyk C++ I 22
Paměťová reprezentace class TB : public TA { int c; virtual void f(); virtual int h(); }; TB B1, B2; AR 2013/2014 Jazyk C++ I 23
Třídní ukazatele Kromě klasických ukazatelů máme možnost pracovat ještě s třídními ukazateli. AR 2013/2014 Jazyk C++ I 24
Třídní ukazatele Klasické ukazatele class TA { int x; int f(); //... }; int (*uf)(); uf = &A.f; uf = A.f; // Error // Error TA A; int *ux; ux = &A.x; // OK AR 2013/2014 Jazyk C++ I 25
Třídní ukazatele Třídní ukazatel = ukazatel do třídy Pracuje s adresami atributů a metod tříd. Představují relativní adresu určité složky vzhledem k začátku instance. NameOfClass::*Declarator; AR 2013/2014 Jazyk C++ I 26
Třídní ukazatele Třídní ukazatel lze porovnávat pomocí operátorů == a!=. Nelze na ně použít operátory <, <=, > nebo >=. Nelze na něj aplikovat adresovou aritmetiku. Ukazatel do třídy A lze implicitně přetypovat na ukazatel do třídy B, kde B je potomek třídy A. Takovéto přetypování je chybné, pokud se jedná o nedostupného, virtuálního nebo nejednozačného předka. Celočíselnou konstantu s hodnotou 0 lze přetypovat na třídní ukazatel jakéhokoli typy. Výsledek je třídní ukazatel nikam. AR 2013/2014 Jazyk C++ I 27
Třídní ukazatele lass TA { int x, y; int f(); //... }; TA A1, A2; TA *UA2 = &A2; //deklarace tř. ukazatelů na atribut a na metodu int TA::*ui; int (TA::*uf)(); //deklarace tř. ukazatele s inicializací int TA::*ui2 = &TA::x; ui = &TA::y; uf = TA::f; // operátor & se musí uvést // dtto uf = &TA::f; AR 2013/2014 Jazyk C++ I 28
Třídní ukazatele Dereference třídního ukazatele se vztahuje přímo na konkrétní instanci. Syntaktický zápis dereferenčních operátorů: Instance.*classPointer PointerOnInstance->*classPointer Třídní ukazatele nelze využít k práci se statickými složkami třídy. AR 2013/2014 Jazyk C++ I 29
Třídní ukazatele A1.*ui = 10; // dtto A1.y = 10 A2.*ui = 15; // dtto A2.y = 15 UA2->*ui = 20; // dtto UA2->y = 20 resp. A2.y = 20 AR 2013/2014 Jazyk C++ I 30
Unie Unie nelze považovat za plnohodnotné objektové typy kvůli následujícím omezením: Neumí dědičnost. Pokud nejsou specifikována přístupová práva pro složky unie, jsou tyto složky veřejně přístupné. Neumí virtuální metody. Sic může mít konstruktor, destruktor a přetížený operátor =, její atribut nemůže být typu s konstruktorem, destruktorem či přetíženým operátorem =. V anonymní unii nemohou existovat metody ani statické složky. AR 2013/2014 Jazyk C++ I 31
Zdroje PRATA, Stephen. Mistrovství v C++. 3. aktualiz. vyd. Překlad Boris Sokol. Brno: Computer Press, 2007, 1119 s. ISBN 978-80-251-1749-1. AR 2013/2014 Jazyk C++ I 32