
- •1. Дополнения к с
- •1.1. Комментарии
- •1.2. Ключевые слова
- •1.3. Константы
- •1.4. Блочные объявления
- •1.5. Ссылки
- •1.6. Новая роль имён перечислений, структур и объединений
- •1.7. Распределение памяти
- •1.8. Встраиваемые функции
- •1.9. Перегрузка функций
- •1.10. Задание для параметров функции значений по умолчанию
- •1.11. Дополнительные операции для доступа к данным
- •1.12. Предопределённые потоки ввода-вывода
- •2.1. Инкапсуляция
- •2.2. Разграничение доступа (скрытие данных и методов)
- •2.3. Друзья классов
- •2.4. Конструкторы и деструкторы
- •2.5. Конструктор по умолчанию
- •2.6. Конструктор копирования
- •2.7. Несколько слов о деструкторах
- •2.8. Перегрузка операций
- •3.1. Наследование
- •3.2. Виртуальные функции –полиморфизм
- •3.3. Шаблоны
3.2. Виртуальные функции –полиморфизм
Прямым результатом наследования явилось включение в язык виртуальных функций. Для того чтобы разобраться, о чем идёт речь, рассмотрим пример:
class BaseClass
{
public: void Method ( );
}
class DrivedClass: public BaseClass
{
public:
void Method ( );
};
void SimpleFunc (BaseClass * ptrBase)
{
ptrBase->Method ( );
}
А теперь попробуйте ответить на вопрос: «К какому методу относится вызов в функции SimpleFunc( )?» Правильно, к методу базового класса. И для того, чтобы вызвать метод производного класса, необходимо использование операции разрешения контекста.
Что же делать, чтобы иметь возможность вызывать оба метода? Ответ на этот вопрос дает полиморфизм – использование одного и того же вызова для ссылки на разные методы в зависимости от типа объекта. Для поддержки полиморфизма язык программирования должен поддерживать механизм позднего связывания, при котором решение о том, какой именно метод с одним и тем же именем должен быть вызван, принимается в зависимости от типа объекта ужево время выполнения программы.
Обычная практика выбора вызываемого метода на этапе компиляции называется ранним связыванием.
В С++ имеется ключевое слово virtual, использование которого перед именем метода предполагает, что все вызовы этого метода (и всех методов с этим именем в общих производных классах) должны выполняться посредством механизма позднего связывания. Небольшое изменение в предыдущем примере снимает вопрос о неоднозначности вызываемого метода:
class BaseClass
{
public: virtual void Method ( ); // Методобъявленвиртуальным
};
class DrivedClass: public BaseClass
{
public:
void Method ( );
};
void SimpleFunc (BaseClass * ptrBase)
{
ptrBase->Method ( );
}
Теперь соответствующий метод будет вызываться в зависимости от того, указатель на какой объект передается в функцию SimpleFunc..
Примечание
1. Если метод объявлен виртуальным – он не может быть перегружен.
2. Виртуальный метод, чаще называемый виртуальной функцией, не может быть статическим.
В каких же случаях следует использовать виртуальные функции, учитывая, механизм позднего связывания требует дополнительных расходов времени на вызов метода? Общее правило следующее: если существует вероятность, что в некотором производном классе будет использован метод, перекрывающий метод базового класса и которому нужен доступ к компонентам этого базового класса, то он должен быть виртуальным.
Может быть объявлен и виртуальный деструктор, что позволяет использовать механизм позднего связывания при определении деструктора, вызываемого для удаления объекта.
С виртуальными функциями тесно связано понятие абстрактного класса — такого, который содержит как минимум одну чистую виртуальную функцию.Чистая виртуальная функция – это функция, определенная со спецификатором = 0, и для которой нет и не планируется какой-либо реализации. Объявление такой функции имеет вид:
virtual void Method ( ) = 0; // Чистая виртуальная функция
Такие функции должны быть определены в производном классе, иначе и производный класс будет абстрактным.
Абстрактные классы чрезвычайно полезны для организации иерархической структуры классов, когда базовый класс определяет некоторое общее поведение, а некоторые частные особенности переносятся в конкретные производные классы.
Примечание
Нельзя создавать объекты абстрактного класса – он может быть использован только в качестве базового для построения других классов. Однако указатель на абстрактный класс создавать можно.