Файл: Pobegaylo_A._C_Cplus_dlya_studenta.pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 13.12.2020

Просмотров: 4166

Скачиваний: 28

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

Глава 17. Наследование классов 

231 

int main() 

  Derived d; 
  Base *b = &d; 
  cout << b->what() << endl;  // печатает: 20 
  Base &c = d; 
  cout << c.what() << endl;   // печатает: 20 
 
  return 0; 

Повторять ключевое слово 

virtual

 в производном классе при за-

мещении функции базового класса не обязательно. Но лучше по-

вторить, чтобы не просматривать иерархию интерфейсов. 
Функция,  объявленная  в  базовом  классе  со  спецификатором 

virtual

,  а  также  все  функции,  замещающие  ее  в  производных 

классах,  называются 

виртуальными

.  Виртуальная  функция  не 

может быть статическим членом класса. 
Если виртуальные функции базового и производного классов от-

личаются только типом возвращаемого значения, то компилятор 

выдаст ошибку. Например: 

  struct  Base 
  { 
    virtual int what() { return 1; } 
  }; 
  struct  Derived: Base 
  { 
    virtual double what() { return 2.2; } // ошибка компиляции 
  }; 

Но при этом следует учитывать, что указатель и ссылка на базо-

вый класс совместимы по типам с указателем и ссылкой на про-

изводный  класс  соответственно.  Поэтому  виртуальная  функция 

производного класса может возвращать соответственно указатель 

или  ссылку  на  класс,  который  является  производным  от  класса, 

указатель или ссылку на который возвращает замещаемая вирту-

альная функция базового класса. 
Если  виртуальная  функция  производного  класса  имеет  такое  же 
имя, как и виртуальная функция базового класса, но другую сиг-


background image

Часть II. Язык программирования С++ 

232 

натуру,  то  виртуальная  функция  в  производном  классе  просто 
скрывает  соответствующую  виртуальную  функцию  в  базовом 
классе. 
В листинге 17.8 приведен пример сокрытия виртуальной функции 
базового класса. 

Листинг 17.8. Сокрытие виртуальной функции базового класса 

#include <iostream> 
using namespace std; 
 
class Base 

public: 
  virtual int what(int n) { return n + 10; } 
}; 
class Derived: public Base 

public: 
  virtual double what(double d) { return d + 20; } 
}; 
 
int main() 

  Derived d; 
  Base *b = &d; 
 
  cout << b->what(5) << endl;   // 15 
  cout << d.what(5.5) << endl;  // 25.5 
 
  return 0; 

Механизм замещения виртуальных функций можно обойти, если 
при  вызове  виртуальной  функции  указать  имя  класса,  которому 
она принадлежит. 
В  листинге  17.9  приведен  пример  вызова  виртуальной  функции 
базового класса через указатель на этот класс. 


background image

Глава 17. Наследование классов 

233 

Листинг 17.9. Обход механизма виртуальных функций 

#include <iostream> 
using namespace std; 
 
class Base 

public: 
  virtual int what() { return 10; } 
}; 
class Derived: public Base 

public: 
  virtual int what() { return 20; } 
}; 
 
int main() 

  Base *b = new Derived; 
  cout << b->Base::what() << endl;  // 10 
 
  return 0; 

17.6. Полиморфизм  

и позднее связывание 

Возможность  вызова  виртуальных  функций  производных  клас- 
сов через указатель или ссылку на базовый класс называется 

по-

лиморфизмом  подтипов

  (subtyping  polymorphism),  или 

вклю-

чающим 

(inclusion) 

полиморфизмом

 , или

 

просто 

полиморфизмом

Класс, который содержит  или  наследует  виртуальную  функцию, 
называется 

полиморфным классом

Термин 

позднее

,

 

или

 отложенное

,

 

или

 динамическое связывание

 

(dynamic binding) относится к тому обстоятельству, что компиля-
тор не может определить, какая виртуальная функция должна вы-
зываться, если к ней обращаются через указатель или ссылку на 
базовый класс. Эта особенность виртуальных функций приводит 


background image

Часть II. Язык программирования С++ 

234 

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

 

для каждого полиморфного класса компилятор строит таблицу 
адресов виртуальных функций (vtable); 

 

каждый объект  полиморфного  класса  содержит  скрытый  ука-
затель (vptr) на таблицу адресов виртуальных функций; 

 

компилятор  автоматически  вставляет  в  начало  конструктора 
полиморфного класса фрагмент кода, который инициализиру-
ет vptr; 

 

при вызове виртуальной функции ее адрес извлекается из таб-
лицы vtable, на которую указывает vptr объекта. 

17.7. Передача  
аргументов по умолчанию  
в виртуальные функции 

Механизм  виртуальных  функций  не  поддерживает  передачу  ар-
гументов по умолчанию в такие функции. То есть, если функция 
производного  класса  вызывается  через  указатель  или  ссылку  на 
базовый класс, то ей передаются аргументы по умолчанию, ука-
занные в базовом классе. Это обусловлено тем, что аргументы по 
умолчанию определяются на этапе компиляции. 
В листинге 17.10 приведен пример передачи аргументов по умол-
чанию в виртуальную функцию. 

Листинг 17.10. Передача аргументов по умолчанию  
в виртуальную функцию 

#include <iostream> 
using namespace std; 
 
class Base 

public: 


background image

Глава 17. Наследование классов 

235 

  virtual int what(int n = 10) { return n; } 
}; 
class Derived: public Base 

public: 
  virtual int what(int n = 20) { return n; } 
}; 
 
int main() 

  Base* b = new Derived; 
  cout << b->what() << endl;  // печатает: 10 
  return 0; 

17.8. Виртуальные деструкторы 

Так как объекты производных классов могут уничтожаться через 
указатели  на  базовые  классы  при  помощи  оператора 

delete

,  то 

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

Листинг 17.11. Вызов виртуальных деструкторов 

#include <iostream> 
using namespace std; 
 
class Base 

public: 
  virtual ~Base() { cout << "~Base" << endl; } 
}; 


Смотрите также файлы