Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
c++book1.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
2 Мб
Скачать
☆

7.2.8 Виртуальные Функции

Виртуальные функции преодолевают сложности решения с по­мощью полей типа, позволяя программисту описывать в базовом классе функции, которые можно переопределять в любом произ­водном классе. Компилятор и загрузчик обеспечивают правильное соответствие между объектами и применяемыми к ним функциями. Например:

struct employee (*

employee* next;

char* name;

short department;

// ...

virtual void print();

*);

Ключевое слово virtual указывает, что могут быть различ­ные варианты функции print() для разных производных классов, и что поиск среди них подходящей для каждого вызова print() является задачей компилятора. Тип функции описывается в базо­вом классе и не может переописываться в производном классе. Виртуальная функция должна быть определена для класса, в ко­тором она описана впервые. Например:

void employee::print()

(*

cout << e->name << "\t" << e->department << "\n";

// ...

*)

Виртуальная функция может, таким образом, использоваться даже в том случае, когда нет производных классов от ее клас­са, и в производном классе, в котором не нужен специальный вариант виртуальной функции, ее задавать не обязательно. Просто при выводе класса соответствующая функция задается в том случае, если она нужна. Например:

struct manager : employee (*

employee* group;

short level;

// ...

void print();

*);

void manager::print()

(*

employee::print();

cout << "\tуровень" << level << "\n";

// ...

*)

Функция print_employee() теперь не нужна, поскольку ее место заняли функции члены print(), и теперь со списком слу­жащих можно работать так:

void f(employee* ll)

(*

for (; ll; ll=ll->next) ll->print();

*)

Каждый служащий будет печататься в соответствии с его типом. Например:

main()

(*

employee e;

e.name = "Дж.Браун";

e.department = 1234;

e.next = 0;

manager m;

m.name = "Дж.Смит";

e.department = 1234;

m.level = 2;

m.next = &e;

f(&m);

*)

выдаст

Дж.Смит 1234 уровень 2

Дж.Браун 1234

Заметьте, что это будет работать даже в том случае, если f() была написана и откомпилирована еще до того, как произ­водный класс manager был задуман! Очевидно, при реализации этого в каждом объекте класса employee сохраняется некоторая информация о типе. Занимаемого для этого пространства (в те­кущей реализации) как раз хватает для хранения указателя. Это пространство занимается только в объектах классов с виртуаль­ными функциями, а не во всех объектах классов и даже не во всех объектах производных классов. Вы платите эту пошлину только за те классы, для которых описали виртуальные функции.

Вызов функции с помощью операции разрешения области ви­димости ::, как это делается в manager::print(), гарантирует, что механизм виртуальных функций применяться не будет. Иначе manager::print() подвергалось бы бесконечной рекурсии. Приме­нение уточненного имени имеет еще один эффект, который может оказаться полезным: если описанная как virtual функция описа­на еще и как inline (в чем ничего необычного нет), то там, где в вызове применяется ::, может применяться inline-подста­новка. Это дает программисту эффективный способ справляться с теми важными специальными случаями, когда одна виртуальная функция вызывает другую для того же объекта. Поскольку тип объекта был определен при вызове первой виртуальной функции, обычно его не надо снова динамически определять другом вызове для того же объекта.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]