лекция 19 (1161114), страница 2
Текст из файла (страница 2)
px->f(); // вызывается функция в зависимости от динамического типа, смотря на что указывает px
px->g(); // связывание статическое, вызывается g() из Х.
Внимание! Любимый вопрос на экзамене: что будет напечатано?
g()- не виртуальная! Зависит только от статического типа!
px->f(); // X::f()
px->g(); // X::g()
px = new Y;
px->f(); // Y::f()
px->g();// X::g()
px->F Если F - виртуальная функция, то мы ничего не можем сказать о том, что будет вызвано
Динамический тип- указатель или ссылка!
Посмотрим, как это реализовано:
У нас есть профиль объекта
класс X:
virtual f();
g();
virtual h();
класс Y:
f();
z();
Z:
f();
h();
Как только в классе появляются виртуальные методы, то в классе появляется специальное поле - ссылка на таблицу виртуальных методов (ТВМ). Эта таблица не для объекта, а для типа - расходы по памяти минимальны.
Во всех объектах эта ссылка занимает одно и то же место.
Поэтому узнать, совпадают ли их динамические типы, очень просто - надо всего лишь сравнить ссылки на ТВМ.
В случае единичного наследования она содержит адрес функции f() и функции h(). Адреса функции g() там нет - это нединамическая функция.
Теперь в классе Y добавляется виртуальный метод z(). И его таблица ТВМ расширяется. Как наследуются классы, так же и наследуются ТВМ. И если класс Z не вводит новые виртуальные функции, а только подменяет их (или ничего не делает), то ТВМ этого класса имеет такой же вид.
g() нет, т.к. она статическая.
ТВМ наследуется.
ТВМ X (по строкам соответственно):
X::f();
X::h();
ТВМ Y:
Y::f();// подменяется в y
X::h();//не подменяется в у
Y::z();// новая
ТВМ Z:
Z::f();
Z::h();
Y::z();
Компилятор, когда видит вызов px->f(), то смотрит на ТВМ(у всех наследников ссылка на ТВМ находится на одинаковом смещении, также в самой ТВМ расположение ф-ий(порядок по строкам)) того класса, на который указывает px.
Виртуальность - это свойство класса, виртуальность связывается со всеми одноименными функциями. Если ч- объект, то компилятор в ТВМ не полезет.
Но виртуальность можно снимать (т.к. это вызов)(вызывать виртуальные функции классов - прародителей) - можно вызывать (! ответственность на программиста!)
px->X::f() (извне) или
X::f() (изнутри класса).
Если мы вызываем через имя класса- виртуальность вызова снимает, имя класса мы можем указывать внутри функций - членов!
Можно вручную( не через таблицу) с помощью switch.
8