
- •Void main (){
- •Операции, определенные по умолчанию над структурированными объектами
- •Void binar(unsigned char ch)
- •Void main() { int k;
- •Расширение действия (перегрузка) стандартных операций
- •Доступ к компонентам структурированного объекта
- •Void ff(cl1 cl,cl2 c2) { тело_функции }
- •Void f1(...);
- •Void f2(...);
- •Классы и шаблоны
- •Int size; // Количество элементов в массиве
- •Vector(int); // Конструктор класса vector
- •Имя_параиетризованного_класса
- •Int length;
- •Void main () {
- •Наследование и другие возможности классов Наследование классов
- •2. Множественное наследование и виртуальные базовые классы
- •Void show ()
- •Void hide()
- •Void riesquare(void)
- •Void show()
- •Void hide()
- •Void show(void)
- •Void hide()
- •Void main()
- •Void show() // Изобразить на экране эллипс
- •Void hide() { int cc, bk;
- •Int min(int valuel, int value2)
- •Void show()
- •Void hide() // Убрать изображение с экрана дисплея
- •Void main()
- •3. Виртуальные функции и абстрактные классы
- •Void main (void)
- •Имя_проиаводного_класса: : show ()
- •Иня_объекта_производноро_класса. Show ()
- •Void main(void)
- •Void sos (int) ;
- •Void func(char);
- •Void sos (int) ;
- •Void chain::showAll(void) // Изображение элементов списка
- •Void main()
- •4. Локальные классы
- •Void showSeg() // Изобразить отрезок на экране
- •Void showSquare(void) // изобразить квадрат
- •5. Классы и шаблоны
- •Int size; // Количество элементов в массиве
- •Vector(int); // Конструктор класса vector
- •Имя_параиетризованного_класса
3. Виртуальные функции и абстрактные классы
К механизму виртуальных функций обращаются в тех случаях, когда в базовый класс необходимо поместить функцию, которая должна по-разному выполняться в производных классах. Точнее, по-разному должна выполняться не единственная функция из базового класса, а в каждом производном классе требуется свой вариант этой функции.
Например, базовый класс может описывать фигуру на экране без конкретизации ее вида, а производные классы (треугольник, эллипс и г.п.) однозначно определяют ее формы и размеры. Если в базовом классе ввести функцию для изображения фигуры на экране, то выполнение этой функции будет возможно только для объектов каждого из производных классов, определяющих конкретные изображения.
До объяснения возможностей виртуальных функций отметим, что классы, включающие такие функции, играют особую роль в объектно-ориентированном программировании. Именно поэтому они носят специальное название - полиморфные.
Рассмотрим теперь, как ведут себя при наследовании невиртуальные компонентные функции с одинаковыми именами, типами и сигнатурами параметров.
Если в базовом классе определена некоторая компонентная функция, то такая же функция (с тем же именем, того же типа и с тем же набором и типами параметров) может быть введена в производном классе. Рассмотрим следующее определение классов:
//BASE.DIR - определения базового и производного классов
struct base {
void fun(int i) { cout << "\nbase::i = " << i; }
};
struct dir: public base{
void fun (int i){ cout << "\ndir::i - " << i; }
};
В данном случае внешне одинаковые функции void fun (int) определены в базовом классе base и в производном классе dir.
В теле класса dir обращение к функции fun (), принадлежащей классу base, может быть выполнено с помощью полного квалифицированного имени, явно включающего имя базового класса: base::fun(). При обращении в классе dir к такой же (по внешнему виду) функции, принадлежащей классу dir, достаточно использовать имя fun () без предшествующего квалификатора.
В программе, где определены и доступны оба класса base и dir, обращения к функциям fun () могут быть выполнены с помощью указателей на объекты соответствующих классов:
//Р10-05.СРР - одинаковые функции в базовом и производном
// классах
#include <iostream.h>
#include "base.dir" // Определения классов
Void main (void)
{
base B, *bp=&B;
dir D, *dp = &D;
base *pbd = &D;
bp->fun(1); // Печатает : base::i = 1
dp->fun(5); // Печатает : dir::i = 5
pbd->fun(4); // Печатает : base::i = 4
}
Результаты выполнения программы:
base::i = 1 dir::i = 5 base::i = 4
В программе введены три указателя на объекты разных классов. Следует обратить внимание на инициализацию указателя pbd. В ней адрес объекта производного класса (объекта D) присваивается указателю на объект его прямого базового класса (base *). При этом выполняется стандартное преобразование указателей, предусмотренное синтаксисом языка Си++. Обратное преобразование, т.е. преобразование указателя на объект базового класса в указатель на объект производного класса, невозможно (запрещено синтаксисом). Обращения к функциям классов base и dir с помощью указателей Ьр и dp не представляют особого интереса. Вызов pbd->fun() требуется прокомментировать. Указатель pbd имеет тип base*, однако его значение - адрес объекта D класса dir.
Какая же из функций base: :fun() или dir: :fun() вызывается при обращении pbd->fun()? Результат выполнения программы показывает, что вызывается функция из базового класса. Именно такой вызов предусмотрен синтаксисом языка Си++, т.е. выбор функции (не виртуальной) зависит только от типа указателя, но не от его значения. "Настроив" указатель базового класса на объект производного класса, не удается с помощью этого указателя вызвать функцию из производного класса.
Вернемся к упомянутому выше примеру с фигурой в виде базового класса с названием figure. Пусть в этом классе определена компонентная функция void show (). Так как внешний вид фигуры в базовом классе еще не определен, то в каждый из производных классов нужно включить свою функцию void show () для формирования изображения на экране. Если оставаться в рамках проиллюстрированного в примере с классами base и dir механизма, то доступ к функции show () производного класса возможен только с помощью явного указания области видимости: