Проверка контроля доступа к функциям происходит на более поздней стадии вызова функции c ++.
Порядок на высоком уровне будет подобен поиску имени, выводу аргумента шаблона (если есть), разрешению перегрузки, а затем проверке контроля доступа (public / protect / private).
Но в вашем фрагменте вы использовали указатель на базовый класс, и функция f () в базовом классе действительно общедоступна, это то, что компилятор может видеть во время компиляции, поэтому компилятор обязательно пропустит ваш фрагмент.
A *ptr = new B;
ptr->f();
Но все вышеперечисленное происходит во время компиляции, поэтому они действительно статичны. Хотя вызов виртуальной функции, часто приводимый в действие vtable & vpointer, представляет собой динамический процесс, который происходит во время выполнения, поэтому вызов виртуальной функции ортогональн к управлению доступом (вызов виртуальной функции происходит после контроля доступа), поэтому вызов функции f () фактически завершился B :: f () независимо от того, является ли управление доступом частным.
Но если вы попытаетесь использовать
B* ptr = new B;
ptr->f()
Это не пройдет, несмотря на vpointer & vtable, компилятор не позволит ему скомпилироваться во время компиляции.
Но если вы попробуете:
B* ptr = new B;
((static_cast<A*>(ptr))->f();
Это бы отлично работало.