C#、C++和Java中重载和覆盖的一点问题
废话少说,先看一个简单的C++程序:
#include <iostream>
using namespace std;
class myFirst
{
public:
void funcA()
{
cout<<"这是myFirst类的非虚拟函数\n";
}
virtual void funcB()
{
cout<<"这是myFirst类的虚拟函数\n";
}
};
class mySecond : public myFirst
{
public:
void funcA()
{
cout<<"这是mySecond类的非虚拟函数\n";
}
virtual void funcB()
{
cout<<"这是mySecond类的虚拟函数\n";
}
};
int main()
{
mySecond bb;
① bb.funcA();
② bb.funcB();
③ ((myFirst*)(&bb))->funcA();
④ ((myFirst*)(&bb))->funcB();
⑤ ((myFirst)(bb)).funcA();
⑥ ((myFirst)(bb)).funcB();
return 0;
}
1和2的输出就不说了,很明确;5和6是向上强制转型(upcasting),将会造成对象的内容被切割;小弟不明白3和4的输出,主要就是(myFirst*)(&bb)这个表达式(我理解是把bb的地址形式转换为myFirst的地址形式),这个表达式对bb的内容有什么样的影响,对于输出的原因不太清楚,烦请各位高手解答一下,最好能从本质上解释。整个程序的输出结果如下所示:
这是mySecond类的非虚拟函数
这是mySecond类的虚拟函数
这是myFirst类的非虚拟函数
这是mySecond类的虚拟函数
这是myFirst类的非虚拟函数
这是myFirst类的虚拟函数
******************************************
下面是同样内容的C#程序:
using System;
using System.Collections.Generic;
using System.Text;
namespace 虚函数测试Cs
{
public class myFirst
{
public void funcA()
{
Console.WriteLine("这是myFirst类的非虚拟函数");
}
public virtual void funcB()
{
Console.WriteLine("这是myFirst类的虚拟函数");
}
};
public class mySecond : myFirst
{
new public void funcA()
{
Console.WriteLine("这是mySecond类的非虚拟函数");
}
public override void funcB()
{
Console.WriteLine("这是mySecond类的虚拟函数");
}
};
class Program
{
static void Main(string[] args)
{
mySecond bb = new mySecond();
① bb.funcA();
② bb.funcB();
③ ((myFirst)(bb)).funcA();
④ ((myFirst)(bb)).funcB();
}
}
}
1和2的输出很明确,不说了。
C# 和C++在类对象赋值运算符(=)这个地方不一样,C++是对象的复制,而在C#中,”=”复制它的引用,即地址,而不是复制对象。例如有一个类A ,在C++中, 代码A a;A b=a;是生成了两个类对象a和b,且这两个对象里面的数据成员的值应该是一样的,而在C#中,代码 A a = new A();A b=a;运行之后,b和a其实指向的地址是同样的,b只是a的一个引用。所以从这个角度来讲,我觉得在C#中,(myFirst)(bb)应该和C++中的(myFirst*)(&bb)在本质上是一样的,经验证,本程序中的3和4与C++程序中的3和4的输出结果是统一的。
本程序输出如下:
这是mySecond类的非虚拟函数
这是mySecond类的虚拟函数
这是myFirst类的非虚拟函数
这是mySecond类的虚拟函数
**********************************************
下面是同样内容的Java程序:
class myFirst
{
public void funcA()
{
System.out.println("这是myFirst类的非虚拟函数");
}
}
class mySecond extends myFirst
{
public void funcA()
{
System.out.println("这是mySecond类的非虚拟函数");
}
}
public class test {
public static void main(String[] args) {
mySecond bb=new mySecond();
① bb.funcA();
② ((myFirst)bb).funcA();
}
}
由于Java中没有虚拟函数,所以只能测试一下函数覆盖的情况。Java和C#类似,”=”复制类对象之间的地址,我原本以为1和2的输出应该和C#程序中的3和4的输出统一,不过结果却出乎我的意料,程序的输出如下:
这是myFirst类的非虚拟函数
这是myFirst类的非虚拟函数
所以小弟的第二个问题是这个Java程序这样输出的原因。另外,我刚刚学Java,看代码看的不是很多,但似乎没有见到Java中有(myFirst)bb这样的用法,是不是Java把C++中一切可能导致歧义的用法都抛弃不用了?呵呵,纯属瞎想,还请高手指出真正的原因。
问题点数:100、回复次数:3Top
1 楼guoxiaoli(半导体)回复于 2006-03-12 15:16:42 得分 50
针对C++
由于funcA是非虚函数,所以
((myFirst*)(&bb))->funcA();//这种对bb的强制类型转换可以按常规思维去考虑
一旦套上了虚函数的概念
((myFirst*)(&bb))->funcB();//就要按照虚函数的概念,子类的对象指针在调用虚函数的过程中要采取向下看的规则!
本质大概就是虚函数内部的机制和指针与引用的内部机制吧!
有待研究!
Top
2 楼junguo(junguo)回复于 2006-03-12 15:32:13 得分 50
③ ((myFirst*)(&bb))->funcA();
④ ((myFirst*)(&bb))->funcB();
其实是编译器在起作用,只有通过指针来调用函数,才会存在多态的情况.你的4中强制转换成了基类的指针,调用虚函数它就会通过虚拟函数表来调用.而虚拟函数表是在对象bb中,他就会调用派生类的对象而不是基类的函数.你可以找c++ 对象模型 那本书看看.
C#中的对象都是引用类型的,基类指向派生类的时候,调用虚函数就通过虚函数表.
java中应该有自己的虚函数机制,你写的好象不对.不过我也不熟悉java.Top
3 楼dx20()回复于 2006-03-12 22:41:01 得分 0
不好意思 那个Java的程序输出有错误,输出应该都是:这是mySecond类的非虚拟函数
请各位高手继续分析Top




