Виртуальные функции (окончание)

   
На этом шаге мы подведем итоги по использованию виртуальных функций.

   
Виртуальными могут быть не любые функции, а только нестатические компонентные функции какого-либо класса.
После того как функция определена как виртуальная, ее повторное определение в производном классе (с тем же самым прототипом)
создает в этом классе новую виртуальную функцию, причем спецификатор virtual может не использоваться.

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

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

   
Сказанное иллюстрирует следующая программа.

//OOР31_1.СРР - некоторые особенности виртуальных функций.
#include <iostream.h>
#include <conio.h>
struct base  {
   virtual void f1(void) { cout << "\nbase::f1"; } 
   virtual void f2(void) { cout << "\nbase::f2"; }
   virtual void f3(void) { cout << "\nbase::f3"; }
}; 
struct dir: public base {
   // Виртуальная функция:
   void f1(void) { cout << "\ndir::f1"; }
   // Ошибка в типе функции:
   // int f2(void) { cout << "\ndir::f2"; }
   // Невиртуальная функция:
   void f3(int i) { cout << "\ndir::f3::i = " << i; } 
};
void main(void) 
{ 
  base B, *pb = &B; 
  dir D, *pd = &D;
  pb->f1();
  pb->f2(); 
  pb->f3();
  pd->f1();
  pd->f2();
  // Ошибка при попытке без параметра вызвать dir::f3(int).
  // pd->f3();
  pd->f3(0) ;
  pb = &D;
  pb->f2(); 
  pb->f3();
  // Ошибочное употребление или параметра, или указателя: 
  // pb->f3(3);
}

Текст этой программы можно взять здесь.

   
Обратите внимание, что три виртуальные функции базового класса по-разному воспринимаются в производном классе.
dir::f1() - виртуальная функция, подменяющая функцию base::f1(). Функция base::f2() наследуется в классе
dir так же, как и функция base::f3(). Функция dir::f3(int) - совершенно новая компонентная функция
производного класса, никак не связанная с базовым классом. Именно поэтому невозможен вызов f3(int) через указатель
на базовый класс. Виртуальные функции base::f2() и base::f3() оказались не переопределенными в
производном классе dir. Поэтому при всех вызовах без параметров f3() используется только
компонентная функция базового класса. Функция dir::f3(int) иллюстрирует соглашение языка о том, что если у
функции производного класса набор параметров отличается от набора параметров соответствующей виртуальной функции
базового класса, то это не виртуальная функция, а новый метод производного класса.

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

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

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

struct base {
      virtual int f(int j) { return j * j; } 
};
struct dir: public base { 
     int f(int i) { return base::f(i * 2); }
};

   
На следующем шаге мы рассмотрим абстрактные классы.



Вы можете оставить комментарий, или Трекбэк с вашего сайта.

Оставить комментарий