调用虚函数的逻辑不清晰(或者是方法隐藏?)

The logic of invoking virtual functions is not clear (or it is method hiding?)

(在 msvc2017 上测试)

struct AAA
{
    virtual float run(int arg)
    {
        return 5.5f;
    }
};

struct BBB : AAA
{
    virtual bool run(double arg)
    {
        return false;
    }
};

struct CCC : BBB
{
    virtual float run(int arg)
    {
        return 7.7f;
    }

    virtual bool run(double arg)
    {
        return true;
    }
};


CCC c;
BBB* pb = &c;
pb->run(5); // call CCC::run(double arg), WHY?? 
pb->run((int)5); // call CCC::run(double arg), WHY?? 

为什么 pb->run(5) 只调用 CCC::run(double arg),而不调用 CCC::run(int arg)

具有不同签名的子 class 的虚方法是否与基 class 的接口重叠?

当你

struct BBB : AAA
{
    virtual bool run(double arg)
    {
        return false;
    }
};

runAAA 中的 run 有不同的签名。这意味着 BBB::run(double) 将隐藏 AAA::run(int)。既然如此,您唯一可以从 BBB 调用的 runbool run(double arg)。当你这样做时

pb->run(5);

它找到 bool BBB::run(double arg),因为这是唯一可以从 BBB 静态调用的函数,然后虚拟调度开始调用 CCC::run(double)


为了得到要调用的函数的int版本,需要将int版本带入BBB。您可以通过编写一个来做到这一点,或者您可以使用 using AAA::run; 将其导入。执行其中任何一个都会使 pb->run(5); 从 [= 调用 runint 版本30=].


不要忘记,在使用多态性时,您应该将顶级析构函数(在本例中为 AAA)声明为虚拟的。这允许您在使用动态分配时正确删除对象。有关详细信息,请参阅:When to use virtual destructors?

一切都很简单。

class BBB 实际上有两个虚函数。一个在其基数 class AAA

中声明
struct AAA
{
    virtual float run(int arg)
    {
        return 5.5f;
    }
};

其他在 class BBB 本身中声明。

struct BBB : AAA
{
    virtual bool run(double arg)
    {
        return false;
    }
};

class BBB 中声明的函数隐藏了class AAA 中声明的函数。 (在派生 class 中声明的任何名称都隐藏了在派生 class 的基 class 中声明的同名实体)

在 class CCC 中,两个函数都被覆盖了。

这些函数调用

pb->run(5); // call CCC::run(double arg), WHY?? 
pb->run((int)5); // call CCC::run(double arg), WHY?? 

没有区别,因为它们的参数类型为 int

指针pb的静态类型是BBB *。因此编译器在 class BBB 中搜索名称 运行。

在 class 中只有一个同名函数可见。它是在 class

中声明的函数
virtual bool run(double arg)
{
    return false;
}

因此编译器 运行 使用此签名来调用此虚函数,但使用为 class CCC 定义的 table 虚函数指针调用它,因为指针的动态类型pbCCC *.

您可以通过 using 声明使 class AAA 中声明的函数在 class BBB 中可见。例如

struct BBB : AAA
{
    using AAA:: run;
    virtual bool run(double arg)
    {
        return false;
    }
};

在这种情况下,函数声明(在 class AAA 中声明)也是 class BBB 中的成员声明。那就是 class BBB 将有两个重载的不同虚函数的声明。

这是一个演示程序

#include <iostream>

struct AAA
{
    virtual float run(int arg)
    {
        return 5.5f;
    }
};

struct BBB : AAA
{
    using AAA:: run;
    virtual bool run(double arg)
    {
        return false;
    }
};

struct CCC : BBB
{
    virtual float run(int arg)
    {
        return 7.7f;
    }

    virtual bool run(double arg)
    {
        return true;
    }
};

int main() 
{
    CCC c;
    BBB* pb = &c;
    std::cout << pb->run(5) << '\n';
    std::cout << pb->run(5.6 ) << '\n';

    return 0;
}

它的输出是

7.7
1

为了使派生 class 及其基础 class 中成员声明的情况更清楚,请考虑块范围的类似情况。

这是一个演示程序

#include <iostream>

void f( int ) { std::cout << "void f( int )\n"; }
void f( double ) { std::cout << "void f( double )\n"; }

int main() 
{
    void f( double );

    f( 5 );
    f( 5.5 );

    return 0;
}

函数f在函数块范围内的内部声明main隐藏了函数在全局范围内的其他声明。

程序输出为

void f( double )
void f( double )