C++ Virtual Functions
In this page:
class Base {
public:
virtual return_type method_name() { }
};
class Derived : public Base {
public:
return_type method_name() override { }
};
Introduction to Virtual Functions
A virtual function is a member function declared in the base class using the virtual keyword, which tells the compiler to defer the decision of which version to call until runtime, based on the actual type of the object rather than the declared type of the pointer or reference.
Example: Introduction to Virtual Functions
// Include std::cout and std::cin
#include <iostream>
// Define the type Animal
class Animal {
// Members below can be accessed from outside the class
public:
virtual void speak() { std::cout << "Animal" << std::endl; }
};
// Define the type Dog
class Dog : public Animal {
// Members below can be accessed from outside the class
public:
void speak() override { std::cout << "Dog" << std::endl; }
};
// Program execution starts in main()
int main() {
// Allocate an object on the heap with new
Animal *a = new Dog();
a->speak();
// Free the heap memory with delete
delete a;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
The override Keyword
C++11 introduced the override keyword, which you write on a derived class's overriding method to make your intent explicit.
The compiler then verifies that this method really does match a virtual function in a parent class exactly, catching typos or signature mismatches that would otherwise silently create a new, unrelated function.
Example: The override Keyword
// Include std::cout and std::cin
#include <iostream>
// Define the type Animal
class Animal {
// Members below can be accessed from outside the class
public:
virtual void speak() { std::cout << "Animal" << std::endl; }
};
// Define the type Dog
class Dog : public Animal {
// Members below can be accessed from outside the class
public:
void speak() override { std::cout << "Dog" << std::endl; }
};
// Program execution starts in main()
int main() {
Dog d;
d.speak();
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Virtual Destructors
If you delete a derived-class object through a base class pointer, the derived class's destructor won't run unless the base class's destructor is marked virtual — skipping this is a classic source of resource leaks, since any cleanup logic in the derived destructor simply never executes.
Example: Virtual Destructors
// Include std::cout and std::cin
#include <iostream>
// Define the type Animal
class Animal {
// Members below can be accessed from outside the class
public:
virtual ~Animal() { std::cout << "Animal destroyed" << std::endl; }
};
// Define the type Dog
class Dog : public Animal {
// Members below can be accessed from outside the class
public:
~Dog() { std::cout << "Dog destroyed" << std::endl; }
};
// Program execution starts in main()
int main() {
// Allocate an object on the heap with new
Animal *a = new Dog();
// Free the heap memory with delete
delete a;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Internal Working of Virtual Functions
Under the hood, C++ implements dynamic dispatch using a hidden table of function pointers called the VTABLE, together with a hidden pointer inside each object called the vptr that points to its class's table.
This machinery runs automatically and adds only a small, predictable runtime cost.
Example: Internal Working of Virtual Functions
#include <iostream>
class Animal {
public:
virtual void speak() { std::cout << "Animal" << std::endl; }
// each object carries a hidden vptr into its class's VTABLE
};
class Dog : public Animal {
public:
void speak() override { std::cout << "Dog" << std::endl; }
};
int main() {
Animal *a = new Dog();
a->speak();
delete a;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Rules of Virtual Functions
Virtual functions come with a few hard rules: they can never be static, since static functions aren't tied to any particular object and dynamic dispatch requires one, and an overriding function's return type must either match exactly or be a compatible covariant type.
Example: Rules of Virtual Functions
#include <iostream>
class Animal {
public:
virtual void speak() { std::cout << "Animal" << std::endl; }
// virtual functions can never be static
};
class Dog : public Animal {
public:
void speak() override { std::cout << "Dog" << std::endl; }
};
int main() {
Animal *a = new Dog();
a->speak();
delete a;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
- Forgetting
virtualin the base function, so the derived version is not chosen at runtime. - Not writing
override, so a mismatched signature compiles as a new function without any warning. - Deleting a derived object through a base pointer when the destructor is not virtual, which is undefined behavior.
Chapter Quiz — Complete all 11 topics to unlock
0/11 topics done
Complete these topics first: