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 { public: void f() class\n"; } };
{ cout << "Method of Base
class Inherited : public Base { public: 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 { public: void Early() { cout << "Method of Base class with early binding\n"; } }; class Inherited : public Base { public: 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 { public: void Early() { /* ... */} virtual void Late() { cout << "Method of Base class with late binding\n"; } }; class Inherited : public Base { public: 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; public: Base(const int& x) : _x(x) { } virtual ~Base() {}; }; class InheritedA : public Base { public: 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 { public: 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
(bA); InheritedA* iB = dynamic_cast(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 { public: int CompareTo(const Comparable& seconde) = 0; } AR 2013/2014
Jazyk C++ I
18
Abstraktní třídy class AbstractClass { public: virtual void Print() = 0; ~AbstractClass() { } };
class ConcreteClass : public AbstractClass { public: 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 { public: virtual void Print() = 0; ~AbstractClass() { } }; void AbstractClass::Print() { cout << typeid(AbstractClass).name() << '\n'; } class ConcreteClass : public AbstractClass { public: 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 { public: virtual virtual virtual virtual virtual virtual
void void void void void void
NextRecord() = 0; PriorRecord() = 0; AddRecord(const std::string& name) = 0; DeleteRecord(const std::string& name) = 0; ShowRecord() = 0; ShowAllRecords() = 0;
};
AR 2013/2014
Jazyk C++ I
21
Paměťová reprezentace class TA { int a, b; public: 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; public: 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 int (*uf)(); uf = &A.f; // Error uf = A.f; // Error
class TA { public: int x; int f(); // ... }; TA A; int *ux; ux = &A.x;
AR 2013/2014
// OK
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 { public: 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;
AR 2013/2014
// operátor & se musí uvést // dtto uf = &TA::f;
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 = A2.*ui = UA2->*ui 20 resp.
AR 2013/2014
10; // dtto A1.y = 10 15; // dtto A2.y = 15 = 20; // dtto UA2->y = A2.y = 20
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