从继承函数中调用虚拟函数

Calling a virtual function from within an inherited function?

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

我试着在脑子里想清楚,但老实说,我不知道这里到底发生了什么
在下面的示例中添加和删除虚拟关键字时,到底发生了什么

#include <iostream>
#include <string>
class A {
public:
    A() { me = "From A"; }
    void caller() { func(); }
    virtual void func() { std::cout << me << std::endl; } // THIS LINE!
private:
    std::string me;
};
class B : public A {
public:
    B() { me = "From B"; }
    void func() { std::cout << me << std::endl; }
private:
    std::string me;
};
int main() {
    A a;
    a.caller();
    B b;
    b.caller();
    return 0;
}

使用虚拟关键字,它会打印"来自A",然后打印"来自B"
在没有虚拟关键字的情况下,它会打印"From A",然后是"From"

到目前为止,这是我唯一一次发现在不涉及指针的情况下使用虚拟函数。我认为,如果删除虚拟关键字,编译器会做标准的事情,即重载继承的函数,最终打印"From A"answers"From B"

我认为这不仅仅是VTable,更重要的是它在特定情况下的行为方式。B有VTable吗?

调用

func()

相当于

this->func()

因此,包含一个指针。

不过,没有必要涉及指针来理解行为。

func在静态已知类型中是虚拟的时,即使是b.func()的直接调用也必须工作,就好像是虚拟调用一样。编译器可以在知道b的最派生类型的基础上对其进行优化。但这是另一种考虑(优化几乎可以做任何事情)。

除了虚拟调度的问题之外,可能带来额外混乱的是您有两个me,一个在A中声明,另一个在B中声明。这是两个不同的对象。

CCD_ 7类型的对象具有两个CCD_ 8类型的数据成员;一个单独存在,一个合并到类型为A的子对象中。然而,后一个在类型为B的方法中并不立即可用,因为它的名称被该类中引入的新me所掩盖(尽管您可以使用限定名称A::me来指代它)。

因此,即使A::funcB::func的主体看起来相同,但在它们两者中使用的标识符me指代不同的成员。

在您的示例中,您不会看到区别:

  • 使用虚拟函数,编译器将通过VTable生成一个调用,在运行时,每个对象都将为其真实类调用正确的函数。

  • 对于非虚拟函数,编译器在编译时根据对象定义的类确定要调用的正确函数。

现在尝试以下操作,查看虚拟功能的作用:

A  *pa = &b;       // pointer to an A: valid as b is a B wich is also an A.  
pa -> caller();    // guess what will be called if virtual or not. 

不需要指针来尝试虚拟函数。你也可以在参考文献中观察到同样的效果:

A& ra = b;       // create a reference to an A, but could as well be a parameter passed by reference.
ra.caller(); 

虚拟函数对多态性很有用。这个想法是,你使用一个类的通用对象,但在编译时你不知道这个对象在运行时是否真的属于这个类,或者它是否不是一个更专业化的对象(从类继承)。