Funkcja wirtualna to funkcja zdefiniowana w nadklasie, która musi być obecna w podklasie, aby ta podklasa miała pełną definicję klasy. Funkcje wirtualne opierają się na paradygmacie programowania zorientowanego obiektowo, zwanym dziedziczeniem wirtualnym, który jest najczęściej spotykany w C++ przy użyciu słowa kluczowego „wirtualny”. Aby zdefiniować funkcję wirtualną, potrzebne są dwie klasy, superklasa i podklasa. Nadklasa to miejsce, w którym funkcja jest po raz pierwszy zadeklarowana i ewentualnie zdefiniowana. Podklasa to miejsce, w którym funkcja jest zdefiniowana — lub nadpisana, w zależności od tego, czy funkcja została zdefiniowana w nadklasie.
Funkcję wirtualną można zdefiniować na dwa sposoby. Po pierwsze, można go zdefiniować jako skrót, w którym ma puste ciało i nic nie robi. Po drugie, może być zdefiniowana jako czysta funkcja wirtualna, gdzie jest zdefiniowana jako NULL w pliku nagłówkowym nadklasy.
Obie metodologie mają swoje zalety i wady. Zdefiniowanie funkcji jako kodu pośredniczącego zapewnia, że wszystkie podklasy mają jej implementację, nawet jeśli nic nie robi. Jeśli jednak zapomni się nadpisać funkcję i zaimplementować ją poprawnie w podklasie, nie pojawią się żadne błędy ani ostrzeżenia, które by na to wskazywały. Z drugiej strony, zdefiniowanie czystej funkcji wirtualnej wymaga, aby każda podklasa miała własną definicję funkcji, a jeśli tak nie jest, pojawią się błędy.
Funkcje wirtualne podlegają jednak tym samym regułom dziedziczenia, co funkcje niewirtualne, więc hierarchie dziedziczenia z więcej niż dwoma poziomami mogą nie wymagać wyraźnych definicji funkcji wirtualnych. Na przykład można rozważyć klasę A, która deklaruje funkcję wirtualną, która jest zaimplementowana w podklasie B. Klasa B ma własną podklasę, klasę C. Klasa C nie wymaga wyraźnej definicji funkcji klasy A, ponieważ dziedziczy definicja z klasy B. Jeśli to konieczne, klasa C może przesłonić funkcję klasy B lub może przesłonić funkcję klasy B podczas jej wywoływania.
Z drugiej strony funkcje wirtualne nie muszą być definiowane w podklasie, jeśli są zadeklarowane jako wirtualne w tej podklasie. Na przykład można rozważyć klasę A, która deklaruje funkcję wirtualną i ma dwie podklasy, B i C. Ponadto można sobie wyobrazić, że klasa B ma podklasy D i E, a podklasa C ma podklasy F i G.
Wszystkie klasy od B do G muszą mieć w jakiś sposób zdefiniowaną funkcję wirtualną klasy A. Jeśli klasa B ma implementację funkcji A, klasy D i E nie potrzebują jej przerabiania. Być może podklasy C muszą implementować funkcję A, ale obie robią coś innego, więc definiowanie funkcji w samej klasie C nie byłoby przydatne. W takim przypadku funkcję można zadeklarować jako wirtualną w klasie C, a implementacja nie jest konieczna.
Funkcje wirtualne mogą być trudne do nauczenia, ale gdy są używane prawidłowo, mogą zmniejszyć powielanie kodu i ogólnie znacznie ułatwić zrozumienie kodu. Istnieje jednak wiele pułapek związanych z funkcjami wirtualnymi, zwłaszcza w przypadku dziedziczenia wielokrotnego. W przypadku dziedziczenia wielokrotnego możliwe jest, że niejednoznacznie zdefiniowane funkcje wirtualne będą ze sobą kolidować, dlatego należy ich używać ostrożnie w tym kontekście.