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

C++ Virtual Functions

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 <iostream>

class Animal {
public:
	virtual void speak() { std::cout << "Animal" << std::endl; }
};

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

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 <iostream>

class Animal {
public:
	virtual void speak() { std::cout << "Animal" << std::endl; }
};

class Dog : public Animal {
public:
	void speak() override { std::cout << "Dog" << std::endl; }
};

int main() {
	Dog d;
	d.speak();
	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 <iostream>

class Animal {
public:
	virtual ~Animal() { std::cout << "Animal destroyed" << std::endl; }
};

class Dog : public Animal {
public:
	~Dog() { std::cout << "Dog destroyed" << std::endl; }
};

int main() {
	Animal *a = new Dog();
	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;
}

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.