Функция переопределения в Java vs С++
Два похожих определения в Java и С++, но совершенно другое поведение.
Версия Java:
class base{
public void func1(){
func2();
}
public void func2(){
System.out.println(" I am in base:func2() \n");
}
}
class derived extends base{
public void func1(){
super.func1();
}
public void func2(){
System.out.println(" I am in derived:func2() \n");
}
};
public class Test
{
public static void main(String[] args){
derived d = new derived();
d.func1();
}
}
выход:
I am in derived:func2()
Версия на С++:
#include <stdio.h>
class base
{
public:
void func1(){
func2();
}
void func2(){
printf(" I am in base:func2() \n");
}
};
class derived : public base
{
public:
void func1(){
base::func1();
}
void func2(){
printf(" I am in derived:func2() \n");
}
};
int main()
{
derived *d = new derived();
d->func1();
return 0;
}
выход:
I am in base:func2()
Я не знаю, почему у них другое поведение.
Даже я знаю, что Java имеет поведение автополиморфизма.
Выход Java трудно понять лично.
По моему мнению, согласно static scope
, функция базового класса func1()
должна только иметь возможность вызвать функцию базового класса func2()
, поскольку он вообще ничего не знает о производном классе. В противном случае поведение вызова принадлежит dynamic scope
.
Возможно, в С++ func2()
в базовом классе есть bind static
, но в Java это bind dynamic
?
Дел >
Поле участника статически включено.
Тип, выводящий часть, запутан.
Я думал, что this
преобразуется в тип base
в base::func1()
. В С++ base::func2()
не является полиморфизмом, поэтому вызывается base::func1()
.
Хотя в Java, base::func2()
является полиморфизмом, поэтому devried::func2()
вызывается.
Как предполагается, что привязка класса func2()
выводится? Или
Какой fun2()
должен быть вызван и как он определяется.
Что произошло за base::func1()
? Есть ли какой-либо листинг здесь для this
(от derive
до base
)?
Если нет, то как this
может достичь функции в классе base
?
void func1(){
func2();
}
Полезное обсуждение на coderanch.
Ответы
Ответ 1
В Java все методы, которые можно переопределить, автоматически virtual. Для него нет механизма выбора (virtual
keyword), как в С++ (и нет способа отказаться).
Java ведет себя так, как будто вы объявили base::func2
как
virtual void func2(){
printf(" I am in base:func2() \n");
}
В этом случае ваша программа напечатала бы "I am in derived:func2()"
.
Как предполагается, что привязка класса func2()
выводится?
Какой fun2()
должен быть вызван и как он определяется.
Для не виртуальных методов (методы С++ без модификатора virtual
) статический тип определяет, какой метод вызывать. Статический тип переменной определяется объявлением переменной и не зависит от того, как выполняется код.
Для виртуальных методов (методы С++ с модификатором virtual
и всеми методами Java) тип выполнения определяет, какой метод вызывать. Тип среды выполнения - это тип фактического объекта во время выполнения.
Пример: Если у вас есть
Fruit f = new Banana();
статический тип f
- Fruit
, а тип выполнения f
- Banana
.
Если вы выполняете f.someNonVirtualMethod()
, будет использоваться статический тип и будет вызываться Fruit::someNonVirtualMethod
. Если вы выполняете f.someVirtualMethod()
, будет использоваться тип среды выполнения и будет вызываться Banana::someVirtualMethod
.
Основная реализация того, как компилятор достигает этого, в основном зависит от реализации, но обычно используется таблица vtable. Подробнее см.
Если нет, то как this
может добраться до функции в классе base
?
void func1(){
func2();
}
Если вам интересно, почему func2()
здесь вызывает base
func2
, это потому, что
A) Вы находитесь в области base
, что означает, что статический тип this
равен base
, а
B) func2
в base
не является виртуальным, поэтому это статический тип, который решает, какую реализацию вызывать.
Ответ 2
В C++ вы можете переопределить функцию базового класса, только если она объявлена virtual
. Поскольку в вашем примере C++ вы не объявили func2() как virtual, поэтому он не был переопределен производным классом func2().
В то время как в Java вам не нужно объявлять функцию в базовом классе как виртуальную, чтобы переопределить их.
Рассмотрим этот пример Java.
class Base{
public void func2(){
System.out.println("Base class func2()");
}
}
class Derived extends Base{
public void func2(){
System.out.println("Derived class func2()");
}
}
class Main extends Base{
public static void main(String[] args) {
Derived derived = new Derived();
derived.func2();
Base base = new Derived();
base.func2();
}
}
Выход
Derived class func2()
Derived class func2()
Если вы хотите объявить функцию базового класса не виртуальной в Java, то объявите эту функцию как final
. Это предотвратит переопределение функции базового класса в производном классе.
Пример:
class Base{
public final void func2(){
System.out.println("Base class func2()");
}
}
Некоторые внешние ссылки принадлежат моему сайту.