Java 与 C++ 中的函数重写

Function overriding in Java vs C++

Java 和 C++ 中的两个相似定义,但行为完全不同。

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()

C++ 版本:

#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,基础class函数func1()应该只能调用基础class函数func2(),因为它对派生的 class 一无所知。否则调用行为属于dynamic scope。 也许在 C++ 中,func2() in base class 是 bind static,但在 Java 中是 bind dynamic

成员字段是静态范围的。


类型推断部分令人困惑。 我以为thisbase::func1()中被转换为base类型。在C++中,base::func2()不是多态,所以调用了base::func1()。 而在Java中,base::func2()是多态性,所以调用devried::func2()

如何推断出func2()class绑定?要么 应该调用哪个 fun2() 以及如何确定。

base::func1()后面发生了什么? this(从 derivebase)这里有任何转换吗? 如果不是,this 是如何到达 base class 中的函数的?

        void func1(){
            func2();
        }

Useful discussion 在 coderanch 上。

在 Java 中,所有可以覆盖的方法都会自动 virtual。它没有选择加入机制(virtual 关键字),因为它在 C++ 中(也没有办法选择退出)。

Java 的行为就像您将 base::func2 声明为

virtual void func2(){
    printf(" I am in base:func2() \n");
}

在这种情况下,您的程序会打印 "I am in derived:func2()".

How the func2() class binding being inferred?
Which fun2() should be called and how it is determined.

对于非虚拟方法(没有 virtual 修饰符的 C++ 方法),静态类型 决定调用哪个方法。变量的静态类型由变量声明决定,不依赖于代码如何执行。

对于虚拟方法(带有 virtual 修饰符的 C++ 方法和 all Java 方法)它是 运行时类型 决定调用哪个方法。运行时类型是运行时实际对象的类型。

示例:如果你有

Fruit f = new Banana();

f的静态类型是Fruitf的运行时类型是Banana

如果您这样做 f.someNonVirtualMethod(),将使用静态类型并调用 Fruit::someNonVirtualMethod。如果您这样做 f.someVirtualMethod(),将使用运行时类型并调用 Banana::someVirtualMethod

编译器如何实现这一点的底层实现基本上取决于实现,但通常使用 vtable。详情参考

  • How Vtable of Virtual functions work
  • How does virtual method invocation work in C++?
  • Mechanism of Vptr and Vtable in C++

If no, how this is able to reach to the function in base class?

void func1(){
    func2();
}

如果您想知道为什么 func2() 在这里调用 basefunc2 那是因为

A) 你在 base 的范围内,这意味着 this 的静态类型是 base,而

B) base 中的 func2 不是 虚拟的,因此它是决定调用哪个实现的静态类型。

在 C++ 中 您只能重写基 class 函数,前提是它被声明为 virtual。由于在您的 C++ 示例中您没有将 'func2()' 声明为 virtual,因此它没有被派生的 class 'func2()`.

覆盖

而在 Java 中,您无需将基 class 中的函数声明为虚拟函数即可覆盖它们。

考虑这个 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 中将基础 class 函数声明为非虚函数,则将该函数声明为 final。这将防止在派生 class.

中覆盖基 class 函数

示例:

class Base{
  public final void func2(){
    System.out.println("Base class func2()");
  }
}

一些外部链接属于我的站点。