假设我有以下类:
class Base
{
public:
virtual void myMethod()
{
}
virtual void myMethod(int x)
{
}
};
class Derived : public Base
{
};
在这种情况下,以下代码编译得很好。
int main(void)
{
Derived obj;
obj.myMethod();
return (0);
}
当我尝试覆盖以下myMethod
之一时出现问题。
class Derived : public Base
{
public:
virtual void myMethod(int x)
{
}
};
现在代码无法编译并提供错误:
错误C2660:'Derived :: myMethod':函数不带0参数
我已经覆盖了其中一个重载函数,显然这已隐藏了Derived
类中的另一个。为了摆脱错误,我必须覆盖所有重载。这违背了我的期望,对我来说似乎不合理。为什么这段代码会这样?
问题可以复制here。
答案 0 :(得分:1)
你的编译器100%正确。
你重载了你的函数以取一个整数作为参数,然后这个函数隐藏了所有的基类函数 - 所以obj调用myMethod(int)
是默认的,但是你没有为你的函数提供一个整数。
如果你修改你的代码
obj.myMethod(4);
当覆盖派生类中的函数时 - 它隐藏了所有其他基类重载。仍然可以使用以下语法调用基类:
obj.Base::myMethod();
在更深入的回答中,这是为什么会发生这种情况。
首先,我们需要了解编译器如何编译面向对象的代码。记住 - 函数编译成汇编命令,它们位于code segment
。变量编译成在堆栈,堆或数据段中枯萎的内存行。函数位于应用程序的一部分,变量位于完全不同的区域。编译器如何使用变量和函数编译类?好吧,它没有。这个想法是这样的:
假设有一个名为X的类,带有一些变量和一些函数
1)承担所有成员职能并将其带出课堂
2)为每个现在全局声明的函数添加另一个参数 - const X* this
3)每当您看到语法x.someFunc(args..)
时,将其更改为someFunc(args..,&x)
4)在函数中 - 如果你不识别符号 - 尝试将它附加this->
,看看你是否可以编译这个
5)将X编译为C结构,将另一个编译为C函数
6)对派生类做同样的事情
(当然,有虚拟表问题,但让我们停在这里)
在你的例子中:
可能代表编译器解析代码的伪代码是
struct Base{}
struct Derived{}
void myMethod(const Base* this);
void myMethod(int x,const Base* this);
void myMethod(int x, const Derived* this);
//what you tried to do is :
myMethod (&obj);
但是编译器找不到与这些参数匹配的任何函数! 对于最初不了解面向对象编译的人来说,这不是很直观,但是在理解了这个编译过程之后它会更有意义。
答案 1 :(得分:1)
实际上,在一个范围内声明一个函数会在更宽的范围内隐藏任何具有相同名称的函数,因此派生类中的覆盖会隐藏基类中的重载。
这通常不是问题,因为您通常会通过基类与多态类进行交互;通常是你想要的,因为它可以防止对基类的更改意外地改变与派生类交互的代码的行为。
但如果你愿意,你可以轻松地将它带回派生类的范围:
using Base::myMethod;
或通过限定名称引用该功能:
obj.Base::myMethod();
答案 2 :(得分:0)
通过覆盖Derived类中的函数,可以隐藏Base类中存在的该函数名的所有重载实现。
因此,使用void Base::myMethod(int)
覆盖void Derived::myMethod(int)
只会为void Derived::myMethod(int)
生成代码,而void Derived::myMethod()
则不会生成代码。
如前所述,您可以明确地调用Base的功能:
obj.Base::myMethod()
。