Страницы

Поиск по вопросам

пятница, 19 апреля 2019 г.

Порядок работы с таблицами виртуальных методов

Добрый вечер.
Если в классе объявлен виртуальный метод, то компилятор создает таблицу виртуальных методов, объявленных в определении этого класса. Производный класс "получает" эту таблицу при наследовании и записывает туда адреса переопределенных (своих реализаций) виртуальных методов.
В момент создания объекта (наследника через указатель на базовый класс), в базовом классе (совместно с созданием VMT) объявляется виртуальный табличный указатель объекта или vptr.
Сначала vptr объекта наследуется всеми производными классами, до тех пор пока не будет инициализирован адресом VMT того произодного класса, которому принадлежит созданный объект.
При вызове виртуального метода, компилятор через vptr достает фактический адрес виртуального метода из VMT нужного производного класса
То есть компилятор генерирует «скрытый» код в конструкторе каждого класса для инициализации vpointer'ов объектов класса адресами соответствующей VMT.
сначала вызывается конструктор базового класса, инициализирующий vptr адресом VMT базового класса, затем вызывается конструктор производного класса, который перезаписывает значение vptr адресом VMT производного класса. (и так до тех пор пока vptr не будет инициализирован адресом VMT того производного класса, которому принадлежит созданный объект)
Вопросы в следующем:
1) Что подразумевается под инициализацией vptr адресом VMT производного класса? Понятно, что у виртуальных методов есть адреса, которые хранятся в таблице, разве адрес есть непосредственно у VMT класса?
2) В каком порядке при запуске программы происходит создание VMT базового класса, создание VMT производного класса (которая получает VMT базы при наследовании и инициализирует ее элементы адресами своих методов), создание vptr и его инициализация таблицами базового и производных классов?
3) При вызове виртуального деструктора значение vptr тоже инициализируется сначала адресом VMT производного, а затем базового класса?


Ответ

Если в классе объявлен виртуальный метод, то компилятор создает таблицу виртуальных методов, объявленных в определении этого класса. Производный класс "получает" эту таблицу при наследовании и записывает туда адреса переопределенных (своих реализаций) виртуальных методов.
Это верно, с той только оговоркой, что все это делается на этапе компиляции. То есть вышепроцитированное - это то, как может "рассуждать" компилятор в процессе формирования VMT для очередного класса.
1) Что подразумевается под инициализацией vptr адресом VMT производного класса? Понятно, что у виртуальных методов есть адреса, которые хранятся в таблице, разве адрес есть непосредственно у VMT класса?
VMT - это таблица в памяти. У нее есть адрес. Вот ее адрес и хранится в указателе vptr каждого объекта. В указателе vptr у объекта типа SomeClass хранится указатель на VMT класса SomeClass. А уж в таблице хранятся указатели на виртуальные методы класса SomeClass (или его предков).
2) В каком порядке при запуске программы происходит создание VMT базового класса, создание VMT производного класса (которая получает VMT базы при наследовании и инициализирует ее элементы адресами своих методов),
Вопрос не совсем корректно поставлен.
Таблицы VMT для всех классов как правило инициализированы статически, то есть их содержимое известно на стадии компиляции. При запуске программы они уже лежат готовенькие к использованию (в т.наз. сегменте инициализированных данных). Таблицы VMT формирует линкер (хотя возможно и более "позднее" формирование загрузчиком). В любом случае, когда программа фактически запустилась, все VMT уже сформированы. Порядок их формирования никакой роли не играет, ибо от него ничего не зависит.
При создании объектов класса идет работа только с указателями на VMT. Сами VMT при этом никто уже не модифицирует (и не "создает").
создание vptr и его инициализация таблицами базового и производных классов?
Вы сами совершенно правильно описали этот процесс в последовательности вызова конструкторов создаваемого объекта, от базовых к производным. Последним отрабатывает "самый производный" конструктор - конструктор полного объекта - в результате чего в vptr остается правильный указатель на VMT полного объекта.
3) При вызове виртуального деструктора значение vptr тоже инициализируется сначала адресом VMT производного, а затем базового класса?
Деструкция происходит в порядке, обратном конструкции. Сначала отрабатывает тело деструктора полного объекта, а в конце он вызывает деструкторы всех своих базовых подобъектов. Каждый деструктор первым делом ставит vptr на VMT своего класса.
Так работает любой деструктор, поэтому не совсем ясно, зачем вы упоминаете вызов именно виртуального деструктора. Когда деструктор уже начал работу, его виртуальность уже не имеет никакого значения.

Комментариев нет:

Отправить комментарий