【问题标题】:Function overriding in Java vs C++Java 与 C++ 中的函数覆盖
【发布时间】:2015-06-17 06:12:39
【问题描述】:

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,基类函数func1()应该只能调用基类函数func2(),因为它根本不知道派生类。否则调用行为属于dynamic scope。 也许在 C++ 中,基类中的func2() 是绑定static,但在Java 中是绑定dynamic

成员字段是静态作用域的。


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

func2() 类绑定是如何被推断出来的?或者 应该调用哪个fun2() 以及如何确定。

base::func1() 背后发生了什么? this(从derivebase)这里有演员吗? 如果不是,this 是如何访问base 类中的函数的?

        void func1(){
            func2();
        }

Useful discussion 在 coderanch 上。

【问题讨论】:

  • 很抱歉,这是我评论的最后一部分。我真的很想知道从base::func1() 调用func2() 时类型推断是如何实现的。
  • 你的意思是 func2() 类绑定是如何被推断出来的?
  • 没错。应该调用哪个fun2() 以及如何确定。
  • 扩展了我的答案。如果还不清楚,请告诉我。
  • 你的扩展很棒。但是如果解释base::func1()背后的事情会更有帮助。

标签: java c++ polymorphism overriding


【解决方案1】:

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

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

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

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

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

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

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

示例:如果您有

Fruit f = new Banana();

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

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

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


如果不是,this 如何访问base 类中的函数?

void func1(){
    func2();
}

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

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

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

【讨论】:

  • (相反,创建一个方法final 确实让你“选择退出”......)
  • @LouisWasserman 是也不是。基类决定不允许子类覆盖最终方法。您甚至不能声明具有相同签名的方法。据我了解,这与 C++ 有所不同。
  • 如果一个方法不能被覆盖,那么讨论它是否是虚拟的就没有意义了。我想说私有、最终和静态方法既不是虚拟的也不是非虚拟的,因为没有人知道如果有人设法覆盖它会调用哪个方法。
  • 但是如何理解base::func1() 呢?是不是要把thisderived类型改成base类型?
  • @aioobe:如果没有虚拟方法调用(基于实际运行时类的仲裁)还不够,你如何定义“非虚拟”?
【解决方案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()");
  }
}

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

【讨论】:

  • 根据您的链接的域/URL 与您的用户名相同或包含您的用户名,您似乎已链接到您自己的站点/您所属的站点。如果这样做,您必须披露它是您的网站。如果您不披露从属关系,则将其视为垃圾邮件。请参阅:What signifies "Good" self promotion?the help center on self-promotion。披露必须是明确的,但不需要是正式的。如果是您自己的个人内容,则可以是“在我的网站上……”、“在我的博客上……”等。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-17
  • 2010-10-11
  • 2023-03-04
  • 1970-01-01
相关资源
最近更新 更多