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
?
成员字段是静态范围的。
类型推断部分令人困惑。
我以为this
在base::func1()
中被转换为base
类型。在C++中,base::func2()
不是多态,所以调用了base::func1()
。
而在Java中,base::func2()
是多态性,所以调用devried::func2()
。
如何推断出func2()
class绑定?要么
应该调用哪个 fun2()
以及如何确定。
base::func1()
后面发生了什么? this
(从 derive
到 base
)这里有任何转换吗?
如果不是,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
的静态类型是Fruit
,f
的运行时类型是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()
在这里调用 base
的 func2
那是因为
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()");
}
}
一些外部链接属于我的站点。
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
?
成员字段是静态范围的。
类型推断部分令人困惑。
我以为this
在base::func1()
中被转换为base
类型。在C++中,base::func2()
不是多态,所以调用了base::func1()
。
而在Java中,base::func2()
是多态性,所以调用devried::func2()
。
如何推断出func2()
class绑定?要么
应该调用哪个 fun2()
以及如何确定。
base::func1()
后面发生了什么? this
(从 derive
到 base
)这里有任何转换吗?
如果不是,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?
Whichfun2()
should be called and how it is determined.
对于非虚拟方法(没有 virtual
修饰符的 C++ 方法),静态类型 决定调用哪个方法。变量的静态类型由变量声明决定,不依赖于代码如何执行。
对于虚拟方法(带有 virtual
修饰符的 C++ 方法和 all Java 方法)它是 运行时类型 决定调用哪个方法。运行时类型是运行时实际对象的类型。
示例:如果你有
Fruit f = new Banana();
f
的静态类型是Fruit
,f
的运行时类型是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 inbase
class?void func1(){ func2(); }
如果您想知道为什么 func2()
在这里调用 base
的 func2
那是因为
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 Base{
public final void func2(){
System.out.println("Base class func2()");
}
}
一些外部链接属于我的站点。