Скрыть виртуальную функцию с помощью не виртуального переопределения

Имея

#include <iostream>

using namespace std;

class A {
public:
    virtual void foo() {
        cout << "A" << endl;
    }
};

class B : public A {
public:
    void foo() {
        cout << "B" << endl;
    }
};

class C : public B {
public:
    void foo() {
        cout << "C" << endl;
    }
};

int main() {
    C c;
    B* b = &c;
    b->foo();

    return 0;
}

Выходной сигнал C, но я ожидал B.

Я не объявлял B::foo() с помощью модификатора virtual, поэтому ожидаю, что вызов функции будет определяться статическим типом (без полиморфизма).

Почему C::foo() вызывается?

Можно ли предоставить не виртуальную функцию в производном классе, который скрывает виртуальную функцию в базе? Какая подпись должна иметь производная функция-член, чтобы b->foo() называет ее, а не (b->*&A::foo)()

Ответ 1

Принцип виртуального наследования функции-члена является прямым следствием стандарта С++:

10.3/2: Если виртуальная функция-член vf объявлена ​​в классе Base и в классе Derived, , полученном прямо или косвенно из Base, функция-член vf с тем же именем, список параметров-типа, cv-qualification и refqualifier (или отсутствие таких же), как Base:: vf объявлен, то Derived:: vf также виртуальный.

Поэтому, независимо от уровня наследования, функция будет виртуальной во всех классах, полученных как-то из A. Нет необходимости ставить ключевое слово virtual.

Целью этого полиморфного подхода является обеспечение того, чтобы вы всегда вызывали соответствующую функцию, соответствующую реальной idendity вашего объекта, независимо от того, используете ли вы указатель на базу или указатель на реальный класс объекта. Вот почему вы получаете "C"!

В этот связанный вопрос SO Я объясню трюк, чтобы создать впечатление об удалении виртуальности на одном уровне, используя множественное наследование. Однако это можно сделать только для одного уровня (вы должны сделать это для класса вашего базового указателя).

* Кстати, вы могли бы написать pb->B::foo(); не нужно *&.