调用虚拟函数的逻辑尚不清楚(或者它是方法隐藏吗?)

dim*_*afo 9 c++ virtual inheritance overriding class

(在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?? 
Run Code Online (Sandbox Code Playgroud)

为什么pb->run(5)只打电话CCC::run(double arg),却不打电话CCC::run(int arg)

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

Vla*_*cow 12

一切都很简单。

BBB类实际上具有两个虚拟功能。一个在其基本类AAA中声明

struct AAA
{
    virtual float run(int arg)
    {
        return 5.5f;
    }
};
Run Code Online (Sandbox Code Playgroud)

其他声明在BBB类本身中。

struct BBB : AAA
{
    virtual bool run(double arg)
    {
        return false;
    }
};
Run Code Online (Sandbox Code Playgroud)

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

在CCC类中,这两个功能均被覆盖。

这些函数调用

pb->run(5); // call CCC::run(double arg), WHY?? 
pb->run((int)5); // call CCC::run(double arg), WHY?? 
Run Code Online (Sandbox Code Playgroud)

不要有所不同,因为他们的参数具有类型int

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

在该类中,只有一个具有该名称的函数可见。它是在类中声明的函数

virtual bool run(double arg)
{
    return false;
}
Run Code Online (Sandbox Code Playgroud)

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

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

struct BBB : AAA
{
    using AAA:: run;
    virtual bool run(double arg)
    {
        return false;
    }
};
Run Code Online (Sandbox Code Playgroud)

在这种情况下,函数的声明(在AAA类中声明)也将是BBB类中的成员声明。也就是说,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;
}
Run Code Online (Sandbox Code Playgroud)

它的输出是

7.7
1
Run Code Online (Sandbox Code Playgroud)

为了使派生类及其基类中的成员声明更加清楚,请考虑具有块作用域的类似情况。

这是一个示范节目

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

程序输出为

void f( double )
void f( double )
Run Code Online (Sandbox Code Playgroud)

  • @dimasafo它搜索名称。然后,它确定该名称属于一个函数,并使用该函数声明来调用其在类CCC中被覆盖的相应定义。 (2认同)

Nat*_*ica 7

当你做

struct BBB : AAA
{
    virtual bool run(double arg)
    {
        return false;
    }
};
Run Code Online (Sandbox Code Playgroud)

run具有不同的签名runAAA。这意味着BBB::run(double)将隐藏AAA::run(int)。既然可以了run,那么您唯一可以拨打的电话BBB就是bool run(double arg)。当你做

pb->run(5);
Run Code Online (Sandbox Code Playgroud)

它发现bool BBB::run(double arg)这是唯一可以从a静态调用的函数BBB,然后虚拟派遣开始调用CCC::run(double)


为了获得int要调用的函数的版本,您需要将其int引入BBB。您可以通过编写一个来实现,也可以using AAA::run;用来将其导入。执行其中任何一个都会pb->run(5);调用from 的int版本。runCCC


别忘了,在玩多态时,应该声明顶级析构函数(AAA在本例中为)是虚拟的。这使您可以在使用动态分配时正确删除对象。有关详细信息,请参见:何时使用虚拟析构函数?