2

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

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

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

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

请注意:我遇到了以下问题:Virtual function calls 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
4

1 回答 1

4

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

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

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

要查看此“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 的大小,但实际值会因编译器和平台而异。)

于 2021-04-12T21:11:31.410 回答