Стандарт оставляет возможность открывать вызов B :: foo напрямую и избегать просмотра таблицы:
#include <iostream>
class A {
public: virtual void foo() = 0;
};
class B : public A {
public: void foo() {
std::cout <<"B::foo\n";
}
};
class C : public B {
public: void foo() {
std::cout <<"C::foo\n";
}
};
int main() {
C c;
A *ap = &c;
// virtual call to foo
ap->foo();
// virtual call to foo
static_cast<B*>(ap)->foo();
// non-virtual call to B::foo
static_cast<B*>(ap)->B::foo();
}
Выход:
C::foo
C::foo
B::foo
Таким образом, вы можете получить ожидаемое поведение следующим образом:
class A {
virtual void foo() = 0;
// makes a virtual call to foo
public: void bar() { foo(); }
};
class B : public A {
void foo() {
std::cout <<"B::foo\n";
}
// makes a non-virtual call to B::foo
public: void bar() { B::foo(); }
};
Теперь абоненты должны использовать bar вместо foo. Если у них есть C *, то они могут привести его к A *, в этом случае bar
вызовет C::foo
, или они могут привести его к B *, и в этом случае bar
вызовет B::foo
. C может переопределить bar снова, если он этого хочет, или не беспокоиться, и в этом случае вызов bar()
на C * вызывает B::foo()
, как и следовало ожидать.
Хотя я не знаю, когда кому-нибудь захочется такого поведения. Весь смысл виртуальных функций заключается в том, чтобы вызывать одну и ту же функцию для данного объекта, независимо от того, какой указатель базового или производного класса вы используете. Поэтому в C ++ предполагается, что если вызовы определенной функции-члена через базовый класс являются виртуальными, то вызовы через производные классы также должны быть виртуальными.