A “virtual function” is a member function that you expect to be redefined in derived classes. When you call a virtual function through a pointer to a base class, the derived class's version of the function is executed. This is precisely the opposite behavior of ordinary member functions.
A virtual function is declared by placing the keyword virtual before the declaration of the member function in the base class. The virtual keyword is not necessary in the declarations in the derived classes; all subsequent versions of a virtual function are implicitly declared virtual. For example, here is a revised version of the employee class hierarchy, having a virtual computePay function:
class Employee
{
public:
Employee( const char *nm );
char *getName() const;
virtual float computePay() const;
virtual ~Employee() {}
private:
char name[30];
};
class WageEmployee : public Employee
{
public:
WageEmployee( const char *nm );
void setWage( float wg );
void setHours( float hrs );
float computePay() const; // Implicitly virtual
private:
float wage;
float hours;
};
class SalesPerson : public WageEmployee
{
public:
SalesPerson( const char *nm);
void setCommission( float comm );
void setSales( float sales );
float computePay() const; // Implicitly virtual
private:
float commission;
float salesMade;
};
class Manager : public Employee
{
public:
Manager( const char *nm );
void setSalary( float salary );
float computePay() const; // Implicitly virtual
private:
float weeklySalary;
};
The definitions of each class's version of computePay do not have to be modified. However, since computePay has been added to the base class, a definition for that version of the function is needed:
float Employee::computePay() const
{
cout << "No salary computation defined\n";
return 0.0;
}
This function is needed primarily as a placeholder. It would be called if a plain Employee object were used, or if one of the derived classes did not provide its own definition of computePay.
Now consider what happens when computePay is called through an Employee pointer:
Employee *empPtr;
float salary;
empPtr= &aWorker;
salary = empPtr->computePay(); // Call WageEmployee::computePay
empPtr = &aSeller;
salary = empPtr->computePay(); // Call SalesPerson::computePay
empPtr = &aBoss;
salary = empPtr->computePay(); // Call Manager::computePay
If computePay hadn't been declared virtual, each statement would call
Employee::computePay, which would return 0.0. However, since computePay is
a virtual function, the function executed is different for each call, even though the calls are exactly the same. The function called is the one appropriate for the actual object that empPtr points to. (You can also use the scope resolution operator to explicitly specify a different version of the function if you want.)
To calculate the weekly payroll for a department, you can write a function like the following:
float computePayroll( EmployeeList &dept )
{
float payroll = 0;
Employee *person;
EmpIter anIter( dept );
person = anIter.getFirst();
payroll += person->computePay();
while( person = anIter.getNext() )
{
// Call appropriate function
// for each type of employee
payroll += person->computePay();
}
return payroll;
}
The statement person->computePay executes the appropriate function, no matter what type of employee person points to.