C++中通过虚表访问函数

Accessing functions through virtual tables in C++

我有两个类 B和D

class B{
public:
  virtual int prva()=0;
  virtual int druga(int)=0;
};

class D: public B{
public:
  virtual int prva(){return 42;}
  virtual int druga(int x){return prva()+x;}
};

我的任务是创建一个函数,它接受指向对象 B 的指针并打印 returning 方法 'prva' 和 'druga' 的值而不使用它们的名称(通过虚拟访问 table)

我写了下面的代码,它成功地打印了方法 'prva' 的 returning 值,但是对于第二种方法 'druga'

却没有做同样的事情
typedef int (*PTRFUN1)(void);
typedef int (*PTRFUN2)(int);

void function (B* var){
    int* vptr =  *(int**)var;
    PTRFUN1 fun1 = (PTRFUN1)vptr[0];
    PTRFUN2 fun2 = (PTRFUN2)vptr[1];
    pprintf("Prva:%d\n", fun1());
    printf("Druga:%d\n", fun2(2));
}

int main(void){
    B *pb = new D();
    function(pb);
}

这段代码执行打印:"Prva:42"

无法在 'druga' 中调用 prva(),我不明白为什么。

此外,如果我简单地删除 prva() 的调用并让主体只是 return x,方法 'druga' 将始终 return“42”或任何数字我让 'prva' return 无论我尝试通过 fun2()

发送什么参数

任何ides我在这里做错了什么,我应该如何访问方法?

无法在标准 C++ 中访问虚拟 table。事实上,该语言甚至没有虚拟 table 是什么的概念。 Virtual table 是实现动态调度的特定(且流行)方式。

Any ides what am I doing wrong here

当您通过 vptr 间接访问时,程序的行为未定义。

一些实现可能有办法在 C++ 中访问 table,但没有标准的方法。如果您的编译器没有,那么唯一的方法就是研究编译器如何实现它,编写程序以在汇编中访问它。

@eerorika 的回答是 100% 正确的,你不应该使用任何会导致未定义行为的东西。

但是,如果您仍然想走这条路,您可以通过将 B 指针作为第一个参数传递给 prva & druga.[=21 来修复您的代码=]

prvadruga 等成员函数有一个隐含的 this 参数,您需要将其传递给函数。 所以类似的东西:

typedef int (*PTRFUN1)(void* that);
typedef int (*PTRFUN2)(void* that, int);

void function (B* var){
    void** vptr =  *(void***)var;
    PTRFUN1 fun1 = (PTRFUN1)vptr[0];
    PTRFUN2 fun2 = (PTRFUN2)vptr[1];
    printf("Prva:%d\n", fun1(var));
    printf("Druga:%d\n", fun2(var, 2));
}

应该 工作(仍然依赖于编译器和 100% 未定义的行为)

Here's a Fiddle 给它。

编辑:

还有一种方法可以调用函数而不使用它们的名称,这不是未定义的行为。
您可以为此使用 Pointer to Member Functions

typedef int (B::*PTRFUN1)();
typedef int (B::*PTRFUN2)(int x);

void function (B* var){
    PTRFUN1 fun1 = &B::prva;
    PTRFUN2 fun2 = &B::druga;
    printf("Prva:%d\n", (var->*fun1)());
    printf("Druga:%d\n", (var->*fun2)(2));
}

这样不会造成UB,仍然可以满足你不使用函数名调用的要求。

Here's a Fiddle 指向成员函数的变体。