Бьерн Страуструп. Язык программирования С++ Второе дополненное издание



Pdf көрінісі
бет120/256
Дата11.07.2022
өлшемі2,87 Mb.
#37591
1   ...   116   117   118   119   120   121   122   123   ...   256
6.2.5 Виртуальные функции 
С помощью виртуальных функций можно преодолеть трудности, возникающие при использовании поля 
типа. В базовом классе описываются функции, которые могут переопределяться в любом производном 
классе. Транслятор и загрузчик обеспечат правильное соответствие между объектами и применяемыми 
к ним функциями: 
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() - это задача транслятора. Тип 
функции указывается в базовом классе и не может быть переопределен в производном классе
Определение виртуальной функции должно даваться для того класса, в котором она была впервые 
описана (если только она не является чисто виртуальной функцией, см. $$6.3). Например: 
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 ($$6.2.2). Напечатать его можно так: 
void employee::print_list() 

for ( employee* p = list; p; p=p->next) p->print(); 

Данные о каждом служащем будут печататься в соответствии с типом записи о нем. Поэтому программа 
int main() 



Бьерн Страуструп.
Язык программирования С++ 
 
156 
employee 
e("J.Brown",1234); 
manager 
m("J.Smith",2,1234); 
employee::print_list(); 

напечатает 
J.Smith 1234 
level 2 
J.Brown 1234 
Обратите внимание, что функция печати будет работать даже в том случае, если функция 
employee_list() была написана и оттранслирована еще до того, как был задуман конкретный 
производный класс manager! Очевидно, что для правильной работы виртуальной функции нужно в 
каждом объекте класса employee хранить некоторую служебную информацию о типе. Как правило, 
реализации в качестве такой информации используют просто указатель. Этот указатель хранится 
только для объектов класса с виртуальными функциями, но не для объектов всех классов, и даже для 
не для всех объектов производных классов. Дополнительная память отводится только для классов, в 
которых описаны виртуальные функции. Заметим, что при использовании поля типа, для него все равно 
нужна дополнительная память. 
Если в вызове функции явно указана операция разрешения области видимости ::, например, в вызове 
manager::print(), то механизм вызова виртуальной функции не действует. Иначе подобный вызов привел 
бы к бесконечной рекурсии. Уточнение имени функции дает еще один положительный эффект: если 
виртуальная функция является подстановкой (в этом нет ничего необычного), то в вызове с операцией 
:: происходит подстановка тела функции. Это эффективный способ вызова, который можно применять в 
важных случаях, когда одна виртуальная функция обращается к другой с одним и тем же объектом. 
Пример такого случая - вызов функции manager::print(). Поскольку тип объекта явно задается в самом 
вызове manager::print(), нет нужды определять его в динамике для функции employee::print(), которая и 
будет вызываться. 


Достарыңызбен бөлісу:
1   ...   116   117   118   119   120   121   122   123   ...   256




©emirsaba.org 2024
әкімшілігінің қараңыз

    Басты бет