Функция переопределения в 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()");
  }
}

Некоторые внешние ссылки принадлежат моему сайту.