前几天我学到了一些关于C ++的新知识;以下不起作用:
class ParentClass {
public:
void someFunction() { printf("ParentClass::someFunction()"); }
};
class ChildClass : public ParentClass {
public:
void someFunction(int a) { printf("ChildClass::someFunction(int)"); }
};
int main() {
ChildClass childClass;
// This call is a compiler error.
// I would expect it to call ParentClass::someFunction()
childClass.someFunction();
}
然而,在Java(以及其他语言)中做同样的事情就像我期望的那样:
public class ParentClass {
public void someFunction() { System.out.println("ParentClass"); }
}
public class ChildClass extends ParentClass {
public void someFunction(int a) { System.out.println("ChildClass"); }
}
public class Main {
public static void main(String[] args) {
ChildClass childClass = new ChildClass();
// The following prints "ParentClass"
childClass.someFunction();
}
}
那么在C ++中给出了什么?为什么这会隐藏名称而不是重载它?
答案 0 :(得分:3)
如果您询问规则是什么,那么只要在一个范围内发现一个或多个重载,名称查找就会停止,并且不会查看任何更宽的范围。因此,在您的情况下,someFunction
的搜索范围从ChildClass
开始,找到匹配项并停止。
仅考虑名称,而不考虑其用法(例如函数调用中的参数数量),可访问性或其他任何内容。如果没有任何重载可用,搜索仍然不会继续到其他范围,并且程序格式不正确。
如果您问为什么规则是这样的,请考虑最初只有一个函数的情况:
struct Base {};
struct Derived : Base {void f(int);}
并且某人使用与
不匹配的类型调用它Derived d;
d.f(42.0); // OK: double converts to int
现在假设某人对Derived
一无所知,决定Base
可以使用其他功能:
struct Base {
void f(double); // Completely unrelated to D::f
};
在C ++规则下,使用D::f
的代码将忽略该函数,该函数将继续像以前一样工作。如果新函数被认为是一个重载,它将是一个更好的匹配,使用D::f
的代码会突然改变行为,可能会导致很多令人头疼的冗长的调试会话。
如果要将派生类范围内的所有基类函数包含在内以作为重载,那么using声明将执行此操作。在你的情况下:
using ParentClass::someFunction;
或者,为了避免上述情况以某些繁琐的措辞为代价,您可以为所需的特定过载编写转发函数:
void someFunction() {ParentClass::someFunction();}
答案 1 :(得分:1)
在C ++中,当基类中的一个函数与派生类中的一个函数同名时,可以发生名称隐藏。原因是函数调用过程的各个阶段。
在C ++中,函数调用过程的各个阶段如下
只要在派生类ChildClass
中找到名称,名称查找就会停止查找其他名称。因此,ChildClass::someFunction()
会隐藏someFunction
中名称为ParentClass
的任何函数。
在名称查找过程之后,由于someFunction()
中没有ChildClass
,因此重载解析失败。
答案 2 :(得分:1)
核心区别在于,在C ++中,方法签名本质上只是方法名称,而在Java中,方法名称和是其参数。
在您的情况下,通过使用相同的方法名称,您将覆盖父方法,因此不再使用不带参数的父方法。在Java中,您必须使用两者具有相同名称的方法覆盖并且具有相同的参数来覆盖方法,因此在您的情况下它们仍然可用。
关于是否还应该包括返回类型存在完全不同的争论 - 让我们不去那里。