【问题标题】:Virtual function calling a non-virtual function and vice versa虚函数调用非虚函数,反之亦然
【发布时间】:2021-07-07 22:48:11
【问题描述】:

我有一个class A 作为class B 的基类。

我在我的虚拟函数xyz()中调用了非虚拟函数abc(),如下所述。

由于运行时多态性,B:xyz 被调用——我明白这一点。

但是,我不明白,为什么后面是B:abc 而不是A:abc,因为abc 是一个非虚拟函数。

请注意:我遇到了以下问题:Virtual function calling a non-virtual function。它提到在虚函数中调用abc() 等效于this->abc(),因此是输出。但是,我不确定我是否理解这部分。

因为,当我做相反的事情(即调用虚函数的非虚函数)时,会显示正确的运行时多态性。那么 this 指针会发生什么?

//Virtual function calling non-virtual 
class A
{
  public:
  void abc()
  {
    cout<<"A:abc"<<endl;
  }

  virtual void xyz()
  {
    cout<<"A:xyz"<<endl;
    abc();
  }
};


class B: public A
{
  public:
  void abc()
  {
    cout<<"B:abc"<<endl;
  }

  void xyz()
  {
    cout<<"B:xyz"<<endl;
    abc();
  }
};

int main() {

  A *obj3 = new B;
  obj3->xyz();\
  return 0;
}
Output
B:xyz
B:abc
//Non-virtual calling virtual function
#include <iostream>
using namespace std;

class A
{
  public:

  void abc()
  {
    cout<<"A:abc"<<endl;
    xyz();
  }

  virtual void xyz()
  {
    cout<<"A:xyz"<<endl;
  }
};

class B: public A
{
  public:

  void abc()
  {
    cout<<"B:abc"<<endl;
    xyz();
  }

  void xyz()
  {
    cout<<"B:xyz"<<endl;
  }
};

int main() {

  A *obj3 = new B;
  obj3->abc(); 
  return 0;
}
Output
A:abc
B:xyz

【问题讨论】:

  • 请澄清一下但是我不明白,为什么后面跟着B:abc而不是A:abc,因为abc是非虚拟函数你是什么意思我的后跟 B:abc?据我所知,B::abc 从未被调用过。
  • @user4581301 检查第一个案例的输出

标签: c++ dynamic-binding run-time-polymorphism


【解决方案1】:

对您的非虚拟 abc 函数的调用在编译时被有效地解析:因此,当从@987654323 的另一个成员函数 调用它时@,函数的class B 版本被调用,并被传递一个指针(this)指向它被调用的对象;同样,如果从class A 函数内部 调用,则将使用class A 定义。也就是说,对于编译器而言,非虚函数与一个类相关联,而不是与该类的任何特定实例相关联。

但是,您的 virtual xyz 函数由编译器以不同方式处理;在这种情况下,函数的引用或指针被添加到类定义中(这通常被添加到所谓的vtable 中,尽管细节是特定于实现的);当您的类的任何对象被创建时,它们包括该函数指针和/或 vtable 的副本。当编译器看到调用此类虚函数的代码时,它会将其转换为调用通过适当的函数指针;因此,函数与 actual 对象一起“旅行”:函数是从派生类还是基类(在您的代码中)调用的无关紧要 - 调用的函数是属于该对象的函数(实例)从中调用它。

总而言之:对非虚函数的调用在编译时解析,而对虚函数的调用(在概念上)在运行时解析。

要查看此“vtable”的实际创建过程,请尝试编译并运行以下代码:

#include <iostream>

class A {
public:
    int i;
    ~A() = default;
    void foo() { std::cout << i << std::endl; }
};

class B {
public:
    int i;
    virtual ~B() = default;
    virtual void foo() { std::cout << i << std::endl; }
};

int main()
{
    std::cout << sizeof(A) << std::endl;
    std::cout << sizeof(B) << std::endl;
    return 0;
}

两个类之间的唯一区别在于一个具有虚函数而另一个没有——但这会导致类对象的大小存在显着差异:vtable 的大小(与,可能是一些“填充”以优化数据对齐)! (在我的 64 位 Windows 上,使用 MSVC,我得到 4 和 16 的大小,但实际值会因编译器和平台而异。)

【讨论】:

  • 所以即使从成员函数中调用虚函数,使用的是“实际对象”而不是“this”?
  • this 指针仍在使用(它是任何非静态成员函数调用的“不可见”参数);事实上,它甚至更重要,因为它(即 this)指向具有 vtable 的对象。
  • 代码2:A类的非虚函数被调用,即abc()。在类 A 的这个成员函数中,调用了一个虚函数 xyz。那么在A类的成员函数中,如果用vptr来解析,那不是用A类的this->vptr吗?它是如何正确调用派生类函数的。
  • @SumaiyaA 再次阅读我的答案:vtable 取自 运行时的每个对象,代码 2 中的 this 指针指向一个B 对象(因为它是用new B 创建的)。这就是多态性:对象(和它的指针)带着它的虚函数,无论去哪里
  • “而对虚函数的调用是(并且只能)在运行时解决的”。优化器可能会按照 as-if 规则“去虚拟化”代码。从 OP 代码中,我们“知道”obj3 的动态类型是 B
猜你喜欢
  • 2012-02-06
  • 2015-09-17
  • 1970-01-01
  • 1970-01-01
  • 2015-04-20
  • 2020-07-02
  • 1970-01-01
  • 1970-01-01
  • 2017-11-29
相关资源
最近更新 更多