假设我有以下类:
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
类中隐藏了另一个。为了消除这个错误,我必须覆盖所有的重载。这违背了我的预期,在我看来也不合理。为什么这个代码会这样做?
这个问题可以在这里重现。
事实上,在一个作用域中声明一个函数会在更大的作用域中隐藏任何同名的函数,因此派生类中的重写会隐藏基类中的重载。
这通常不是问题,因为您通常会通过基类与多态类进行交互;通常是您想要的,因为它可以防止对基类的更改意外地改变与派生类交互的代码的行为。
但是,如果你想的话,你可以很容易地将它带回派生类的范围:
using Base::myMethod;
或者用限定名称引用函数:
obj.Base::myMethod();
您的编译器是100%正确的
您重载了函数,将一个整数作为参数,然后此函数隐藏了所有基类函数-因此obj默认调用myMethod(int)
,但您没有为函数提供整数。如果您将代码修复为obj.myMethod(4);
问题解决了。
当重写派生类中的函数时,它会隐藏所有其他基类重载。仍然可以使用以下语法来调用基类:obj.Base::myMethod();
在更深入的回答中,这就是它发生的原因。首先,我们需要了解编译器是如何编译面向对象代码的。记住,这些函数编译成汇编命令,它们位于code segment
中。变量编译成一行行内存,这些内存位于堆栈、堆或数据段中。函数位于应用程序的一部分,变量位于完全不同的区域。编译器如何编译带有变量AND函数的类?嗯,事实并非如此。这个想法是这样的:
假设a有一个名为X的类,其中包含一些变量和一些函数
1)获取所有成员函数并将它们从类中带出
2)为每个现在全局声明的函数添加另一个参数-const X* this
3)每次看到语法x.someFunc(args..)
时,都会将其更改为someFunc(args..,&x)
4)在函数中-如果您不识别符号,请尝试将其附加为this->
,看看是否可以编译这个
5)将X编译为C结构,将另一个编译为C函数
6)对派生类也这样做(当然,还有虚拟表的问题,但让我们到此为止)
在您的示例中:
可能表示编译器解析代码的psuedo代码是
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);
但是编译器找不到任何与这些参数匹配的函数!对于那些最初不知道如何进行面向对象编译的人来说,这不是很直观,但在理解了这个编译过程后,它会更有意义。
通过覆盖派生类中的函数,可以隐藏基类中存在的该函数名称的所有重载实现。
因此,用void Derived::myMethod(int)
重写void Base::myMethod(int)
只为void Derived::myMethod(int)
生成代码,而不为void Derived::myMethod()
生成代码。
如前所述,您可以显式调用Base的函数:obj.Base::myMethod()
。