【问题标题】:Pointer-to-member-function performs virtual dispatch?指向成员函数的指针执行虚拟调度?
【发布时间】:2021-10-31 07:40:56
【问题描述】:

我执行了以下代码。

#include <iostream>

class Base
{
public:
    virtual void func()
    {
        std::cout<<"Base func called"<<std::endl;
    }
};

class Derived: public Base
{
public:
    virtual void func()  override
    {
        std::cout<<"Derived func called"<<std::endl;
    }
};

int main()
{
    void (Base::*func_ptr)()=&Base::func; //Yes, the syntax is very beautiful.
    Base* bptr=new Derived();
    (bptr->*func_ptr)();
}

我的预期输出是Base func called。然而,相反,输出是

Derived func called

这让我很惊讶,因为我认为func_ptr 应该只能看到Base 成员(因为我认为func_ptr 不是通过_vptr 访问成员函数,而是函数地址本身。

我想知道,在这种情况下,虚拟调度是如何发生的(对虚拟表的访问是如何发生的),以及这种行为在 C++ 标准中的定义位置(我不能找到什么)?

【问题讨论】:

  • 我相信这是作为“偏移量”实现的,因此它会从this 指针加上偏移量指向它所指向的任何类型。因为它指向一个实际的 Derived 对象,所以它是正在运行的函数。
  • 如果你想一想,当你写bptr-&gt;func时,编译器也将func解析为Base::func,只有在运行时它才会被解析为正确的调用。
  • 标准中没有提到虚拟表。它们是特定编译器的实现细节。 VTable 并不是实现虚拟方法的唯一方式,它只是最常见的方式。
  • 指向成员函数的指针与指针不同。当它指向的成员函数是一个虚函数时,它有足够的数据来处理虚调度。

标签: c++ function-pointers virtual-functions member-pointers


【解决方案1】:

参考[expr.call],具体是here

[如果选择的函数是虚拟的],它在对象表达式的动态类型中的最终覆盖器被调用;这样的调用称为虚函数调用

无论是通过指针调用还是通过类成员访问调用函数都是一样的(ref);为虚函数调用的实际函数最终取决于被调用对象的实际类型

[class.virtual] 下标准中的一些(非规范性)注释说了很多相同的事情:

[注3:虚函数调用的解释取决于被调用对象的类型(动态类型)

[注 4:[...] 虚函数调用依赖于特定对象来确定调用哪个函数。


如果您想知道如何虚函数派发是如何发生的,您需要寻找一个具体的实现,因为如何没有标准化。 p>

(我真的很喜欢这篇文章 A basic glance at the virtual table,它展示了使用 C 实现它的一种可能方式)

【讨论】:

  • 现在,经过编辑,对我来说更有意义了,谢谢。
  • 是否可以得到不会引起虚拟调度的函数指针?
  • @MarekR 获取剪切对象类型的指针,如果您打算向下转换为基础对象,并重新解释转换指针。它是 UB 和您的良心。
  • @Swift-FridayPie 如果是 UB,那么这不是解决方案。另外请提供示例,因为您的描述对我来说含糊不清。
  • @MarekR: 获取 member 函数指针是不可能的,但如果您编写另一个函数包装对将显式调用基类的成员函数的引用的调用。这甚至可以用于非捕获 lambda,如下所示:godbolt.org/z/74WvGP698
【解决方案2】:
  1. 您的理解有误。
  2. 这是一个实现细节。不同的编译器可能会以不同的方式实现它。如有疑问,look at the assembly。在这个特定的编译器中,它实际上非常聪明。如果存储在指向成员的指针中的值是偶数,则它是一个直接函数指针(所有函数都位于偶数地址)。如果它是奇数,它是 vtable 中的偏移量,加一。所有的 vtable 偏移量也是偶数,因此要获得实际指针,它会减 1。

【讨论】:

  • “你的理解是错误的”——这就是为什么我在这里。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-12-17
  • 2011-07-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-08
  • 1970-01-01
  • 2013-01-26
相关资源
最近更新 更多