java final methods vs c++ nonvirtual functions
are java final methods and c+开发者_如何学Go+ nonvirtual methods different or the same? How?
They are different.
C++ non-virtual methods are not dispatched, and do not override anything.
Java final methods are dispatched, and can override methods in their classes superclasses.
However, they are the similar in the respect that neither C++ non-virtual methods or Java final methods can be overridden. They are also similar in the sense that if you have some object whose static type is the type in question, the runtime system does not need to dispatch the method call.
To illustrate the difference, consider these two Java classes:
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.
In the C++ equivalent where B::toString() was non-virtual, I believe a.toString()
could not dispatch to B::toString()
. (I'm a bit rusty on my C++ ... )
(In fact, the Java JIT compiler is capable of detecting cases where virtual dispatching is not needed ... without you declaring classes or methods as final
. Thus, the true purpose of final
is to specify that a method shall not be overridden or a class shall not be extended ... and have the Java compiler check this for you.)
You can still declare non-virtual member functions with the same signature in inheriting classes in C++, where-as Java explicitly forbids declaring methods with the same signature in which the base class declares that method final. Virtuality in C++ just helps find the correct function to call when dealing with inheritance/polymorphism.
Example:
#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;
}
The comparable example in Java using final will result in a compiler error:
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()");
}
}
For more explanation on Polymorphism in C++, see cplusplus.com: Polymorphism
Effictively, though, both methods have similar goals: to prevent overriding of a function in the base class. They just go about it in slightly different ways.
They are very different, in fact, I would say, completely unrelated.
In C++, if a base class has a non-virtual member function, then, you can declare, in a derived class, a non-virtual member function with the same name. The effect will be that the derived class' member function will hide the base class' member function. And no virtual dispatching can occur. As in the following:
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!"
};
The above shows how the member function of the derived class hides the base class function. If you have a pointer to a base class, since the functions are non-virtual, the virtual table is not used to resolve the call and thus, the foo function from the base class will be called. The point here is that, in C++, nothing prevents you from creating another function in the Derived class with the same name (note that a different signature will still cause the hiding of all the base class' member functions with the same name). All you will get is a compiler warning telling you that the member function of the Derived class hides member function(s) of the Base class.
A final member function in Java is completely different. In Java, all member functions are virtual. So you can't turn-off the virtual dispatching like you can in C++. A final member function just means that any subsequent derived class will not be allowed (an error will occur) to declare a member function with the same name (and signature). But virtual dispatch (and thus, overriding, in the dynamically polymorphic sense) still occurs between the interface / base class that declared the original member function and the derived class that marked it as final. It is just that later overriding of the function is strictly forbidden (i.e. trying the above code with foo() marked as final in the Base class would give an error in the declaration of the Derived class, because the foo() there would not be allowed).
As you see, the two concepts are completely different.
- In C++, with a non-virtual member function, virtual dispatching does not occur (thus, no "overriding" in the traditional sense) but you are allowed to have derived classes with member functions of the same name (and it can be useful sometimes in "static polymorphism").
- In Java, with a final member function, virtual dispatching still occurs, but overriding in subsequent derived classes is strictly forbidden.
Using a virtual vs non-virtual function is C++ can make a performance difference, conversely there may be no performance difference in Java. In Java, marking a method as final
it is purely about clarity and maintainability of code (it is not the default behaviour and relatively rarely used), and in C++ non-virtual function are the default behaviour and commonly used in part because they have better performance characteristics.
In Java, the code generated can differ based on how it is used, whereas C++ must produce for correctness at compile time.
e.g. if the JVM detects that a "virtual" method has only one or two implementations common used it can inline those methods or treat a "virtual" method with only one implementation used as if it were final.
精彩评论