1

我有两个班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 的指针并打印返回方法 'prva' 和 'druga' 的值而不使用它们的名称(通过虚拟表访问)

我编写了以下代码,成功打印了方法 'prva' 的返回值,但未能对第二种方法 '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”

它无法prva()在“druga”内部调用,我不知道为什么。

此外,如果我只是删除 call ofprva()并让 body just be return x,则无论我尝试通过什么参数发送,方法 'druga' 将始终返回“42”或我让 'prva' 返回的任何数字fun2()

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

4

2 回答 2

2

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

任何想法我在这里做错了什么

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

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

于 2020-04-03T11:58:10.593 回答
1

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

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

成员函数喜欢prva并且有一个需要传递给函数druga的隐式参数。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% 未定义的行为)

这是一个小提琴

编辑:

还有一种方法可以调用函数而不使用它们的名称,这不是未定义的行为。
您可以为此使用指向成员函数的指针:

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 并且仍然满足您不使用函数名称来调用它们的要求。

这是指针到成员函数变体的小提琴

于 2020-04-03T12:12:10.960 回答