如果我们在基础类,并在派生类中名称相同的函数的函数,可以在基类的函数从派生类对象叫什么名字?这是一个有趣的问题,作为实验可以预测以下C++程序的输出。
#include
using namespace std;
class Base
{
public:
int f(int i)
{
cout << "f(int): ";
return i+3;
}
};
class Derived : public Base
{
public:
double f(double d)
{
cout << "f(double): ";
return d+3.3;
}
};
int main()
{
Derived* dp = new Derived;
cout << dp->f(3) << '\n';
cout << dp->f(3.3) << '\n';
delete dp;
return 0;
}
该程序的输出为:
f(double): 6.3
f(double): 6.6
而不是假定的输出:
f(int): 6
f(double): 6.6
重载不适用于C++编程语言中的派生类。 Base和Derived之间没有重载分辨率。编译器查看Derived的范围,找到单个函数“ double f(double)”并调用它。它永远不会干扰Base的(封闭)范围。在C++中,没有作用域之间的重载-派生类作用域不是该一般规则的例外。 (有关更多示例,请参见此内容)
参考:www.stroustrup.com上的技术常见问题解答
现在考虑该程序的Java版本:
class Base
{
public int f(int i)
{
System.out.print("f (int): ");
return i+3;
}
}
class Derived extends Base
{
public double f(double i)
{
System.out.print("f (double) : ");
return i + 3.3;
}
}
class myprogram3
{
public static void main(String args[])
{
Derived obj = new Derived();
System.out.println(obj.f(3));
System.out.println(obj.f(3.3));
}
}
上面程序的输出是:
f (int): 6
f (double): 6.6
因此,在Java,重载可在与C++相反的范围内进行。 Java编译器根据用于调用方法的参数类型来确定要在编译时执行的重载方法的正确版本,这两个类的重载方法的参数都会接收在调用中使用的参数的值并执行重载方法。
最后,让我们尝试以下C#程序的输出:
using System;
class Base
{
public int f(int i)
{
Console.Write("f (int): ");
return i + 3;
}
}
class Derived : Base
{
public double f(double i)
{
Console.Write("f (double) : ");
return i+3.3;
}
}
class MyProgram
{
static void Main(string[] args)
{
Derived obj = new Derived();
Console.WriteLine(obj.f(3));
Console.WriteLine(obj.f(3.3));
Console.ReadKey(); // write this line if you use visual studio
}
}
注意:Console.ReadKey()用于暂停控制台。它类似于C / C++中的getch。
上面程序的输出是:
f(double) : 6.3
f(double): 6.6
代替假设的输出
f(int) : 6
f(double) : 6.6
说明:在这里,我们正在创建派生类的对象,因此编译器将优先考虑派生类,并在需要时执行隐式类型转换。因此,一旦编译器到达Console.WriteLine(obj.f(3));这行代码将检查参数兼容性。这里的3是int ,它与派生类函数f的double兼容。因此,编译器将执行int到double的隐式类型转换。因此,输出f(double):6.3将会出现。
现在,当编译器运行在Console.WriteLine(obj.f(3.3))时; ,它将再次赋予派生类优先级,并发现可调用。因此它将评估派生类函数f 。因此,输出f(double):6.6将会出现。
现在让我们再来看一次将基类函数f放入派生类中的情况,反之亦然,如下所示:
using System;
class Base
{
public double f(double i)
{
Console.Write("f (double) : ");
return i+3.3;
}
}
class Derived : Base
{
public int f(int i)
{
Console.Write("f (int): ");
return i + 3;
}
}
class MyProgram
{
static void Main(string[] args)
{
Derived obj = new Derived();
Console.WriteLine(obj.f(3));
Console.WriteLine(obj.f(3.3));
Console.ReadKey(); // write this line if you use visual studio
}
}
输出:
f (int): 6
f (double) : 6.6
您对预期的输出感到震惊吗?这怎么可能?
好吧,我们对这些问题有一个答案。由于我们已经创建了Derived类的对象,因此C#编译器将优先考虑derived,如果找不到任何兼容性,则将其用于基类。因此,当编译器位于Console.WriteLine(obj.f(3))时;的代码行,它将检查派生的类方法f ,并且发现可调用的,因此执行此操作,输出结果为f(int):6 。现在当Console.WriteLine(obj.f(3.3));这行代码将执行,将发现派生的类方法不适合作为3.3(double)
与int数据类型不兼容,因此编译器现在将更喜欢基类,并且在那里找到最佳匹配并执行该匹配。因此,该输出为f(双精度):6.6 。
原因与C++程序中说明的相同。像C++一样,在Base类和Derived类之间没有重载解析。在C#中,没有作用域之间的重载,派生类作用域也不是该一般规则的例外。这与C++相同,因为C#语言的创建者Anders hejlsberg认为,C#的设计与C++更加接近。