Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Проверка ошибок при использовании виртуальных функций
В некоторых объектно-ориентированных языках, которые поддерживают позднее связывание, проверка ошибочных сообщений осуществляется на этапе выполнения программы. С++, посредством виртуальных функций, поддерживает и статический контроль сообщений, и позднее связывание. class P { public: P(void){} virtual void hello(void) {} }; class C: public P { public: C(void) {} virtual void hello(void) { cout < < “Hello world”; } }; void main() { P *p; C c; p=& c; p-> hello(“Hello”); // ошибка } Компилятор выдаст ошибку на строке p-> hello(“hello”). Компилятор может определить, что виртуальная функция hello не имеет параметров, даже если сообщение связывается с методом hello класса C во время выполнения программы. Виртуальные функции, определенные в родительском или потомственных классе имеют одинаковые списки параметров, так как позднее связывание не оказывает влияния на контроль типов параметров. Техническая реализация виртуальных функций Объект С++ представляет собой непрерывный участок памяти. Указатель на такой объект содержит начальный адрес этого участка. Когда вызывается функция-член (объекту посылается сообщение), этот вызов транслируется в обычный вызов функции с дополнительным аргументом, который содержит указатель на объект. Например, ClassName *object; object-> message(10); преобразуется в ClassName_message(object, 10); При создании объектов производных классов их поля сцепляются с полями родительских классов. Эту функцию выполняет компилятор. Виртуальные функции реализованы с использованием таблиц функций. Рассмотрим следующие классы. class P { int value; public: virtual int method1(float r): virtual void method2(void); virtual float method3(char *s); }; class C1: public P { public: void method2(void); }; class C2: public C1 { public: float method3(char *s); }; Таблица виртуальных функций, virtualTabl, содержит функции-члены каждого класса в полиморфическом кластере. Указатель на эту таблицу имеют все объекты классов и подклассов полиморфического кластера. Типичный объект, приведенных выше классов, выглядит, примерно, так: int value; virtual_table-> P:: method1 C1:: method2 C2:: method3 Компилятор преобразует вызов виртуальной функции в косвенный вызов через virtualTable. Например, С2 *с; c-> method3(“Hello”); преобразуется в (*(c-> virtual_table[2]))(c, ”Hello”); Абстрактные базовые классы Базовый класс иерархии типа обычно содержит ряд виртуальных функций, которые обеспечивают динамическую типизацию. Часто в базовом классе эти виртуальные функции фиктивны и имеют пустое тело. Определенное значение им придают в порожденных классах. В С++ для этой цели применяется чистая виртуальная функция. Чистая виртуальная функция - виртуальная функция-член, тело которой обычно не определено. Запись этого объявления внутри класса следующая: virtual прототип функции = 0; Чистая виртуальная функция используется для того, чтобы “отложить” решение о реализации функции. В ООП терминологии это называется отсроченным методом. Класс, имеющий, по крайней мере, одну чистую виртуальную функцию - абстрактный класс. Для иерархии типа полезно иметь базовый абстрактный класс. Он содержит общий базовые свойства порожденных классов, но сам по себе не может использоваться для объявления объектов. Напротив, он используется для объявления указателей, которые могут обращаться к объектам подтипа, порожденным от абстрактного класса. Объясним эту концепцию при помощи разработки примитивной формы экологического моделирования. В нашем примере будем иметь различные формы взаимодействия жизни с использованием абстрактного базового класса living. Создадим fox (лису) как типичного хищника, и rabbit (кролика ) как его жертву. Rabbit есть grass (траву).
const int N = 40, STATES=4; // размер квадратной площади enum state { EMPTY, GRASS, RABBIT, FOX };
class living; typedef living *world[N][N]; // world будет моделью
class living { // что живет в мире protected: int row, column; // местоположение void sums(world, int sm[]); // sm[#states] используется next public: living(int r, int c): row(r), column(c) {} virtual state who()=0; // идентификация состояний virtual living* next(world w)=0; // расчет next }; void living:: sums(world w, int sm[]) { sm[EMPTY]=sm[GRASS]=sm[RABBIT]=sm[FOX]=0; for(int i=-1; i < = 1; i++) for(int j=-1; j < = 1; j++) sm[w[row+i][column+j]-> who()]++; }
// текущий класс - только хищники class fox: public living { protected: int age; // используется для принятия решения о смерти public: fox(int r, int c, int a=0): living(r, c), age(a) {} state who() { return FOX; } // отложенный метод для FOX living* next(world w); // отложенный метод для FOX }; // текущий класс - только жертвы class rabbit: public living { protected: int age; // используется для принятия решения о смерти public: rabbit(int r, int c, int a=0): living(r, c), age(a) {} state who() { return RABBIT; } // отложенный метод для RABBIT living* next(world w); // отложенный метод для RABBIT }; // текущий класс - только растения class grass: public living { public: grass(int r, int c): living(r, c) {} state who() { return GRASS; } // отложенный метод для GRASS living* next(world w); // отложенный метод для GRASS }; // жизнь отсутствует class empty: public living { public: empty(int r, int c): living(r, c) {} state who() { return EMPTY; } // отложенный метод для EMPTY living* next(world w); // отложенный метод для EMPTY }; Обратите внимание на то, что проект позволяет развивать и другие формы хищника, жертвы и растительной жизни, используя следующий уровень наследования. Характеристики поведения каждой формы жизни фиксируется в версии next(). Living* grass:: next(world w) { int sum[STATES]; sums(w, sum); if(sum[GRASS] > sum[RABBIT) // есть траву return (new grass(row, column)); else return (new empty(row, column)); } Grass может быть съеден Rabbit. Если в окрестности имеется больше grass, чем rabbit, grass остается, иначе - grass будет съедена. Преимущества позднего связывания В С++ преимущество различной реакции объекта на сообщение можно получить в полиморфическом кластере. Можно создать коллекцию ссылок или указателей на объекты. Эти ссылки(указатели) должны быть объявлены как ссылки или указатели на объекты базового класса или одного из производных классов в полиморфическом кластере. Любая попытка загрузить указатель или ссылку на объект, не входящий в полиморфический кластер, закончится неудачей (компилятор выдаст сообщение о несоответствии типов). Ссылки и указатели на объекты базового класса совместимы только с ссылками или указателями на объекты public-производного класса. В языках поддерживающих только раннее связывание ответственность за вызов соответствующей версии функции лежит на программисте. Обычно для выполнения такого контроля, используются операторы множественного выбора, такие как операторы switch, if else. Каждый дополнительный выбор, вводимый в систему, необходимо включить во все операторы множественного выбора. При этом программа изменяется во многих местах.. В языках, поддерживающих позднее связывание, как С++, при необходимости внести дополнение в программу, создаются новые производные классы. Дополнительные возможности реализуются через виртуальные функции этих классов. Такая возможность позволяет минимизировать модификацию программы. Виртуальные деструкторы
С++ позволяет об’явить деструктор виртуальным, так же как и обычную член-функцию. Тогда деструкторы всех классов, порожденных из класса, в котором об’явлен виртуальный деструктор, так же будут виртуальными. Использование виртуальных деструкторов позволяет обеспечить вызов соответствующего дестрктора при разрушении об’ектов оператором delete, даже если тип разрушаемого об’екта неизвестен на стадии компиляции..
class Figure { public: Figure(); virtual ~Figure(); }; typedef Figure *PFigure; class Circle: public Figure { public: Circle(int centerx, int centery, int radius); virtual ~Circle(); }; class Rectangle: public Figure { public: Rectangle(int left, int top, int right, int bottom); ~Rectangle(); }; int main(); { const ALLFigures = 2; PFigures figures[ALLFigures]; figures[0]=new Circle(100, 100, 10); // массив указателей на фигуры figures[1]=new Rectangle(100, 100, 200, 300); for(int count=0; count< ALLFigures; count++) delete figures[count]; // уничтожение массива }
Задания для самостоятельной работы Вариант 1. Создать абстрактный базовый класс с виртуальной функцией - площадь. Создать производные классы: прямоугольник, круг, прямоугольный треугольник, трапеция со своими функциями площади. Для проверки определить массив ссылок на абстрактный класс, которым присваиваются адреса различных объектов. Площадь трапеции: S=(a+b)h/2 Задание 2. Создать класс - данные - абстрактный базовый класс. Создать производные классы - данные типа сигнал, данные типа результат обработки и вспомогательные данные. Все данные имеют функции отображения, сохранения и обработки. Вариант 2. Создать абстрактный класс с виртуальной функцией: норма. Создать производные классы: комплексные числа, вектор из 10 элементов, матрица (2х2). Определить функцию нормы - для комплексных чисел - модуль в квадрате, для вектора - корень квадратный из суммы элементов по модулю, для матрицы - максимальное значение по модулю. Задание 2 Создать класс - данные - абстрактный базовый класс. Создать производные классы - данные типа сигнал, данные типа результат обработки и вспомогательные данные. Все данные имеют функции отображения, сохранения и обработки. . Вариант 3. Создать абстрактный класс (кривые) вычисления координаты y для некоторой x. Создать производные классы: прямая, эллипс, гипербола со своими функциями вычисления y в зависимости от входного параметра x. Уравнение прямой: y=ax+b, эллипса: x2/a2+y2/b2=1, гиперболы: x2/a2-y2/b2=1 Задание 2. Создать класс - данные - абстрактный базовый класс. Создать производные классы - данные типа сигнал, данные типа результат обработки и вспомогательные данные. Все данные имеют функции отображения, сохранения и обработки. Вариант 4. Создать абстрактный базовый класс с виртуальной функцией - сумма прогрессии. Создать производные классы: арифметическая прогрессия и геометрическая прогрессия. Каждый класс имеет два поля типа double. Первое - первый член прогрессии, второе (double) - постоянная разность (для арифметической) и постоянное отношение (для геометрической). Определить функцию вычисления суммы, где параметром является количество элементов прогрессии. Арифметическая прогрессия aj=a0+jd, j=0, 1, 2, … Сумма арифметической прогрессии: sn=(n+1)(a0+an)/2 Геометрическая прогрессия: aj=a0rj, j=0, 1, 2, … Сумма геометрической прогрессии: sn=(a0-anr)/(1-r) Задание 2. Создать класс - данные - абстрактный базовый класс. Создать производные классы - данные типа сигнал, данные типа результат обработки и вспомогательные данные. Все данные имеют функции отображения, сохранения и обработки. Вариант 5. Создать базовый класс список. Реализовать на базе списка стек и очередь с виртуальными функциями вставки и вытаскивания. Задание 2. Создать класс - данные - абстрактный базовый класс. Создать производные классы - данные типа сигнал, данные типа результат обработки и вспомогательные данные. Все данные имеют функции отображения, сохранения и обработки. Вариант 6. Создать базовый класс - фигура, и производные класс - круг, прямоугольник, трапеция. Определить виртуальные функции площадь, периметр и вывод на печать. Задание 2. Создать класс - данные - абстрактный базовый класс. Создать производные классы - данные типа сигнал, данные типа результат обработки и вспомогательные данные. Все данные имеют функции отображения, сохранения и обработки. Вариант 7. Создать базовый класс - работник и производные классы - служащий с почасовой оплатой, служащий в штате и служащий с процентной ставкой. Определить функцию начисления зарплаты. Задание 2. Создать класс - данные - абстрактный базовый класс. Создать производные классы - данные типа сигнал, данные типа результат обработки и вспомогательные данные. Все данные имеют функции отображения, сохранения и обработки. Вариант 8. Создать абстрактный базовый класс с виртуальной функцией - площадь поверхности. Создать производные классы: параллелепипед, тетраэдр, шар со своими функциями площади поверхности. Для проверки определить массив ссылок на абстрактный класс, которым присваиваются адреса различных объектов. Площадь поверхности параллелепипеда: S=6xy. Площадь поверхности шара: Sp=4 r2. Площадь поверхности тетраэдра: S=aÖ 2 3 Задание 2. Создать класс - данные - абстрактный базовый класс. Создать производные классы - данные типа сигнал, данные типа результат обработки и вспомогательные данные. Все данные имеют функции отображения, сохранения и обработки. Вариант 9. Создать класс человек, производные от которого девушки и молодые люди. Определить виртуальную функцию реакции человека на вновь увиденного другого человека. Задание 2. Создать класс - данные - абстрактный базовый класс. Создать производные классы - данные типа сигнал, данные типа результат обработки и вспомогательные данные. Все данные имеют функции отображения, сохранения и обработки. Вариант 10. Создать абстрактный базовый класс с виртуальной функцией - объем. Создать производные классы: параллелепипед, пирамида, тетраэдр, шар со своими функциями объема. Для проверки определить массив ссылок на абстрактный класс, которым присваиваются адреса различных объектов. Объем параллелепипеда - V=xyz (x, y, z - стороны, пирамиды: V=xyh (x, y, - стороны, h - высота), тетраэдра: V= aÖ 3 2/12, шара: Vp=4 r3/3. Задание 2. Создать класс - данные - абстрактный базовый класс. Создать производные классы - данные типа сигнал, данные типа результат обработки и вспомогательные данные. Все данные имеют функции отображения, сохранения и обработки. Вариант 11. Создать абстрактный класс - млекопитающие. Определить производные классы - животные и люди. У животных определить производные классы собак и коров. Определить виртуальные функции описания человека, собаки и коровы. Задание 2. Создать класс - данные - абстрактный базовый класс. Создать производные классы - данные типа сигнал, данные типа результат обработки и вспомогательные данные. Все данные имеют функции отображения, сохранения и обработки. Вариант 12. Создать базовый класс - Предок, у которого есть имя. определить виртуальную функцию печати. Создать производный класс Ребенок, у которого функция печати дополнительно выводит имя. Создать производный класс от последнего класса - Внук, у которого есть отчество. Написать свою функцию печати. Задание 2. Создать класс - данные - абстрактный базовый класс. Создать производные классы - данные типа сигнал, данные типа результат обработки и вспомогательные данные. Все данные имеют функции отображения, сохранения и обработки. Вариант 13. Создать класс живущих с местоположением. Определить наследуемые классы - лиса, кролик и трава. Лиса ест кролика. Кролик ест траву. Лиса может умереть - определен возраст. Кролик тоже может умереть. Кроме этого определен класс - отсутствие жизни. Если в окрестности имеется больше травы, чем кроликов, то трава остается, иначе трава съедена. Если лис слишком старый он может умереть. Если лис слишком много (больше 5 в окрестности), лисы больше не появляются. Если кроликов меньше лис, то лис ест кролика. Задание 2. Создать класс - данные - абстрактный базовый класс. Создать производные классы - данные типа сигнал, данные типа результат обработки и вспомогательные данные. Все данные имеют функции отображения, сохранения и обработки. Вариант 14. Создать абстрактный базовый класс с виртуальной функцией - корни уравнения. Создать производные классы: класс линейных уравнений и класс квадратных уравнений. Определить функцию вычисления корней уравнений. Задание 2. Создать класс - данные - абстрактный базовый класс. Создать производные классы - данные типа сигнал, данные типа результат обработки и вспомогательные данные. Все данные имеют функции отображения, сохранения и обработки. Лабораторная работа №8 " Производные классы: множественное наследование" Цель: Научиться создавать производные классы от нескольких родительских классов. Использовать множественное наследование в своих программах. Популярное:
|
Последнее изменение этой страницы: 2016-07-14; Просмотров: 985; Нарушение авторского права страницы