Они очень разные, на самом деле, я бы сказал, совершенно не связанные.
В C ++, если базовый класс имеет не виртуальную функцию-член, вы можете объявить в производном классе не виртуальную функцию-член с тем же именем. В результате функция-член производного класса будет скрывать функцию-член базового класса. И никакой виртуальной диспетчеризации не может быть. Как в следующем:
struct Base {
void foo() {
std::cout << "Base::foo called!" << std::endl;
};
};
struct Derived : Base {
void foo() {
std::cout << "Derived::foo called!" << std::endl;
};
};
int main() {
Derived d;
d.foo(); //OUTPUT: "Derived::foo called!"
Base* b = &d;
b->foo(); //OUTPUT: "Base::foo called!"
};
Выше показано, как функция-член производного класса скрывает функцию базового класса. Если у вас есть указатель на базовый класс, поскольку функции не виртуальные, виртуальная таблица не используется для разрешения вызова и, следовательно, будет вызвана функция foo из базового класса. Дело в том, что в C ++ ничто не мешает вам создать другую функцию в классе Derived с тем же именем (обратите внимание, что другая сигнатура по-прежнему вызовет скрытие всех функций-членов базового класса с тем же именем). Все, что вы получите, - это предупреждение компилятора о том, что функция-член класса Derived скрывает функции-члены базового класса.
Последняя функция-член в Java совершенно другая. В Java все функции-члены являются виртуальными. Таким образом, вы не можете отключить виртуальную диспетчеризацию, как в C ++. Последняя функция-член просто означает, что любому последующему производному классу не будет разрешено (возникнет ошибка) объявлять функцию-член с тем же именем (и подписью). Но виртуальная диспетчеризация (и, следовательно, переопределение в динамическом полиморфном смысле) все еще происходит между интерфейсом / базовым классом, который объявил исходную функцию-член, и производным классом, который пометил ее как final. Просто последующее переопределение функции строго запрещено (т. Е. Попытка вышеуказанного кода с помощью foo (), помеченного как final в базовом классе, приведет к ошибке в объявлении класса Derived, поскольку foo () там не будет допускается).
Как видите, эти два понятия совершенно разные.
- В C ++, с не виртуальной функцией-членом, виртуальная диспетчеризация не происходит (таким образом, нет «переопределения» в традиционном смысле), но вам разрешено иметь производные классы с функциями-членами с тем же именем (и это может полезно иногда в "статическом полиморфизме").
- В Java с функцией финального члена виртуальная диспетчеризация все еще происходит, но переопределение в последующих производных классах строго запрещено.