С помощью виртуальных функций можно преодолеть трудности, возникающие при использовании поля типа. В базовом классе описываются функции, которые могут переопределяться в любом производном классе. Транслятор и загрузчик обеспечат правильное соответствие между объектами и применяемыми к ним функциями:
class employee { char* name; short department; // ... employee* next; static employee* list; public: employee(char* n, int d); // ... static void print_list(); virtual void print() const;
};
Служебное слово virtual (виртуальная) показывает, что функция print() может иметь разные версии в разных производных классах, а выбор нужной версии при вызове print() - это задача транслятора. Тип функции указывается в базовом классе и не может быть переопределен в производном классе. Определение виртуальной функции должно даваться для того класса, в котором она была впервые описана (если только она не является чисто виртуальной функцией). Например:
void employee::print() const { cout << name << '\t' << department << '\n'; // ... }
Мы видим, что виртуальную функцию можно использовать, даже если нет производных классов от ее класса. В производном же классе не обязательно переопределять виртуальную функцию, если она там не нужна. При построении производного класса надо определять только те функции, которые в нем действительно нужны:
class manager : public employee { employee* group; short level; // ... public: manager(char* n, int d); // ... void print() const; };
Место функции print_employee() заняли функции-члены print(), и она стала не нужна. Список служащих строит конструктор employee. Напечатать его можно так:
void employee::print_list() { for ( employee* p = list; p; p=p->next) p->print(); }
Данные о каждом служащем будут печататься в соответствии с типом записи о нем. Поэтому программа
int main() { employee e("J.Brown",1234); manager m("J.Smith",2,1234); employee::print_list(); }
напечатает
J.Smith 1234 level 2 J.Brown 1234