Учитывая коды ниже:
class Base
{
public:
virtual void f()
{
std::cout << "virtual Base::f()\n";
}
};
class D1 : public Base
{
public:
virtual void f()
{
std::cout << "virtual D1::f()\n";
}
};
int main()
{
D1 d1;
Base *bp = &d1;
bp->f();
return 0;
}
Выход был именно то, что я ожидал:
virtual D1::f()
Press <RETURN> to close this window...
Но однажды я удалил virtual void f()
от class Base
Компилятор пожаловался, что:
error: 'class Base' has no member named 'f'
Может кто-нибудь сказать мне, почему компилятор не генерирует коды, чтобы он мог связывать виртуальные функции во время рома?
Вы вызываете виртуальные функции-члены через указатель на Base
, Это означает, что вы можете вызывать только те методы, которые существуют в Base
учебный класс. Вы не можете просто добавлять методы к типу динамически.
Хотя немного поздно, как ответ, прямая цитата из C ++ Primer о том, как разрешаются вызовы функций по отношению к наследованию. Ваш код не выполняется при поиске имени (шаг 2 ниже), что делается статически.
Понимание того, как решаются вызовы функций, крайне важно для
понимание наследования в C ++. Учитывая вызов p-> mem () (или
obj.mem ()), выполняются следующие четыре шага:
Сначала определите статический тип p (или obj). Потому что мы звоним
член, этот тип должен быть типом класса.Ищите mem в классе, который соответствует статическому типу p (или obj). Если mem не найден, посмотрите в прямой базовый класс и
продолжить цепочку классов до тех пор, пока не будет найдена mem или последний класс
ищется. Если mem не найден в классе или в его базе
классы, то вызов не будет компилироваться.Как только mem найден, выполните обычную проверку типов (§6.1, стр. 203), чтобы проверить, является ли этот вызов допустимым с учетом найденного определения.
Предполагая, что вызов допустим, компилятор генерирует код, который варьируется в зависимости от того, является ли вызов виртуальным или нет:
— Если мем виртуальный и звонок сделан по ссылке или
указатель, затем компилятор генерирует код для определения во время выполнения
какую версию запустить на основе динамического типа объекта.— В противном случае, если функция не виртуальная, или если вызов
объект (не ссылка или указатель), компилятор генерирует нормальный
вызов функции.