Студопедия

КАТЕГОРИИ:


Архитектура-(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; Просмотров: 339; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.015 сек.