Механизм позднего связывания
Для каждого класса (не объекта), содержащего хотя бы один виртуальный метод, компилятор создает таблицу виртуальных методов (vtbl), в которой для каждого виртуального метода записан его адрес в памяти. Адреса методов содержатся в таблице в порядке их описания в классах. Адрес любого виртуального метода имеет в таблице одно и то же смещение для каждого класса в пределах иерархии.
Каждый объект содержит скрытое дополнительное поле vptr для ссылкина таблицу виртуальных методов. Оно заполняется конструктором при создании объекта. На этапе компиляции ссылки на виртуальные методы заменяются на обращения к vtbl через vptr объекта, а на этапе выполнения в момент обращения к методу его адрес выбирается из таблицы. Таким образом, вызов виртуального метода, в отличие от обычных методов и функций, выполняется через дополнительный этап получения адреса метода из таблицы. Это несколько замедляет выполнение программы.
Рекомендуется делать виртуальными деструктор, чтобы гарантировать правильное освобождение памяти из-под динамического объекта, т.к. в этом случае всегда будет выбран деструктор, соответствующий фактическому типу объекта. Деструктор передает операции delete размер объекта, имеющий тип size_t. Если удаляемый объект является производным и в нем не определен виртуальный деструктор, передаваемый размер объекта может оказаться неправильным.
Лучше объявлять виртуальными только методы, для которых есть вероятность, что они будут переопределены в производных классах. Методы, которые во всей иерархии останутся неизменными или те, которыми производные классы пользоваться не будут, делать виртуальными нет смысла. С другой стороны, при проектировании иерархии не всегда можно предсказать, каким образом будут расширяться базовые классы, а объявление метода виртуальным обеспечивает гибкость и возможность расширения.
Например, вызов метода draw будет осуществляется из метода перемещения объекта. Если текст метода перемещения не зависит от типа перемещаемого объекта, переопределять этот метод в производных классах нет необходимости, и он может быть описан как невиртуальный. Если метод draw виртуальный, метод перемещения сможет без перекомпиляции работать с объектами любых производных классов.
Виртуальный механизм работает только при использовании указателей или ссылок на объекты. Объект, определенный через указатель или ссылку и содержащий виртуальные методы, называется полиморфным.В данном случае полиморфизм состоит в том, что с помощью одного и того же обращения к методу выполняются различные действия в зависимости от типа, на который ссылается указатель в каждый момент времени.
Абстрактные классы
Абстрактный класс - класс, содержащий хотя бы один чисто виртуальный метод. Такие классы предназначены для представления общих понятий, которые предполагается конкретизировать в производных классах.
Абстрактны класс может использоваться только в качестве базового для других классов - объекты абстрактного класса создавать нельзя (прямой или косвенный вызов чисто виртуального метода приводит к ошибке при выполнении).
Правила определения абстрактного класса:
· его нельзя использовать при явном приведении типов, для описания типа параметра и типа возвращаемого функцией значения;
· допускается объявлять указатели и ссылки на абстрактный класс, если при инициализации не требуется создавать временный объект;
· если класс, производный от абстрактного, не определяет все чисто виртуальные функции, он также является абстрактным.
Таким образом, можно создать функцию, параметром которой является указатель на абстрактный класс. На место этого параметра при выполнении программы может передаваться указатель на объект любого производного класса. Это позволяет создавать полиморфные функции, работающие с объектом любого типа в пределах одной иерархии.
Тема 2.14