The standard does leave an opening to call B::foo directly and avoid a table lookup:
#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();
}
Output:
C::foo
C::foo
B::foo
So you can get the behaviour you say you expect as follows:
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(); }
};
Now callers should use bar instead of foo. If they have a C*, then they can cast it to A*, in which case bar
will call C::foo
, or they can cast it to B*, in which case bar
will call B::foo
. C can override bar again if it wants, or else not bother, in which case calling bar()
on a C* calls B::foo()
as you'd expect.
I don't know when anyone would want this behaviour, though. The whole point of virtual functions is to call the same function for a given object, no matter what base or derived class pointer you're using. C++ therefore assumes that if calls to a particular member function through a base class are virtual, then calls through derived classes should also be virtual.