9.2. Virtuální funkce

Časová náročnost
Časová náročnost: 15 minut

Virtuální funkce je členská funkce, která je deklarována v rámci základní třídy a je redefinována odvozenou třídou. Aby byla funkce považována za virtuální je nutné uvést před jejím jménem klíčové slovo virtual. Při dědění třídy, která obsahuje virtuální funkci, si odvozená třída virtuální funkci redefinuje dle sebe. Když je virtuální funkce redefinována odvozenou třídou není již klíčové slovo virtual nutné.

Virtuální funkce může být volána jako jakákoliv jiná členská funkce. Když ukazatel základní třídy ukazuje na odvozený objekt, který obsahuje virtuální funkci, a tato virtuální funkce je volána prostřednictvím ukazatele, pak se na základě typu objektu odkazovaného ukazatelem určí a to za běhu programu, která verze virtuální funkce bude spuštěna. Tímto způsobem se dociluje polymorfismu za běhu programu. Následně se třídě, která definuje virtuální funkci říká polymorfická třída.

V následujícím příkladu si ukážeme doposud probranou teorii virtuálních funkcí.

src/9_1.cpp
Příklad 9.1.
#include <iostream.h>

class base {
public:
  int i;
  base(int x) { i = x; }
  virtual void func()
  {
    cout << "Verze func() ze tridy base: ";
    cout << i << '\n';
  }
};

class derived1 : public base {
public:
  derived1(int x) : base(x) {}
  void func() 
  {
    cout << "Verze func() ze tridy derived1: ";
    cout << i*i << '\n';
  }
};

class derived2 : public base {
public:
  derived2(int x) : base(x) {}
  void func() 
  {
    cout << "Verze func() ze tridy derived2: ";
    cout << i+i << '\n';
  }
};

int main()
{
  base *p;
  base ob(10);  
  derived1 d_ob1(10);   
  derived2 d_ob2(10); 

  p = &ob;
  p->func(); // func() tridy base

  p = &d_ob1;
  p->func(); // func() tridy derived1

  p = &d_ob2;
  p->func(); // func() tridy derived2

  return 0;
}


Pokud odvozená třída nepřehodnotí virtuální funkci, pak je použita funkce definovaná v základní třídě.

src/9_2.cpp
Příklad 9.2.
#include <iostream.h> 

class base {
public:
  int i;
  base(int x) { i = x; }
  virtual void func()
  {
    cout << "Verze func() ze tridy base: ";
    cout << i << '\n';
  }
};

class derived1 : public base {
public:
  derived1(int x) : base(x) {}
  void func() 
  {
    cout << "Verze func() ze tridy derived1: ";
    cout << i*i << '\n';
  }
};

class derived2 : public base {
public:
  derived2(int x) : base(x) {}
};

int main()
{
  base *p;
  base ob(10);  
  derived1 d_ob1(10);   
  derived2 d_ob2(10); 

  p = &ob;
  p->func(); // func() tridy base

  p = &d_ob1;
  p->func(); // func() tridy derived1

  p = &d_ob2;
  p->func(); // func() tridy base

  return 0;
}


Může se stát, že ikdyž je virtuální funkce definována v základní třídě, tak nemusí provádět smysluplné operace. Někdy prostě jenom definuje základní sadu členských funkcí a proměnných, které si pak odvozené funkce upraví.

upozornění

Jazyk C++ proto podporuje tzv. čistě virtuální funkce. Čistě virtuální funkce nemají definici vztaženou k základní třídě. Je v ní zahrnut pouze prototyp s následující syntaxí:
virtual typ jméno-funkce(seznam-argumentů) = 0;


Tato syntaxe říká překladači, že vzhledem k základní třídě nemá funkce tělo. Pokud je virtuální funkce definována jako čistá, pak to každou odvozenou třídu nutí, aby ji přehodnotila. Jestliže to odvozená třída neudělá, ohlásí se chyba překladu.

Pokud třída obsahuje nejméně jednu čistě virtuální funkci, pak je na ni odkazováno jako na abstraktní třídu. Abstraktní třídy tedy existují proto, aby mohly být děděny.

Následující příkaz nám ukazuje použití čisté virtuální funkce. Program vypočte obsah čtyřúhelníka a trojúhelníka na základě děděné čisté virtuální funkce.

src/9_3.cpp
Příklad 9.3.
#include <iostream.h>

class area {
  double dim1, dim2; // rozmery
public:
  void setarea(double d1, double d2)
  {
    dim1 = d1;
    dim2 = d2;
  }
  void getdim(double &d1, double &d2)
  {
    d1 = dim1;
    d2 = dim2;
  }
  virtual double getarea() = 0; // cista virtualni funkce
};

class rectangle : public area {
public:
  double getarea() 
  {
    double d1, d2;
  
    getdim(d1, d2);
    return d1 * d2;
  }
};

class triangle : public area {
public:
  double getarea()
  {
    double d1, d2;
  
    getdim(d1, d2);
    return 0.5 * d1 * d2;
  }
};

int main()
{
  area *p;
  rectangle r;
  triangle t;

  r.setarea(3.3, 4.5);
  t.setarea(4.0, 5.0);

  p = &r;
  cout << "Obsah ctyruhelnika je: " << p->getarea() << '\n';

  p = &t;
  cout << "Obsah trojuhelnika je: " << p->getarea() << '\n';

  return 0;
}


flash\virtual.swf
Flashová animace
Kliknutím na ikonu spustíte animaci.