调用虚函数时的奇怪行为

Strange behaviour when calling virtual functions

本文关键字:函数 调用      更新时间:2023-10-16

我不明白这段代码出了什么问题。这看起来像一个不可思议的陷阱!

代码:

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编译

这是由于名称隐藏。

当您在Bar中声明一个名为foo的函数时,您将在Foo中隐藏所有具有相同名称的声明。

因此,当指针的静态类型为Bar时,编译器只在中找到Bar中接受double的版本,因此它隐式地转换int以满足这一点。

如果您希望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; }
};

当类型为Bar*时,只有一个版本的方法可见,即带有double参数的版本。

具有相同名称(但不同签名)的基方法被隐藏

要使它们可用,可以在派生类中使用using Foo::foo

根据您的编译器,我认为您可能还会收到有关隐式转换的警告,或者您显然想要调用隐藏方法的事实。

Foo中有两个foo的重载,一个是double,另一个是int

Bar中有一个foo的重载,它接受一个double。此重载将基类中同名的所有函数隐藏起来。这叫做名称隐藏

一个修复方法是使用声明将基类中的其他foo重载引入Foo派生类的作用域:
class Bar : public Foo
{
    public:
        using Foo::foo; 
        virtual double foo(double x) const { return x * x; }
};