【问题标题】:How do virtual functions work in C# and Java?虚函数在 C# 和 Java 中如何工作?
【发布时间】:2010-10-02 09:44:42
【问题描述】:

虚函数在 C# 和 Java 中是如何工作的?

它是使用类似于 C++ 的相同 vtable 和 vpointer 概念还是完全不同的东西?

【问题讨论】:

  • 是的,它们共享相同的概念。 Here 是 .NET Article 中关于 Wikipedia 上的虚拟方法表的方法调用描述的链接

标签: c# java virtual vtable


【解决方案1】:

虚拟函数在 Java 中是如何工作的?

编码面试官喜欢这个问题。是的。虽然 Java 没有 virtual 关键字,但 Java 有虚函数,你可以编写它们。

在面向对象编程中,virtual function 或虚拟方法是一种函数或方法,其行为可以在继承类中被具有相同签名的函数覆盖。这个概念是面向对象编程 (OOP) 的多态部分的一个非常重要的部分。

就这样的特定语言提出架构问题需要良好的沟通技巧和深入掌握 Java 编译器的基本原理,特别是接口、抽象类以及继承的工作原理。

引导面试官了解虚拟功能的具体示例。

是的,您可以使用接口在 Java 中编写虚函数。

Java interface methods 都是“纯虚拟的”,因为它们被设计为被覆盖。例如:

interface Bicycle {         //the function applyBrakes() is virtual because
    void applyBrakes();     //functions in interfaces are designed to be 
}                           //overridden.

class ACMEBicycle implements Bicycle {
    public void applyBrakes(){               //Here we implementing applyBrakes()
       System.out.println("Brakes applied"); //function, proving it is virtual.
    }
}

是的,您可以使用抽象类在 Java 中编写虚函数。

Java Abstract classes 包含隐含的“虚拟”方法,由扩展它的类实现。例如:

abstract class Dog {                   
    final void bark() {               //bark() is not virtual because it is 
        System.out.println("woof");   //final and if you tried to override it
    }                                 //you would get a compile time error.

    abstract void jump();             //jump() is a virtual function because it
}                                     //is part of an abstract class and isn't
                                      //final.  
class MyDog extends Dog{
    void jump(){
        System.out.println("boing");    //here jump() is being overridden, a 
    }                                   //demonstration that it is virtual.
}
public class Runner {
    public static void main(String[] args) {
        MyDog myDog = new MyDog();       //instantiating myDog
        myDog.jump();                    //calling the overridden function jump()
    }
}

你可以强制一个函数在泛型类中不是虚拟的,方法是把它设为final

例如:

class myJavaFoobarClass {

    final boolean giveMeTrueFunction()   //this Java function is NOT virtual
    {                                    //because final keyword prevents this
        return true;                     //function from being modified in a
    }                                    //subclass.

    boolean isItRainingFunction()   //this Java function IS virtual because
    {                               //without the final keyword, the function
        return false;               //can be overridden in a subclass.
    }
}

【讨论】:

    【解决方案2】:

    至少在 Java 中没有 virtual 关键字。

    它只是解析为您调用的任何方法的派生版本...

    class A{
    void sayhi(){ System.out.println("A");}
    }
    class B extends A{
    void sayhi(){ System.out.println("B");}
    }
    
    A a = new B();
    a.sayhi();
    

    将打印“B”。

    您可以通过声明类 Abstract 并保留已声明但未实现的纯虚拟方法来创建“纯虚拟”方法。或者通过使用接口/实现而不是类/扩展。接口基本上是一个类,其中所有方法都是纯虚拟的。这有一个额外的好处是一个类可以实现多个接口,因为与 C++ 不同,Java 类只能直接继承另一个类。

    编辑:


    针对您的评论,Naveen:

    如果你说 A a = new A(); a.sayhi();它会打印“A”。

    java 术语是动态的。您可以将其视为虚拟的,但这可能会使一些 Java 开发人员感到困惑。至少那些不懂 C++ 的人。在 Java 中没有显式指针,所以我们不需要担心虚拟/非虚拟。没有 VTable,您只需回溯类及其祖先,直到找到所需方法的实现。只有单一继承,所以你不必担心构造函数的顺序(它总是自下而上)。

    在 C++ 中,如果你有虚拟方法并执行类似的操作,你会得到不同的行为

    a->sayhi();
    

    a 是 A* 指向 B 的一个实例而不是

    a.sayhi();
    

    其中a是A类型的对象,持有B类型的对象

    【讨论】:

    • 我知道Java中没有virtual关键字,但默认情况下不都是virtual吗?顺便说一句,在上面的例子中,如果 A a = new A(); a.sayhi();在这种情况下,我们如何确定在运行时调用哪个函数?
    • 你为什么会期待其他的 A.sayhi() 被调用?你构造了一个 A 类型的对象,所以当然会执行 A.sayhi() 并打印“A”。
    • 感谢您的解释,我知道它现在在 Java 中是如何工作的了。
    【解决方案3】:

    Java 中的虚函数完全不同。在 Java 中,虚函数是指可以在其子类中覆盖的函数。所以Java的所有非静态方法都是虚函数。只有 final 和 private 函数没有被继承,所以它们不是虚函数。

    所以我们可以说Java中所有非final和private的非静态函数都是虚函数。

    【讨论】:

      【解决方案4】:

      所有支持多态的语言都使用 vtable 来解析对正确函数的方法调用。 Java 和 .NET 也是如此。

      它们都编译为某种中间语言(.NET 的 IL 和 java 的字节码),但 vtable 在这种中间语言中不可见。底层引擎(CLR for .NET)支持它

      【讨论】:

        【解决方案5】:

        我确信 Java 不使用 vtables,因为它能够支持二进制兼容性。

        澄清:vtable 是在编译派生/子类时构建的。如果基类/超类布局发生变化,则需要重新编译 d/s 的 vtable。在 Java 中,情况并非如此。您可以更改 b/s 而无需重新编译 d/s。

        嗯。 vtable 可能是在运行时构建的。加载类时。在这种情况下,Java 和 C++ 将相同但不同。

        【讨论】:

        • 不确定运行时使用的 vtable(即函数指针表)如何排除二进制兼容性?
        • 我在想,由于基础对象的布局可以改变查找必须经过一个额外的间接级别。
        • vtable 的实际存在取决于运行时的具体实现
        【解决方案6】:

        纯虚函数在 C++ 中。 C# 使用接口来达到同样的目的,所以我建议你走这条路。

        【讨论】:

          猜你喜欢
          • 2011-12-06
          • 2012-10-12
          • 1970-01-01
          • 1970-01-01
          • 2022-11-20
          • 2010-10-17
          • 1970-01-01
          • 2013-08-28
          • 2012-04-21
          相关资源
          最近更新 更多