КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Void main(void). bp->vfun(l); // Печатает: base::i = 1
{ base B, *bp = &В; dir1 D1, *dp1 = &D1; dir2 D2, *dp2 = &D2; bp->vfun(l); // Печатает: base::i = 1 dpl->vfun(2); // Печатает: dirl::i = 2 dp2->vfun(3); // Печатает: dir2::i = 3 bp = &D1; bp->vfun(4); // Печатает:dir1::i = 4 bp = &D2; bp->vfun(5); // Печатает: dir2::i = 5 } Результат выполнения программы: base::i = 1 dirl::i = 2 dir2::i = 3 dirl::i = 4 dir2::i = 5 В примере надо обратить внимание на доступ к функциям vfun () через указатель bр на базовый класс. Когда bр принимает значение адреса объекта класса base, то вызывается функция из базового класса. Затем bр последовательно присваиваются значения ссылок на объекты производных классов &Dl, &D2, и выбор соответствующего экземпляра функции vfun () каждый раз определяется именно объектом. Таким образом, интерпретация каждого вызова виртуальной функции через указатель на базовый класс зависит от значения этого указателя, т.е. от типа объекта, для которого выполняется вызов. В противоположность этому интерпретация вызова через указатель невиртуальной функции зависит только от типа указателя (это было показано в предыдущем примере с функцией fun()). Виртуальными могут быть не любые функции, а только нестатические компонентные функции какого-либо класса. После того как функция определена как виртуальная, ее повторное определение в производном классе (с тем же самым прототипом) создает в этом классе новую виртуальную функцию, причем спецификатор virtual может не использоваться. В производном классе нельзя определять функцию с тем же именем и с той же сигнатурой параметров, но с другим типом возвращаемого значения, чем у виртуальной функции базового класса. Это приводит к ошибке на этапе компиляции. Если в производном классе ввести функцию с тем же именем и типом возвращаемого значения, что и виртуальная функция базового класса, но с другой сигнатурой параметров, то эта функция производного класса не будет виртуальной. В этом случае с помощью указателя на базовый класс при любом значении этого указателя выполняется обращение к функции базового класса (несмотря на спецификатор virtual и присутствие в производном классе похожей функции). Сказанное иллюстрирует следующая программа: //Р10-07.СРР - некоторые особенности виртуальных функций #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 fl(void) { cout << "\ndir::fl"; } // Ошибка в типе функции: // 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->fl(); pb->f2(); pb->f3(); pd->fl<); pd->f2(); // Ошибка при попытке без параметра вызвать dir::f3(int): // pd->f3(); pd->f3(0); pb = & D; pb->fl(); pb->f2(); Pb->f3(); // Ошибочное употребление или параметра, или указателя: // pb->f3(3); } Результат выполнения программы: base::f1 base::f2 base::f3 dir ::f1 base::f2 dir::f3::i = 0 dir::f1 base::f2 base::f3 Обратите внимание, что три виртуальные функции базового класса по-разному воспринимаются в производном классе. dir::fl() -виртуальная функция, подменяющая функцию base::fl(). Функция 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); } }; Абстрактные классы. Абстрактным классом называется класс, в котором есть хотя бы одна чистая (пустая) виртуальная функция. Чистой виртуальной называется компонентная функция, которая имеет следующее определение: virtual тип имя_функции(список_формальных_параметров) = 0; В этой записи конструкция "= 0" называется "чистый спецификатор". Пример описания чистой виртуальной функции: virtual void fpure(void) = 0; Чистая виртуальная функция "ничего не делает" и недоступна для вызовов. Ее назначение - служить основой для подменяющих ее функций в производных классах. Исходя из этого становится понятной невозможность создания самостоятельных объектов абстрактного класса. Абстрактный класс может использоваться только в качестве базового для производных классов. При создании объектов такого производного класса в качестве подобъектов создаются объекты базового абстрактного класса Предположим, что имеется абстрактный класс: class В { protected: virtual void func(char) = 0; void sos (int); }; На основе класса в можно по-разному построить производные классы: class D: public В { void func(char); }; class E: public В { void sos (int); }; В классе d чистая виртуальная функция func () заменена конкретной виртуальной функцией того же типа. Функция B::sos() наследуется классом D и доступна в нем и в его методах. Класс D не абстрактный. В классе е переопределена функция B::sos(), а виртуальная функция в::func() унаследована. Тем самым класс е становится абстрактным и может использоваться только как базовый. Как всякий класс, абстрактный класс может иметь явно определенный конструктор. Из конструктора возможен вызов методов класса, но любые прямые или опосредованные обращения из конструктора к чистым виртуальным функциям приведут к ошибкам во время выполнения программы. Механизм абстрактных классов разработан для представления общих понятий, которые в дальнейшем предполагается конкретизировать. Эти общие понятия обычно невозможно использовать непосредственно, но на их основе можно, как на базе, построить частные производные классы, пригодные для описания конкретных объектов. Например, из абстрактного класса "фигура" можно сформировать класс "треугольник", "окружность" и т.д. В качестве примера рассмотрим программу, в которой на основе базового класса point построен абстрактный класс figure. В классе figure определены: конструктор, чистая виртуальная функция show () для вывода изображения фигуры, например, на экран дисплея. Кроме того, в класс входят методы hide О - убрать изображение фигуры с экрана дисплея и move () - переместить изображение фигуры в заданную точку экрана. Функции hide() и move () обращаются к чистой виртуальной функции show (). Однако реальное выполнение show () возможно» только после создания производного класса, в котором чистая виртуальная функция show() будет подменена компонентной функцией для изображения конкретной фигуры. Определение абстрактного класса figure (в файле figure. срр): //FIGURE.CPP - абстрактный класс на базе класса #include "point.срр" class figure:
Дата добавления: 2014-01-11; Просмотров: 365; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |