The usual way compilers handle virtual functions is to add a hidden member to each object. The hidden member holds a pointer to an array of function addresses. Such an array is usually termed a
Figure 13.5. A virtual function mechanism.
When you call a virtual function, the program looks at the vtbl address stored in an object and goes to the corresponding table of function addresses. If you use the first virtual function defined in the class declaration, the program uses the first function address in the array and executes the function that has that address. If you use the third virtual function in the class declaration, the program uses the function whose address is in the third element of the array.
In short, using virtual functions has the following modest costs in memory and execution speed:
• Each object has its size increased by the amount needed to hold an address.
• For each class, the compiler creates a table (an array) of addresses of virtual functions.
• For each function call, there’s an extra step of going to a table to look up an address.
Keep in mind that although nonvirtual functions are slightly more efficient than virtual functions, they don’t provide dynamic binding.
Things to Know About Virtual Methods
We’ve already discussed the main points about virtual methods:
• Beginning a class method declaration with the keyword virtual in a base class makes the function virtual for the base class and all classes derived from the base class, including classes derived from the derived classes, and so on.
• If a virtual method is invoked by using a reference to an object or by using a pointer to an object, the program uses the method defined for the object type rather than the method defined for the reference or pointer type. This is called
• If you’re defining a class that will be used as a base class for inheritance, you should declare as virtual functions the class methods that may have to be redefined in derived classes.
There are several other things you may need to know about virtual methods, some of which have been mentioned in passing already. Let’s look at them next.
Constructors
Constructors can’t be virtual. Creating a derived object invokes a derived-class constructor, not a base-class constructor. The derived-class constructor then uses a base-class constructor, but this sequence is distinct from the inheritance mechanism. Thus, a derived class doesn’t inherit the base-class constructors, so usually there’s not much point to making them virtual, anyway.
Destructors
Destructors should be virtual unless a class isn’t to be used as a base class. For example, suppose Employee is a base class and Singer is a derived class that adds a char * member that points to memory allocated by new. Then, when a Singer object expires, it’s vital that the ~Singer() destructor be called to free that memory.
Now consider the following code:
Employee * pe = new Singer; // legal because Employee is base for Singer
...
delete pe; // ~Employee() or ~Singer()?
If the default static binding applies, the delete statement invokes the ~Employee() destructor. This frees memory pointed to by the Employee components of the Singer object but not memory pointed to by the new class members. However, if the destructors are virtual, the same code invokes the ~Singer() destructor, which frees memory pointed to by the Singer component, and then calls the ~Employee() destructor to free memory pointed to by the Employee component.
Note that this implies that even if a base class doesn’t require the services of an explicit destructor, you shouldn’t rely on the default constructor. Instead, you should provide a virtual destructor, even if it has nothing to do:
virtual ~BaseClass() { }