http://www.csse.monash.edu.au/~jonmc/CSE2305/Topics/05.10.VirtualFns/html/text.html
A member function which is declared with the virtual keyword is called a virtual function
Once a function is declared virtual, it may be redefined in derived classes (and is virtual in those classes too)
When the compiler sees a call to a virtual function via a pointer or reference, it calls the correct version for the object in question (rather than the version indicated by the type of the pointer or reference)
An example
class Vehicle { public: Vehicle(char* regnum) : myRegNum(strdup(regnum)){} ~Vehicle(void) { delete myRegNum; } virtual void Describe(void) { cout << "Unknown vehicle, registration " << myRegNum << endl; } protected: char* myRegNum; }; class Car : public Vehicle { public: Car(char* make, char* regnum) : Vehicle(regnum), myMake( strdup(make) ){} ~Car(void) { delete myMake; } virtual void Describe(void) { cout << "Car (" << myMake << "), registration " << myRegNum << endl; } protected: char* myMake; }; Vehicle* vp1 = new Car ("Jaguar","XJS 012"); Vehicle* vp2 = new Vehicle ("SGI 987"); Vehicle* vp3 = new Vehicle ("ABC 123"); vp1->Describe(); // PRINTS "Car (Jaguar)....." vp2->Describe(); // PRINTS "Unknown vehicle....." vp3->Describe(); // PRINTS "Unknown vehicle....."
Normally when the compiler sees a member function call it simply inserts instructions calling the appropriate subroutine (as determined by the type of the pointer or reference)
However, if the function is virtual a member function call such as vp1->Describe() is replaced with following:
(*((vp1->_vtab)[0]))()
The expression vp1->_vtab locates a special "secret" data member of the object pointed to by vp1. This data member is automatically present in all objects with at least one virtual function. It points to a class-specific table of function pointers (known as the classe's vtable)
The expression (vp1->_vtab)[0] locates the first element of the object's class's vtable (the one corresponding to the first virtual function - Describe()). That element is a function pointer to the appropriate Describe() member function.
Finally, the expression (*((vp1->_vtab)[0]))() dereferences the function pointer and calls the function
We can visualize the set-up for vp1, vp2, and vp3 as follows:
The same problem that occurred with the non-virtual version of Vehicle::Describe() also occurs with the Vehicle::~Vehicle() destructor
That is, the following code doesn't call the Car::~Car() destructor, and so the memory allocated to the myMake data member leaks :
Vehicle* vp4 = new Car ("Aston Martin", "JB 007"); // AND LATER... delete vp4;
Why doesn't delete call the correct destructor?
We can force delete to call the destructor corresponding to the type of the object (not that of the pointer), by declaring the Vehicle::~Vehicle() destructor virtual:
class Vehicle { public: Vehicle(char* regnum) : myRegNum(strdup(regnum)) {} virtual ~Vehicle(void) { delete myRegNum; } virtual void Describe(void) { cout << "Unknown vehicle, registration " << myRegNum << endl; } protected: char* myRegNum; }; class Car : public Vehicle { public: Car(char* make, char* regnum) : Vehicle(regnum), myMake( strdup(make) ) {} virtual ~Car(void) { delete myMake; } virtual void Describe(void) { cout << "Car (" << myMake << "), registration " << myRegNum << endl; } protected: char* myMake; }; Vehicle* vp4 = new Car ("Jaguar","XJS 012"); delete vp4; // CALLS Car::~Car() // (WHICH THEN CALLS Vehicle::~Vehicle())