- •Примеры
- •Примеры
- •[Править]в объектно-ориентированных языках
- •Параметрический полиморфизм
- •[Править]Специальный полиморфизм
- •[Править]Неявная типизация
- •Статический и динамический полиморфизм
- •Полиморфизм включения
- •Параметрический полиморфизм
- •Полиморфизм переопределения
- •Полиморфизм-перегрузка
- •Сравнение полиморфизма в функциональном и объектно-ориентированном программировании
- •Определение методов класса вне класса
- •Второй пример
- •Что вам необходимо знать
- •25. Inline Функции
- •26. Указатель this
- •27. Селекторы
- •5.1 Сопоставление шаблонов
- •5.2 Синтаксис селекторов
- •5.2.1 Группировка
- •5.3 Универсальный селектор
- •5.4 Селекторы типов
- •5.5 Селекторы потомков
- •5.6 Селекторы дочерних элементов
- •5.7 Селекторы сестринских элементов
- •5.8 Селекторы атрибутов
- •5.8.1 Сопоставление атрибутам и значениям атрибутов
- •5.8.2 Значения атрибутов, используемые в dtd по умолчанию
- •5.8.3 Селекторы классов
- •28. Модификаторы
- •29. Конструкторы
- •Назначение конструктора
- •Виды конструкторов
- •Конструктор по умолчанию
- •Конструктор копирования
- •Конструктор преобразования
- •Виртуальный конструктор
- •Синтаксис
- •Пример Конструктора по умолчания
- •31. Деструкторы
- •Виртуальный деструктор
- •Создание простого конструктора
- •Конструкторы и параметры по умолчанию
- •Перегрузка конструкторов
- •Представление о деструкторе
- •Что вам необходимо знать
- •32. Друзья
- •Определение друзей класса
- •Ограничение количества друзей
- •Что вам необходимо знать
- •Совместное использование элемента данных
- •Использование элементов с атрибутами public static, еслиобъекты не существуют
- •Использование статических функций-элементов
- •Что вам необходимо знать
- •37. Перезагрузка операций
- •Перегрузка унарных операций
- •Перегрузка постфиксных операций
- •Перегрузка бинарных операций
- •38. Преобразования типов
- •Const_cast не применим:
- •39. Наследование
- •Простое наследование
- •Второй пример
- •Что такое защищенные элементы
- •Разрешение конфликта имен
- •Что вам необходимо знать
- •40. Виртуальные функции и полиморфизм Виртуальные функции
- •Перекрытие методов
- •Абстрактные классы и чистые виртуальные функции
- •Модификаторы доступа
- •Ковариантность
40. Виртуальные функции и полиморфизм Виртуальные функции
Для примеров будем использовать классы из предыдущей лекции. Следует напомнить, что у нас был класс Person, от которого был унаследован класс Student. Рассмотрим следующий пример:
Student s; Person &p = s; s.name(); //Student::name() p.name(); //Person::name()
В 3-й строке вызовется метод класса Student, т.к. s является объектом этого класса. Однако, в строке 4 вызовется метод name базового класса Person, хотя по логике следовало бы тоже ожидать вызов name() класса Student — ведь p— это ссылка на объект производного класса.
Возможность вызова методов производного класса через ссылку или указатель на базовый класс осуществляется с помощью механизма виртуальных функций. Чтобы при вызове p.name() вызвался метод класса Student, реализуем классы следующим образом:
struct Person { virtual string name() const; }; struct Student: Person { string name() const; };
Перед методом name класса Person мы указали ключевое слово virtual, которое указывает, что метод является виртуальным. Теперь при вызове p.name() произойдет вызов метода класса Student, несмотря на то, что мы его вызываем через ссылку на базовый класс Person. Аналогичная ситуация и с указателями:
Student s; Person *p = &s ; p->name(); //вызовется Student::name(); Person n; p = &n; p->name(); //вызовется Person::name()
Если с некоторого класса в иерархии наследования метод стал виртуальным, то во всех производных от него классах он будет виртуальным, вне зависимости от того, указано ли ключевое слово virtual в классах наследниках.
Механизм виртуальных функций реализует полиморфизм времени выполнения: какой виртуальный метод вызовется будет известно только во время выполнения программы.
В качестве следующего примера можно рассмотреть класс TextFile, от которого наследуются два класса:GZippedTextFile и BZippedTextFile. Базовый класс имеет два метода: name(), возвращающий имя файла, и read(), считывающий данные из файла. В этом случае виртуальным имеет смысл сделать только метод read, т.к. у каждого типа сжатого файла будет свой способ считывания данных:
struct TextFile { string name() const; virtual string read(size_t count); //... }; struct GZippedTextFile : TextFile { string read(size_t count); //... } struct BZippedTextFile : TextFile { string read(size_t count); //... }
Перекрытие методов
Рассмотрим класс A, у которого имеется метод f(int), и класс B, унаследованный от A, у которого есть метод f(long):
struct A { void f(int); }; struct B : A { void f(long); };
В следующем коде:
B b; b.f(1);
произойдет вызов метода f(long) класса B, несмотря на то, что у родительского класса A есть более подходящий методf(int). Оказывается, что метод f(int) родительского класса A перекрылся. Для того, чтобы в примере вызвался методf(int), следует добавить строку using A::f; в определении класса B:
struct B : A { using A::f; void f(long); };
