3 minute read

Introduction

First off, it is very important that you already know the basics of C++, as well as inheritance and polymorphism. These topics will be useful to fully understand virtual functions.

Context

Let’s first assume that we have a base class called Animal,and we have a derived class called Cat. Example:

#include <iostream>
#include <string>
class Animal {
public:
    std::string getName() {
        return "I am an animal";
    }
};

class Cat : public Animal {
public:
    std::string getName() {
        return "I am a cat";
    }
};

int main() {
    Cat* ptrCat = new Cat; //Pointer of type Cat to a new Cat object allocated on the heap
    std::cout << ptrCat->getName() << std::endl; //Returns "I am a cat"
}

If we run the code, we will get the following output: I am a cat

So far, we haven’t encountered any weird behavior. However, note that if we were to access our Cat object with a pointer of type Animal like:

int main() {
    Animal* ptrCat = new Cat;
    std::cout << ptrCat->getName() << std::endl; //Returns "I am an animal"
    return 0;
}

We would get the following output: I am an animal

What is happening here? Basically, we are accessing the getName function of the Animal class, and not the Cat class. This is because we are accessing the Animal class using a pointer of type Animal, and not a pointer of type Cat. The compiler simply calls whatever function is defined on the type (Animal*).

This would also happen if we had a *vector<Animal>** since each element would be of type Animal* (a pointer).

This is one of the dangers of polymorphism, but this can be easily fixed with Virtual Functions as we will explain below.

Virtual Functions

The virtual keyword is used to declare a function as virtual. A virtual function is a function that can be overridden by a derived class. That means that, once we call the function using a pointer of type Animal* which points to a Cat object, the compiler will call the Cat class version of the function. Thus, fixing the problem above.

Example:

#include <iostream>
#include <string>
class Animal {
public:
    virtual std::string getName() { //Tells the compiler that it can be overridden by a derived class function
        return "Animal";
    }
};

class Cat : public Animal {
public:
    std::string getName() {
        return "I am a cat";
    }
};

int main() {
    Animal* ptrCat = new Cat;
    std::cout << ptrCat->getName() << std::endl; //Returns "I am a cat"
    return 0;
}

Output of the code: I am a cat

Note that by using the virtual keyword, we are effectively telling the compiler that the getName() function can be overridden by a derived class member function (Cat::getName()).

References

Read more about virtual functions here: https://en.cppreference.com/w/cpp/language/virtual