[[uop:vmt]]
 

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.

  • 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

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

 
uop/vmt.txt · Last modified: 2020/11/13 12:14 by 88.103.111.44
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki