为什么虚函数会被隐藏?

22

我有以下类:

class A {
public:
    virtual void f() {}
};


class B : public A{
public:
    void f(int x) {}
};
如果我说:
B *b = new B();
b->f();
编译器显示错误 C2660:"B::f":函数不接受 0 个参数。B 类中的函数不应该进行重载吗?因为它是虚函数,虚函数会被隐藏吗?
编辑:我确实想从 A 继承 B,这展示了相同的行为。

9
也许您希望从A中推导B? - Sven Marnach
6个回答

39

假设你想让B派生自A:

f(int)f()是不同的函数签名,因此是不同的函数。

您可以使用具有兼容签名的函数(即具有相同签名或返回类型“更具体”的函数)来覆盖虚函数(这称为协变)。

否则,您的派生类函数会隐藏虚函数,就像派生类声明与基类函数名称相同的函数的任何其他情况一样。您可以在类B中使用using A::f;以取消隐藏该名称。

或者,您可以将其调用为(static_cast<A*>(b))->f();b->A::f();。区别在于,如果B实际上覆盖了f(),则前者调用覆盖,而后者无论如何都调用A中的函数。


搞清楚了!我不知道为什么会认为使用 virtual 时不应该发生隐藏。 但现在我明白了。谢谢 :) - Oszkar
为什么我们不能用不同的返回类型隐藏虚函数?即使非虚函数允许使用不同的返回类型。 - Asif Mushtaq
@UnKnown:对于非虚拟函数,函数是完全独立的;A * a = new B(); a.nonVirtualFunction()将调用A::nonVirtualFunction,而不是B::nonVirtualFunction。但是对于虚拟函数,它将调用B::nonVirtualFunction,因此显然B::nonVirtualFunction必须至少与A::nonVirtualFunction兼容。 - ruakh

7

Class B没有继承自A,因此不存在函数F()。你可能是想要:

class A {
public:
    virtual void f() {}
};


class B : public A {
public:
    void f(int x) {}
};

编辑:我错过了实际的函数隐藏。请查看Steve Jessop的答案,以获得更详细的解释。


虽然你的发现很有价值,但它并不能解决问题(请参考Steve Jessops的回答)。 - Björn Pollex
哈哈,就像@SteveJessop一样,我在签名问题上跳了进去,甚至没有注意到B并不派生自A!+1 - wilhelmtell
@wilhelmtell:没错,我没有注意到缺失的继承,直到Rod指出来。 - Steve Jessop
确实,我忘记以那种方式编写它了,但是Steve Jessop的答案澄清了正确的情况。 - Oszkar

4
不需要,需要分别是"否"和"是"。如果您想要重载行为,则需要使用以下语句。
using A::f;

在B中。

1
确实,我错过了那个 :) 如果是这样的话,那就是你需要做的。 - Stuart Golodetz
是的,我是指派生它。谢谢! - Oszkar

2
B不是从A派生的,正确的声明如下:
class B : public A

2

2
当编译器有多种方法来解析一个符号时,它必须选择哪个具有优先权,除非代码告诉它不同。你所期望的是重载优先于覆盖。(over, over, over, aaaaack! Sorry, got 'over'whelmed).
这个例子中,B继承了一个虚方法,在其中子类提供了一个重载版本。重载是指在同一类中使用相同的方法名但不同签名的方法。由于B是A的子类,它正在覆盖f(),这意味着它不能同时成为一个重载。这就是为什么它被隐藏的原因。
对于类A,声明方法
virtual void f() {}  

作为虚拟方法,该方法将使用一定的规则解析,这些规则与您对 b 的声明不一致。
B *b = new B();

通过创建“B”的实例“b”,编译器无需使用“A”中相同名称方法的虚拟性。

如果你像这样声明了“b”

B *b = new A();  

那么调用 b->f(); 将会使用虚函数解析,确实指向 A 中的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接