====== Tabulka virtuálních metod ====== Třídy, které mají virtuální metody, obsahují ukazatel na tabulku virtuálních metod. \\ Tabulka má pro každou virtuální metodu jednu řádku s ukazatelem na funkci implementující danu metodu. \\ Každé virtuální metodě překladač přiřadí číslo řádky v tabulce virtuálních metod. Pokud vytvoříme více instancí téhož typu obsahují všechny instance ukzatel na stále stejnou tabulku virtuálních metod. \\ ( A pokud máme více virtuálních metod, ušetříme místo v paměti, jednotlivé instance obsahují jeden ukazatel na tabulku místo několika ukazatelů na funkce, které by odpovídaly implementaci z předešlé kapitoly.) Při volání virtuální metody se v instanci nalezne ukazatel na tabulku, v tabulce použije řádku odpovídající dané metodě a zde nalezene ukazatel na funkci, kterou zavolá. \\ ( Při volání virtuální metod nesmí být ukazetel na instanci roven //nullptr//, jinak by nešlo nalézt tabulku virtuálních metod. ) V následujícím příkladu jsou dvě třídy a několik virtuálních metod. class Basic { public: int x, y; public : virtual void print () { cout << "Basic (" << x << ", " << y << ")" << endl; } virtual int distance () { return sqrt (x*x +y*y); } virtual void scale (int c) { x = x * c; y *=c; } }; class Component : public Basic { public: int z; public : virtual void print () { cout << "Component (" << x << ", " << y << "," << z << ")" << endl; } virtual int distance () { return sqrt (x*x +y*y + z*z); } virtual void move (int dx, int dy, int dz) { x += dx; y += dy; z += dz; } }; * Vytvoříme instanci typu //Basic// uložíme ji do proměnné **b**. * Další instanci typu //Component// uložíme do proměnné **c**. * Třída //Component// je odvozená z třídy //Basic// a tak můžeme ukazatel na druhou instanci uložit také do proměnné //u// typu ukazatel na //Basic// Basic * b = new Basic; Component * c = new Component; Basic * u = c; Proměnné //x//, //y// a //z// jsou poněkud nestandardně veřejně přístupné (//public://) \\ K těmto proměnným můžeme přistupovat pomocí **b->x**, **u->y** nebo **c->z** \\ K proměnné //z// se nedostaneme pomocí ukazatele na //Basic//, tedy **u->z** **nelze** použít. {{vmt.png}} * Třída //Basic// má tabulku virtuálních metod se třemi řádkami, odpovídajícími metodám //print//, //distance// a //scale// * Třída //Component// má tabulku se čtyřmi řádky, přibyla metoda //move// * První dvě řádky obsahují ukazatele na předefinované //print// a //distance// ze třídy //Component// * Ve třetí řádce zůstává //scale// ze třídy //Basic//, neboť v odvozené třídě nebyla předefinována * Přibyla čtvrtá řádka pro //move// * Metodu //move// můžeme zavolat pomocí **c->move (1, 2, 3)** * Nelze použít **u->move**, neboť //u// je typu ukazatel na //Basic// a třída //Basic// nezná metodu //move//. ==== Vynechání některých klíčových slov virtual v odvozené třídě ==== Pokud vynecháme klíčové slovo **virtual** u metod **print** a **distance** ve třídě **Component**, program se bude chovat stejně, metody jsou již deklarovány jako virtální ve tříde //Basic//. ==== Vynechání některých klíčových slov virtual v základní třídě ==== Pokud vynecháme klíčová slova **virtual** u metod **print** a **distance** ve třídě **Basic** * **c->print()** bude stále volat //Component::print// neboť //c// je typu ukazatel na //Component// * **u->print()** bude stále volat **Basic::print**, protože //u// je typu ukazatel na //Basic// a //print// není virtuální metoda * //virtual print ()// až ve třídě //Component// to nijak neovlivní ==== Vynechání všech klíčových slov virtual v základní třídě ==== Pokud vynecháme klíčová slova **virtual** u všech metod ve třídě **Basic**, \\ třída nebude obsahovat ukazatel na tabulku virtuálních metod Pokud odvozená třída **Component** stále bude mít virtuální metody, \\ překladač nejspíše ve třídě komponent nejprve umístí proměnné //x// a //y//, aby třída //Component// začínanala stejně třída //Basic//, \\ a ukazatele na instance odvozené třídy //Component// se daly přiřadit i do proměnných typu ukazatele na //Basic// (např. **u = c;**) Za proměnné //x// a //y// nejspíše bude umístěn ukazatel na tabulku virtuálních metod a až za ním bude proměnná //z//. ====== Přetypování dynamic_cast ====== [[dynamic| přetypování dynamic_cast]] Proměnnou typu ukazatel na základní třídu nemůžeme přiřadit do proměnné typu ukazatel na odvozenou třídu. Basic * b = new Basic; Component * v = b; Je zřejmé, že objekt typu //Basic// nemá proměnnou //z// a metodu //move//, a proto nelze přiřadit //v = b;// Pokud v proměnné typu //Basic*// byl uložen objekt typu //Component//, potom by bylo možné objekt uložit do proměné typu //Component*//. \\ Ovšem překladač nám stejně přiřazení nedovolí. Z hlediska překladače je přířazení ukazatele na //Basic// do ukazatele na //Component// potenciálně nebezpečné. Item * c = new Component; Basic * u = c; Component * v = u; /* překladač ohlásí chybu */ Konstrukce **dynamic_cast** umožňuje přetypování ukazatele na základní třídu na ukazatel na odvozenou třídu Basic * u; Component * v = dynamic_cast < Component * > (u); Pokud parametr v kulatých závorkách za běhu programu nebude skutečně typu uvedeného v < > závorkách, bude výsledkem **nullptr**