← Back to C++ Course | Chapter 10: Polymorphism & Abstraction | Lesson 3 of 11

C++ Virtual Functions

A virtual function lets a child's version be chosen while the program runs, even if you hold the object as its parent, like calling speak on an animal and getting the right sound. It powers flexible designs.
Syntax
cpp
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

cpp
// 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;
}

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

cpp
// 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;
}

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

cpp
// 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;
}

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

cpp
#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;
}

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

cpp
#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;
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Forgetting virtual in the base function, so the derived version is not chosen at runtime.
  2. Not writing override, so a mismatched signature compiles as a new function without any warning.
  3. Deleting a derived object through a base pointer when the destructor is not virtual, which is undefined behavior.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.