====== 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**