调用虚函数时出现奇怪的行为

5
我是一名有用的助手,可以为您翻译文本。

我不明白这段代码哪里出了问题。它看起来像一个难以捉摸的陷阱!

这段代码:

class Foo
{
  public:
      virtual double foo(double x) const = 0;
              double foo(int x) const { return (double)(x + x); }
};

class Bar : public Foo
{
    public:
        virtual double foo(double x) const { return x * x; }
};

int main()
{
    Bar* b = new Bar;
    Foo* f = b;
    std::cout << b->foo(3) << " " << f->foo(3) << std::endl;
    std::cout << b->foo(5.0) << " " << f->foo(5.0) << std::endl;
    return 0;
}

打印以下输出:
9 6
25 25

我推断当指针类型为Bar*时,会隐式转换调用Bar::foo(double) const。但是为什么这样的事情可以在没有任何警告的情况下发生?

我使用GCC 4.7.2。我使用g++ -Wall foobar.cpp -o foobar.exe进行编译。


2
我认为如果你选择的第二个数字不是2会更好。因为2*2=4且2+2=4。 - nabroyan
1
我很好奇,如果 foo(int) 的返回值也是 int,你是否有相同的问题?@nabroyan确实,最好使用相同的值(但不同的类型):33.0 - Kryptos
1
可能是 https://dev59.com/EHI-5IYBdhLWcg3w48xO 和 https://dev59.com/R3RC5IYBdhLWcg3wFdJx 的重复问题。 - Kryptos
你说得对。我已经改成了 5.0 - Caduchon
@Kryptos:是的,同样的行为。 - Caduchon
3个回答

9
这是因为名称隐藏。
当你在Bar中声明一个名为foo的函数时,你会隐藏Foo中所有具有相同名称的声明。
因此,当指针的静态类型为Bar时,编译器只能找到Bar中带有double参数的版本,因此它会隐式地将int转换为double以满足这个要求。
如果你想让Foo中的int版本可见,请添加一个using声明:
class Bar : public Foo
{
    public:
        using Foo::foo;
//      ^^ makes the versions in Foo visible
        virtual double foo(double x) const { return x * x; }
};

2
当类型为Bar*时,只有一个版本的方法是可见的,即带有double参数的版本。 具有相同名称但不同签名的基本方法被隐藏。 要使它们可用,您可以在派生类中使用using Foo::foo
根据您的编译器,我认为您可能还会收到有关隐式转换或您显然想调用隐藏方法的警告。

1
Foo中有两个重载的foo,一个接受double,另一个接受int
Bar中只有一个重载的foo,它接受double。此重载会隐藏所有来自基类的相同名称函数。这被称为名称隐藏
修复方法是使用using声明将基类中的其他foo重载引入到Foo派生类的范围内:
class Bar : public Foo
{
    public:
        using Foo::foo; 
        virtual double foo(double x) const { return x * x; }
};

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