0

我不确定这是否记录在任何地方。我们都知道在虚函数的情况下,每个类都有一个 vptr,它指向一个称为虚表的函数指针数组。我想知道vptr的原型是什么。例如,如果一个类声明如下,

class A
{
   int a;
   public: A(){}
   virtual void display();
   virtual void setValue(int x);
};

现在我们在 A 类的 vtable 中有两个函数指针。单个 vptr 如何能够定义不同原型的两个定义?

如果我的理解有误,请告诉我。

谢谢!拉胡尔。

4

3 回答 3

3

vptr 是一个实现细节,因此它没有原型。

于 2012-06-03T17:40:55.917 回答
1

正如 Oli Charlesworth 所指出的,虚拟指针是一个实现细节,所以这个问题在 C++ 方面并没有真正的意义。也就是说,以下虚拟功能(某些功能)的手动实现可能有助于您的理解:

struct vtable {
    void (*display)(void*);
    void (*setValue)(void*, int);
};

void A_display(void *this_) { /*Cast this_ to A* and do A stuff*/ }
void A_setValue(void *this_, int x) { /*Cast this_ to A* and do A stuff*/ }

vtable A_vtable = {A_display, A_setValue};

struct A {
    vtable *vptr = &A_vtable;
    int a;
    public: A(){}
};

void B_display(void *this_) { /*Cast this_ to B* and do B stuff*/ }
void B_setValue(void *this_, int x) { /*Cast this_ to B* and do B stuff*/ }

vtable B_vtable = {B_display, B_setValue};

struct B {
    vtable *vptr = &B_vtable;
    int a;
    public: B(){}
};

void display(void *obj) {
    ((*static_cast<vtable**>(obj))->display)(obj);
}
void setValue(void *obj, int) {
    ((*static_cast<vtable**>(obj))->setValue)(obj, int);
}

当然,这只是提供了虚函数功能的一小部分,但是应该相当简单地看到它vptrs指向具有固定类型的函数的指针集合。

于 2012-06-03T18:13:31.353 回答
1

虚拟表(在使用这种东西的实现中)是“编译器魔法”的产物。它不需要有特定的原型,因为没有 C++ 代码直接使用它。相反,编译器自定义生成一个以符合每个需要一个的类。编译器还生成访问每个元素的代码,因此它可以保证以类型安全的方式访问每个元素。

例如,编译器知道该A::setValue方法的槽包含一个指向与setValue签名匹配的函数的指针,因为编译器是首先将它放在那里的那个。此外,直接访问该槽的唯一代码是编译器生成的机器代码,并且在生成此类代码之前,编译器已经确认原始 C++ 代码正在调用该setValue函数。因此,无需担心该setValue插槽可能会保存除setValue-conformant 函数指针之外的任何内容。也不担心可能会访问其他插槽;如果发生这种情况,那将是一个编译器错误,而不是普通用户代码的结果。

表的元素永远不会被视为一个组,因此不需要它们都具有相同的类型。充其量,它们都有一种“适合 CPU 跳转的通用指针或偏移量”。由于那时它还不是真正的 C++,因此“类型”不必适合任何特定的 C++ 类型。

于 2012-06-03T18:16:29.243 回答