Quote Originally Posted by Muthuveerappan View Post
Just to summarize the reason how this happened:
1) Access is checked by the compiler at compile time, the fact that it is A::f1() public allows the compiler to let it pass through
2) Which virtual function would be invoked is determined at runtime based on the table, and at runtime access checks are not performed again.

I suppose that is how it is possible to access B::f1() outside the class through the base pointer.
That's the wrong way to look at it. Trying to understand C++ by secondguessing the compiler or runtime system is close to meaningless.

C++ should be understood by its definition. Of course the C++ designers must make sure there exists a reasonably efficient realization for each language feature they put in. This is even a C++ design goal but it doesn't explain why the language feature is there in the first place.