In the C++ programming language virtual is a keyword in whose purpose is to declare a virtual function. (Virtual is also a keyword in some other programming languages which are beyond the scope of this write up.) Because of what it means to be virtual, only member functions can be virtual. To understand what virtual is, one must first understand both classes and inheritance in C++. As always, the best way to explain code is code:
/* The following code is a complete program and will compile and run as is */
class Food {
public:
Food() { }
virtual ~Food() { }
virtual int EatMe() { return 2; }
virtual const char *QueryName() { return "GENERIC"; }
};
class VeggieFood : public Food {
private:
int *foodHolder;
public:
VeggieFood() { foodHolder = new int[5]; foodHolder[0] = 3; }
virtual ~VeggieFood() { delete [] foodHolder; }
virtual int EatMe() { return foodHolder[0]; }
virtual const char *QueryName() { return "Veggie"; }
};
class Meat : public Food {
public:
virtual ~Meat() { }
virtual int EatMe() { return 0; }
virtual const char *QueryName() { return "Meat"; }
virtual int GetProtein() = 0;
};
using namespace std;
#include <iostream>
int main() {
Food *ptrFood;
ptrFood = new Food;
cout << ptrFood->QueryName() << endl;
delete ptrFood;
ptrFood = new VeggieFood;
cout << ptrFood->QueryName() << endl;
delete ptrFood;
}
The output from this program will be:
GENERIC
Veggie
This means that ptrFood, even though its a pointer to the base class Food, can be used to execute functions in any class derived from Food. This allows one to make generic code which will work with classes that have yet to be written. For instance, functors can be created using virtual functions rather than using templated functions.
Destructors of classes with virtual functions should always be virtual. The above code shows why; without ~Food() being virtual, only ~Food() would be called, not ~VeggieFood(). The result would be leaked memory. This is why STL containers such as vector should never be used as a base class. STL containers have non virtual destructors as a matter of standard, so a derived class may silently leak memory. On the other hand, constructors can never be virtual. This is because an object is always explicitly created by name either by being declared globally or locally or through the new, as seen above.
Meat has a pure virtual function named GetProtein(). This can be seen by the = 0 which follows the function declaration. A pure virtual function is declared in a class but not defined. The result is that the class itself can not be instantiated. All of the following lines would cause a compiler error:
Meat maddeningCow;
ptrFood = new Meat;
Meat::GetProtein();
Therefore, a class with pure virtual functions is only useful if other classes are derived from it. Classes such as Meat are useful for defining an abstract interface which other classes and function may make use of without having to write a series of dummy functions. Further, all pure virtual functions must be implemented in a child class for the class to be instantiated, providing a compiler-generated reminder that dummy functions do not.
There are many things about virtual functions that a coder doesn't especially need to know, but it can be valuable to know just so one can quibble with other programmers and feel more geeky. Virtual functions use a technique referred to as late binding. It is late binding because with normal binding the function to call is determined at link time, when the executable (e.g. a.out) is made. For such normal binding, a call or similar assembly instruction is hard coded pointing directly to the intended routine. With late binding, extra information, often stored in a structure called a vtable, is kept to determine which function to call. When a virtual function is called, a small stub is executed that determines which type function to actually call, and then control is passed to that function as normal. The result is that virtual functions are slightly slower than non-virtual functions. The speed difference is akin to normal functions versus inline functions.
Sources: memory, probably derived from long fogotten original sources of:
- man pages (especially those dealing with the STL)
- a data structures class
- lots of other people's code
Thanks to Swap who reminded me that modern people use namespaces in C++.