C++ 中的名称隐藏与 Java
Name Hiding in C++ vs Java
前几天我学到了一些关于 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++ 有什么用呢?为什么这会隐藏名称而不是重载它?
在 C++ 中,当基 class 中的一个函数与派生 class 中的一个函数同名时,就会发生名称隐藏。原因是函数调用过程的阶段。
在C++中,函数调用过程的阶段如下
- 姓名查询
- 过载分辨率
- 访问控制
一旦在派生 class ChildClass
中找到名称,名称查找就会停止查找其他名称。因此,ChildClass::someFunction()
隐藏了 ParentClass
.
中任何名称为 someFunction
的函数
名称查找过程后,重载解析失败,因为 ChildClass
中没有 someFunction()
。
核心区别在于,在 C++ 中,方法签名本质上只是方法名称,而在 Java 中,它是方法名称 和 其参数。
在您的情况下,通过使用相同的方法名称,您将覆盖父方法,因此不带参数的父方法不再可用。在 Java 中,您必须使用 both 具有相同名称 and 具有相同参数的方法来覆盖方法,因此在您的如果它们仍然可用。
关于 return 类型是否也应该包括在内的争论完全不同——我们不去那里。
如果您要问规则是什么,那么只要在一个范围内发现一个或多个重载,名称查找就会停止,并且不会查看任何更广泛的范围。因此,在您的情况下,someFunction
的搜索从 ChildClass
的范围开始,找到一个匹配项,然后停止。
只考虑名称,不考虑其用法(例如函数调用中的参数数量)、可访问性或其他任何内容。如果 none 个重载可用,搜索仍然不会继续到其他范围,并且程序格式错误。
如果您要问为什么规则是这样的,请考虑最初只有一个函数的情况:
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
的代码会突然改变行为,可能会导致令人头疼和冗长的调试会话。
如果您希望将派生 class 范围内的所有基 class 函数都包含在被视为重载的范围内,那么 using 声明就可以做到这一点。在你的情况下:
using ParentClass::someFunction;
或者,为了避免上述情况,但代价是一些乏味的措辞,您可以为您想要的特定重载编写转发函数:
void someFunction() {ParentClass::someFunction();}
前几天我学到了一些关于 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++ 有什么用呢?为什么这会隐藏名称而不是重载它?
在 C++ 中,当基 class 中的一个函数与派生 class 中的一个函数同名时,就会发生名称隐藏。原因是函数调用过程的阶段。
在C++中,函数调用过程的阶段如下
- 姓名查询
- 过载分辨率
- 访问控制
一旦在派生 class ChildClass
中找到名称,名称查找就会停止查找其他名称。因此,ChildClass::someFunction()
隐藏了 ParentClass
.
someFunction
的函数
名称查找过程后,重载解析失败,因为 ChildClass
中没有 someFunction()
。
核心区别在于,在 C++ 中,方法签名本质上只是方法名称,而在 Java 中,它是方法名称 和 其参数。
在您的情况下,通过使用相同的方法名称,您将覆盖父方法,因此不带参数的父方法不再可用。在 Java 中,您必须使用 both 具有相同名称 and 具有相同参数的方法来覆盖方法,因此在您的如果它们仍然可用。
关于 return 类型是否也应该包括在内的争论完全不同——我们不去那里。
如果您要问规则是什么,那么只要在一个范围内发现一个或多个重载,名称查找就会停止,并且不会查看任何更广泛的范围。因此,在您的情况下,someFunction
的搜索从 ChildClass
的范围开始,找到一个匹配项,然后停止。
只考虑名称,不考虑其用法(例如函数调用中的参数数量)、可访问性或其他任何内容。如果 none 个重载可用,搜索仍然不会继续到其他范围,并且程序格式错误。
如果您要问为什么规则是这样的,请考虑最初只有一个函数的情况:
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
的代码会突然改变行为,可能会导致令人头疼和冗长的调试会话。
如果您希望将派生 class 范围内的所有基 class 函数都包含在被视为重载的范围内,那么 using 声明就可以做到这一点。在你的情况下:
using ParentClass::someFunction;
或者,为了避免上述情况,但代价是一些乏味的措辞,您可以为您想要的特定重载编写转发函数:
void someFunction() {ParentClass::someFunction();}