【问题标题】:Dynamic dispatch mechanism and the process of invoking the correct function in C++C++中动态调度机制和调用正确函数的过程
【发布时间】:2021-06-01 13:15:35
【问题描述】:

我了解 vptr 和 vtable 的概念以及它们在实现此机制中的作用。

编译器为每个具有至少一个虚函数的类构造一个vtable,并为每个类实例添加一个vptr指针,以便它可以访问vtable并在运行时执行正确的函数,但我无法理解究竟是如何。

考虑以下代码:

struct A
{
  virtual void foo() { cout << "A::foo" << endl; }
  virtual void goo() { cout << "A::goo" << endl; }
};

struct B
{
  virtual void goo() { cout << "B::goo" << endl; }
  virtual void foo() { cout << "B::foo" << endl; }
};

int main()
{
  A *r = (A *)(new B());
  r->foo();
}

输出:

B::goo

您能解释一下 B::goo 是如何以及为什么被处决的吗?编译器是否在编译时确定了在 vtable 中查找的位置? 谢谢。

编辑:

上面的代码sn-p只是一个例子,我认为可以让你理解我不理解的机制的哪一部分——很明显,我错了:)

请看下面的例子:

struct A
{
  virtual void foo() {/* some implementation */}
  virtual void goo() {/* some implementation */}
};

int main()
{
  A *a = new A();
  a->foo(); // <=====
}

对于这个例子,编译器构造了一个vtable,里面有两个函数指针,一个指向foo,另一个指向goo。 调用 foo 时,究竟发生了什么?编译器如何知道要使用哪个指针(在 vtable 中)?希望这说明清楚。

【问题讨论】:

  • reinterpret_cast 经常导致 UB。
  • 当我的机器尝试执行 r-&gt;foo(); 时崩溃了这就是 未定义行为 的好处,你看到的结果和我看到的结果一样好看到了。
  • 这能回答你的问题吗? How are virtual functions and vtable implemented?

标签: c++ dynamic-dispatch function-invocation


【解决方案1】:

您的示例具有未定义的行为:当您尝试通过 A 指针对 B 对象进行别名时,您违反了 the strict aliasing rule

与未定义的行为一样,所有的赌注都没有了,对程序行为的任何进一步分析都是徒劳的。

【讨论】:

  • 感谢您的回答,代码 sn-p 只是一个示例(可能不是一个很好的示例),让您了解我不了解的机制的哪一部分。在我没有违反任何东西的正常情况下,编译器如何知道要调用 vtable 中的哪些函数?
  • @katkato 这是编译器特定的细节,但通常 vtable 条目的顺序是首先声明虚函数的顺序。在你的代码中,r-&gt;foo() 想要调用第一个虚函数(因为fooA 中是第一个)。当r 指向B 时,它会在B 的vtable 中查找它并找到B::goo,因为它具有相同的签名,所以可能只适用于这种情况。
猜你喜欢
  • 1970-01-01
  • 2013-11-24
  • 2011-06-09
  • 1970-01-01
  • 2019-07-04
  • 1970-01-01
  • 2021-07-25
  • 2021-08-31
  • 1970-01-01
相关资源
最近更新 更多