V tomto tutoriálu se pomocí příkladů seznámíme s virtuální funkcí C++ a jejím použitím.
Co je virtuální funkce v C++?
Virtuální funkce v C++ je členská funkce v základní třídě, u které očekáváme, že bude v odvozených třídách přepsána.
Virtuální funkce v C++ se používá v základní třídě, aby bylo zajištěno, že funkce bude přepsána. To platí pro případy, kdy ukazatel základní třídy ukazuje na objekt odvozené třídy.
Zvažte například kód níže:
class Base >; class Derived : public Base >;
Pokud později vytvoříme ukazatel základního typu, který bude ukazovat na objekt odvozené třídy a zavoláme funkci print(), bude volat funkci print() základní třídy.
Jinými slovy, členská funkce Base není přepsána.
int main() print(); návrat 1; >
Abychom tomu zabránili, prohlásíme funkci print() základní třídy za virtuální pomocí klíčového slova virtual.
class Base >;
Virtuální funkce jsou nedílnou součástí polymorfismu v C++.
Příklad 1
#include using namespace std; class Base ; class Derived : public Base ; int main() print(); návrat 1; >
Odvozená funkce
Zde jsme deklarovali funkci print() v Base jako virtuální.
Konstruktory v C++
Tato funkce je tedy přepsána, i když použijeme ukazatel základního typu, který ukazuje na odvozený objekt 1.
Přepsat ID
C++11 nám poskytlo nové přepsání identifikátoru, které je velmi užitečné pro předcházení chybám při používání virtuálních funkcí.
Tento identifikátor identifikuje členské funkce odvozených tříd, které přepisují členskou funkci základní třídy.
class Base >; class Derived : public Base >;
Pokud použijeme prototyp funkce ve třídě Derived a definujeme tuto funkci mimo třídu, pak použijeme následující kód:
class Derived : public Base ; // definice funkce void Derived::print()
Použití přepisu
Při použití virtuálních funkcí v C++ může dojít k chybám při deklaraci členských funkcí odvozených tříd.
Použití identifikátoru přepsání způsobí, že kompilátor zobrazí chybové zprávy, když k těmto chybám dojde.
V opačném případě se program jednoduše zkompiluje, ale virtuální funkce nebude obrácena.
Zde jsou některé z těchto možných chyb:
- Chybně pojmenované funkce: Pokud se například virtuální funkce v základní třídě nazývá print(), ale my jsme omylem pojmenovali náhradní funkci v odvozené třídě jako pint().
- Funkce s různými návratovými typy: Pokud je virtuální funkce řekněme typu void, ale funkce v odvozené třídě je typu int.
- Funkce s různými parametry: pokud se parametry virtuální funkce a funkce v odvozených třídách neshodují.
- Základní třída nedeklaruje virtuální funkci.
Veřejné, chráněné a soukromé dědictví v C++
Použití funkce
Máme základní třídu Zvíře a odvozené třídy Pes a Kočka.
Každá třída má datový člen s názvem type. Předpokládejme, že tyto proměnné jsou inicializovány odpovídajícími konstruktory.
class Animal . . >; třída Pes : public Animal . . >; třída Kočka : public Animal . . >;
Nyní řekněme, že náš program vyžaduje, abychom pro každou třídu vytvořili dvě veřejné funkce:
- getType() vrátí hodnotu typu;
- print() vypíše hodnotu typu.
Obě tyto funkce bychom mohli vytvořit samostatně v každé třídě a přepsat je, což by byl dlouhý a únavný proces.
Nebo bychom mohli udělat getType() virtuální ve třídě Animal a pak vytvořit jednu samostatnou funkci print(), která jako argument vezme ukazatel na typ zvířete. Tuto jedinou funkci pak můžeme použít k přepsání té virtuální.
třída Zvíře ; . . . . void print(Animal* ani) < cout getType()
Díky tomu bude kód kratší, čistší a méně se opakující.
Příklad 2: Ukázka
// C++ program pro demonstraci použití virtuální funkce #include #include using namespace std; class Animal // deklarace virtuální funkce virtuální řetězec getType() >; třída Pes : public Animal string getType() override >; třída Kočka : public Animal string getType() override >; void print(Animal* ani) < cout getType() int main()
Zvíře: Zvíře Zvíře: Pes Zvíře: Kočka
Zde jsme použili virtuální funkci getType() a ukazatel Animal ani, abychom se vyhnuli opakování funkce print() v každé třídě.
V main() jsme vytvořili 3 Animal ukazatele pro dynamické vytváření objektů tříd Animal, Dog a Cat.
// dynamické vytváření objektů pomocí ukazatelů zvířat Animal* animal1 = new Animal(); Zvíře* pes1 = nový pes(); Zvíře* kočka1 = nová Kočka();
Poté zavoláme funkci print() pomocí těchto ukazatelů:
- Při volání print(animal1) ukazuje ukazatel na objekt Animal. Virtuální funkce ve třídě Animal se tedy provádí uvnitř print().
- Když je zavoláno print(dog1), ukazatel ukazuje na objekt Dog. Virtuální funkce je tedy přepsána a funkce Dog se provede uvnitř print().
- Když se zavolá print(cat1), ukazatel ukazuje na objekt Cat. Takže virtuální funkce je přepsána a funkce Cat se provede uvnitř print().