Java最终方法与C ++非虚函数

问题描述

| java final方法和c ++非虚拟方法是不同还是相同?怎么样?     

解决方法

您仍然可以在C ++的继承类中声明具有相同签名的非虚拟成员函数,而Java明确禁止声明具有相同签名的方法,而基类在该方法中将方法声明为final。在处理继承/多态性时,C ++中的虚拟性仅有助于找到要调用的正确函数。 例:
#include <iostream>

class Base
{
public:
    void doIt()
    {
        std::cout << \"from Base.doIt()\" << std::endl;
    }
};

class Child : public Base
{
public:

    void doIt()
    {
        std::cout << \"from Child.doIt()\" << std::endl;
    }
};

int main()
{
    Base a;
    a.doIt(); // calls Base.doIt()
    Child b;
    b.doIt(); // calls Child.doIt()
    Base *c = new Base();
    c->doIt(); // calls Base.doIt()
    Child *d = new Child();
    d->doIt(); // calls Child.doIt()
    Base *e = new Child();
    e->doIt(); // calls Base.doIt()
    std::cin.ignore();
    return 0;
}
Java中使用final的可比较示例将导致编译器错误:
public class Base
{
    public final void doIt()
    {
        System.out.println(\"In Base.doIt()\");
    }
}

public class Child extends Base
{
    public void doIt() // compiler error: Cannot overload the final method from Base
    {
        System.out.println(\"In Child.doIt()\");
    }
}
有关C ++中多态性的更多说明,请参见cplusplus.com:多态性 实际上,这两种方法都有相似的目标:防止在基类中重写函数。他们只是以略有不同的方式进行处理。     ,他们是不同的。 C ++非虚拟方法不会被分派,并且不会覆盖任何内容。 调度Java final方法,并且可以在其类的超类中重写方法。 但是,就C ++非虚拟方法或Java final方法都不能重写而言,它们是相似的。从某种意义上说,它们也是相似的,如果您有某个对象的静态类型是所讨论的类型,则运行时系统无需调度该方法调用。 为了说明不同之处,请考虑以下两个Java类:
public class A {
    public String toString() {
        return \"A\";
    }
}

public class B extends A {
    public final String toString() {
        return \"B\";
    }
}

A a = ...
B b = ...
a.toString();  // could call A.toString() or B.toString() - dispatch
b.toString();  // could only call B.toString() - no dispatch required
               // but it will behave the same as if dispatching had occurred.
在B :: toString()是非虚拟的C ++等效语言中,我相信
a.toString()
无法分派给
B::toString()
。 (我对C ++有点生锈...) (实际上,Java JIT编译器能够检测到不需要虚拟调度的情况……而无需将类或方法声明为
final
。因此,ѭ5the的真正目的是指定不得重写方法或类不得扩展...,并请Java编译器为您检查)。     ,他们是非常不同的,实际上,我想说,是完全无关的。 在C ++中,如果基类具有非虚拟成员函数,则可以在派生类中声明具有相同名称的非虚拟成员函数。结果是派生类的成员函数将隐藏基类的成员函数。并且不会发生虚拟调度。如下所示:
struct Base {
  void foo() {
    std::cout << \"Base::foo called!\" << std::endl;
  };
};

struct Derived : Base {
  void foo() { 
    std::cout << \"Derived::foo called!\" << std::endl;
  };
};

int main() {
  Derived d;
  d.foo();    //OUTPUT: \"Derived::foo called!\"
  Base* b = &d;
  b->foo();   //OUTPUT: \"Base::foo called!\"
};
上面显示了派生类的成员函数如何隐藏基类函数。如果您有一个指向基类的指针,则由于这些函数是非虚拟的,因此不会使用虚拟表来解析该调用,因此将调用基类中的foo函数。这里的重点是,在C ++中,没有什么可以阻止您在具有相同名称的Derived类中创建另一个函数(请注意,不同的签名仍将导致所有具有相同名称的基类成员函数的隐藏)。您将得到的只是一个编译器警告,告诉您Derived类的成员函数隐藏了Base类的成员函数。 Java中的最终成员函数完全不同。在Java中,所有成员函数都是虚拟的。因此,您无法像在C ++中一样关闭虚拟调度。最终成员函数只是意味着不允许任何后续的派生类(将发生错误)声明具有相同名称(和签名)的成员函数。但是,在声明原始成员函数的接口/基类与将其标记为final的派生类之间仍然存在虚拟调度(因此,在动态多态意义上是覆盖)。只是严格禁止以后重写该函数(即,尝试在基类中将foo()标记为final的上述代码会在Derived类的声明中产生错误,因为foo()不会允许)。 如您所见,这两个概念完全不同。 在C ++中,使用非虚拟成员函数时,不会发生虚拟分派(因此,传统意义上没有\“ overriding \”),但是允许您使用具有相同名称的成员函数的派生类。有时在“静态多态”中很有用)。 在Java中,使用最终成员函数,仍然会进行虚拟调度,但是严格禁止在后续派生类中进行重写。     ,C ++使用虚函数与​​非虚函数可以产生性能差异,相反,Java中可能没有性能差异。在Java中,将方法标记为“ 5”是纯粹关于代码的清晰度和可维护性(这不是默认行为,相对很少使用),而在C ++中,非虚函数是默认行为,并且在某种程度上通常被使用,部分原因是因为它们具有更好的性能。性能特点。 在Java中,所生成的代码可能因使用方式而异,而C ++必须在编译时产生正确性。 例如如果JVM检测到\“ virtual \”方法仅具有一个或两个常用的实现,则可以内联这些方法或将\“ virtual \”方法仅使用一个实现作为最终方法对待。     

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...