Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Чистая виртуальная функция и абстрактный класс
Часто в базовом классе виртуальная функция не определяется, а только объявляется. Чтобы подчеркнуть, что это делается намеренно, задается объявление
virtual void FF(...) = 0;
Такая функция называется чистой виртуальной функцией. Если в классе задана чистая виртуальная функция, то определить объект этого класса нельзя! Например, задан класс:
class A { public: virtual Print() = 0; // чистая виртуальная функция ... };
Тогда следующий фрагмент – ошибочен:
{ A x; // ошибка! ... }
Определение. Класс, содержащий хотя бы одну чистую виртуальную функцию, называется абстрактным. Кроме того, что нельзя определить объект абстрактного класса, он не может быть типом формального аргумента какой-либо функции или возвращаемым значением:
A Func(B x, A y){..}
Ошибка! Ошибка! 5.4. Правила определения виртуальных функций Правило 1. Виртуальная функция в базовом классе либо определяется, либо объявляется как чистая виртуальная;
Правило 2. Виртуальная функция в порожденном классе: а) либо определяется, б) либо заново объявляется как чистая виртуальная, в) либо остается таковой по умолчанию. В последних двух случаях порожденный класс также становится абстрактным и нельзя объявлять объект этого класса. Заметим, что в некоторых случаях мы не знаем, как определить виртуальную функцию в порожденном (или базовом) классе, а объекты этого класса задавать требуется. Выход – определить виртуальную функцию как пустую. Например, virtual void FF(..) { } Правило 3. Порожденный класс сам может задавать свои виртуальные функции. К виртуальным функциям, заданных впервые в порожденном классе, нельзя обратиться через указатель на базовый класс, если виртуальная функция в базовом классе не определялась.
Правило 4. Базовому классу известны все переопределения его виртуальных функций, однако базовому классу неизвестны виртуальные функции, объявленные в порожденных классах. Рассмотрим это свойство на следующем примере. Например, заданы классы
class A { … public: virtual void Print() = 0; // чистая виртуальная };
class B: public A {... public: virtual void Print() = 0; // осталась и в B чистой виртуальной virtual int F() = 0; // новая чистая виртуальная функция
... };
class C: public B {... public: virtual int F() {return 3;} virtual void Print() {cout << “\nКласс C:”;} … };
void main() // Что можно, что нельзя? {A *pa; C z; C *pc = &z; B *pb; pa = pc; pa –> Print(); // можно: работает C::Print(), объявленная в A, как чистая виртуальная int k = pa –> F(); // так нельзя: F() не объявлялась в базовом классе A никак! pb = pc; k = pb –> F(); // верно: именно в классе B, как базовом для C, F() была объявлена как виртуальная (чистая) }
Правило 5. В том случае, когда виртуальная функция вызывается через указатель на базовый класс, такой вызов называется виртуальным, и он соответствует позднему связыванию. Но вызов виртуальной функции может быть невиртуальным, т.е. имеет место раннее связывание, в следующих случаях:
а) виртуальная функция вызывается не через указатель или ссылку, а через объект. Например,
B y; y.Print(); // Ясно: B::Print()
б) Виртуальная функция вызывается через указатель или ссылку на базовый класс, но уточняется именем класса. Например,
A x; B y; A *pa = &y; pa –> B:: Print(); // Невиртуальный вызов виртуальной функции pa –> Print(); // Виртуальный вызов виртуальной функции
в) Если виртуальная функция вызывается в конструкторе или деструкторе порожденного класса, то всегда вызывается функция базового класса, т.к. объект порожденного класса еще не создан или уже разрушен.
Правило 6. Вызов виртуальной функции через ссылку на базовый класс также является виртуальным. Чаще всего такая ситуация возникает, когда типом аргумента некоторой внешней функции является ссылка на базовый класс, а в этой функции выполняется обращение к виртуальной функции. Фактическим аргументом в этом случае может быть объект любого порожденнного класса, где определена виртуальная функция, так как при вызове выполняется стандартное преобразование порожденного объекта к базовому. Например, определена функция
void DoPrint(A &a) {a.Print(); // Виртуальный вызов функции Print }
и в основной функции задан вызов ее с объектами разных классов:
void main() {A x(1, 1, 1); B y(2, 3, 1, 6, 4, 7); DoPrint(x); // Выполнится A:: Print(); DoPrint(y); // Выполнится B:: Print();
}
Правило 7. До сих пор речь шла о виртуальных функциях. Но виртуальными могут быть и операции, перегруженные в классе. Например, в классе A определим виртуальную операцию ‘-‘ (изменить знак данных)
class A {.... public: virtual void operator -() {d1 = -d1; d2 = -d2; d3 = -d3;} .... };
class B: public A {... public: void operator-() { // в том случае, если d1 будет в части protected можно и так: // d1= -d1;d2= -d2;A::d3= -A::d3;d3= -d3;d4= -d4; // то есть в общем случае нельзя. // Но короче и грамотнее так: A:: operator-(); // работает базовая операция ‘-‘. Без A:: будет рекурсия d3 = -d3; d4 = -d4;} };
Примеры виртуальных вызовов операции ‘-‘:
A x(2, 2, 3), *pa; B y(5, 4, 3, 2, 1); pa = &y; -(*pa); // виртуальный вызов операции ‘-’ через указатель на базовый класс для объекта y pa –> Print(); // виртуальный вызов B::Print() A &sa = y; -sa; // виртуальный вызов операции ‘-‘ через ссылку sa sa.Print(); //виртуальный вызов функции Print() через ссылку
Правило 8. Виртуальные функции могут быть и приватными. Например, функция print() в классах A и B:
class A {virtual void print() {cout << “\n********A*********\n”;} protected: int a; public: A() {a = 1;} virtual void Print() {print(); cout << a;} ... };
class B: public A { int b; void print() {cout << “\n**********B********\n”;} public; B() {b = 2;} void Print() {print(); cout << a << ’ ‘ << b;} };
При вызове виртуальной функции Print() будет вызываться и виртуальная приватная функция print(). Например,
A x, *pa; B y; pa = &x; pa –> Print(); // Работают функции Print() и print() класса A pa = &y; pa –> Print(); // Работают функции Print() и print() класса B
Замечание. Удивительно другое. Если бы функция Print() класса B была задана так:
B:: Print() {print(); A:: Print(); cout << ’ ‘ << b; }
то при виртуальном вызове
pa = &y; pa –> Print();
2 раза проработала бы виртуальная функция print() из класса B. Почему? Понять можно, разобравшись с механизмом позднего связывания.
|
||||||
Последнее изменение этой страницы: 2021-12-07; просмотров: 47; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.141.199.243 (0.014 с.) |