Теоретические сведения для студентов специальностей «Экономика и организация производства», страница 24

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

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

3) конструктор не может быть виртуальной функцией, хотя деструктор можно объявить со спецификатором virtual.

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

Использование виртуальных функций.

Обычно при проектировании программы общие атрибуты иерархических классов представляются в определении базовых классов, частные - в особых производных классах.

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

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

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

Рассмотрим демонстрационный пример использования виртуальных функций:

struct B {

virtual void vf1();

          virtual void vf2();

          virtual void vf3();

          void f();}

class D: public B {

virtual void vf1();

          //виртуальная ф-я, спецификатор virtual допустим, но избыточен

          void vf2(int);

          //другой список аргументов, функция не является виртуальной

          char vf3();//ошибка, т.к. изменяется только тип возвращаемого значения,

//что недопустимо

          void f();};                        //обычный метод класса D

          void main (void) { B b, *bp;       //объект и указатель класса B

          bp=&b;                                      //присвоение указателю адреса объекта

          bp->vf1();                        //вызывается B::f1();

          D d;                                 //объявлен объект d класса D

          bp=&d;        //присвоение указателю на базовый класс B адреса объекта

//производного класса d;

          bp->vf1();                        //вызывается D::vf1();

          bp->vf2();                        //вызывается B::vf2();, т.к. функция

          //vf2(int) из D имеет другой список аргументов и не является

//виртуальной функцией;

          bp->f(); }      //вызов B::f() - не виртуального метода из класса B, так как

//невиртуальные методы класса D для указателя из класса B неизвестны.

Указатель "видит" только имена методов, которые объявлены в его собственном классе (классе, в котором объявлен указатель). Переопределенная функция vf1() в классе D автоматически становится виртуальной. Используемый для vf1() спецификатор virtual, вообще говоря, является избыточным.

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

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

Тема 9. АБСТРАКТНЫЕ КЛАССЫ. Множественное наследование

АБСТРАКТНЫЕ КЛАССЫ

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