Виртуальные и динамические методы, замещающие методы
Виртуальные методы
Адреса этих методов определяются лишь во время выполнения программы из специальной таблицы. Такой поиск называют еще поздним связыванием (late binding). Решение о вызове конкретного метода решается в процессе выполнения программы, и решение основывается на данных, хранящихся в объекте, вызывающем метод.
Таким образом, появляется возможность реализовать различные варианты поведения объектов разных классов при вызове методов с одним именем. Это, по сути, определение полиморфизма.
Когда компилятор встречает обращение к виртуальному методу, он подставляет вместо обращения к конкретному адресу код-смещение относительно начала специальной таблицы, из который и извлекается нужный адрес. Эта таблица называется таблицей виртуальных методов (Virtual Method Table, VMT). Такая таблица есть у каждого класса. В ней хранятся указатели (адреса) всех виртуальных методов класса: вновь объявленных и унаследованных. Отсюда достоинства и недостатки: они вызываются сравнительно быстро, хотя и медленнее статических, однако для хранения указателей требуется большее количество памяти. Доступ к виртуальному методу через VMT — это извлечение адреса по коду (смещению) из таблицы, состоящее из одной машинной инструкции.
Примечания:
• Для объявления метода виртуальным служит ключевое слово-директива Virtual, которое указывается в конце объявления заголовка метода после точки с запятой.
• Директива Virtual всегда вводит новый виртуальный метод, никак не связанный с любым другим одноименным методом, наследуемым классом. Переопределенный виртуальный метод ведет себя аналогично переопределенному статическому методу, т.е. полиморфизм не проявляется. Переопределить виртуальный метод в потомке можно и статическим методом.
Type T1=Class
Procedure Test; Virtual; End;
T2=Class(T1)
Procedure Test; // Test скрывается, но не замещается
End;
Var SomeObject: T1;
Begin
SomeObject:=T2.Create;
SomeObject.Test; // Вызывается T1.Test
• У каждого производного класса свое пространство имен, предваряющее пространство имен всех наследуемых этим классом методов. Поэтому можно скрыть наследуемый метод, объявив одноименный виртуальный метод. Однако в VMT таблице сохраняется адрес нового и старого (переопределенного) виртуального метода, следовательно, к переопределенному виртуальному методу возможен доступ с использованием механизма приведения типов:
TParentClass(Obj). <метод>;
Динамические методы
Динамические методы вызываются медленнее виртуальных, но позволяют более экономно расходовать память. Каждому динамическому методу присваивается индекс — отрицательное число. В таблице динамических методов (Dynamic Method Table, DMT) класса хранятся индексы и адреса только тех динамических методов, которые описаны в данном классе. Если в DMT данного класса адрес нужного динамического метода не находится, то просматривается DMT класса-предка, и так далее по всей цепочке наследования до класса TObject. Таким образом, доступ к методу через DMT может привести к активному поиску в DMT разных классов и потребовать сотен тактов процессора.
Во всем остальном между виртуальными и динамическими методами нет принципиальной разницы.
Динамические методы используются для редко вызываемых методов, когда потерями в производительности можно пренебречь. Синтаксис объявления динамических методов:
Procedure <имя метода>[(<параметры>)}; Dynamic;
Function <имя метода>[(<параметры>)}: <тип результата>; Dynamic;
Редко перекрываемые методы рекомендуется делать динамическими.
Замещающие методы
В то время как, директивы Virtual и Dynamic всегда вводят новое имя метода, то директива Override всегда указывает на новую реализацию уже существующего наследуемого виртуального или динамического метода. Весь смысл виртуальных и динамических методов состоит в том, чтобы дать классу-потомку возможность модифицировать поведение метода, введенного в его классе-предшественнике. Директива Override служит для того, чтобы дать понять транслятору, что ваша цель — модифицировать (заменить или расширить — два разных варианта) реализацию наследуемого метода.
Объявление Override-метода должно в точности совпадать с объявлением исходного Virtual или Dynamic метода по названию, числу, именам и типам параметров, а также типу результата для функции. Новая строка в VMT (DMT) таблице не создается, поскольку адрес замещенного метода записывается в строку таблицы замещаемого метода. Таким образом, у класса-предка и класса-потомка адрес одноименного метода находится в строке таблицы VMT (DMT) с одинаковым смещением от ее начала. Однако «начинка» этих методов различна.
Синтаксис объявления замещающих виртуальных методов:
Procedure|Constructor|Destructor <имя метода>[(<параметры>)];Override;
Function <имя метода>[{<параметры>)]: <тип результата>; Override;;
Вызов виртуального метода может во время работы программы привести к вызову любой из его реализации (принцип полиморфизма) в производных классах, поэтому у них у всех должны быть идентичные списки параметров.