Я изучаю наследование в C ++ 11, и я обнаружил, что если производный класс переопределил имя виртуальной функции, но с другим прототипом, указатель базового класса, назначенный указателем на производный класс, может получить доступ только к версии базового класса. функции. Функция производной версии недоступна. Интересно, почему это происходит.
class Enemy {
public:
virtual void describe() { std::cout << "Enemy"; }
};
class Dragon : public Enemy {
public:
virtual void describe(int dummy) { std::cout << "Dragon"; }
};
In main
,
Dragon foo;
Enemy* pe = &foo;
pe->describe(); // Enemy
foo.describe(1); // Dragon
pe->describe(1); // no matching function, candidate is Enemy::describe()
Из того, что я знаю о таблицах виртуальных функций, производный объект, на который указывает pe
(т.е. foo
), должен иметь член vpointer, указывающий на vtable Dragon
. Я также знаю, что переопределение имени функции в производном классе скроет все функции с тем же именем в базовом классе. Таким образом, в vtable Дракона адрес «описать» должен быть функцией с параметром int dummy
.
Но оказывается, что pe
может получить доступ к версии метода Enemy
, которая должна быть скрытой. И pe
не может получить доступ к версии Dragon
метода, которая должна быть в vtable pe
. Он работает так, как если бы использовалась vtable Enemy
. Почему так происходит?
Обновление: думаю, теперь я более или менее понимаю механизмы, лежащие в основе этого. Вот моя гипотеза:
Поскольку это указатель на Enemy
, программа сначала найдет имя метода в области Enemy
. Если имя не найдено, компилятор выдает ошибку. Если он не виртуальный, то назовите его. Если он виртуальный, запишите смещение метода в vtable Enemy
. Затем программа использует это смещение для доступа к нужному методу в vtable целевого объекта.
Если метод правильно переопределен, адрес функции в vtable целевого объекта по этому смещению был бы изменен. В противном случае это будет тот же адрес функции, что и в vtable Enemy
, как в примере.
Поскольку describe
Dragon
с int dummy
- это другой прототип, он добавляется в vtable Dragon
после исходного describe
, унаследованного от Enemy. Но к версии int dummy
нельзя получить доступ из Enemy*
, потому что vtable Enemy
даже не имеет этого смещения.
Это правильно?
pe
используетEnemy
API, и метод, к которому осуществляется доступ, не является частью этого API. - person Eljay   schedule 15.10.2019