虚表的顺序重要吗?

Is the order of the virtual table important?

本文关键字:顺序 虚表      更新时间:2023-10-16

我是新来的,所以别对我太苛求。根据我的讲师前段时间所说的,虚拟桌子的顺序很重要。但我不明白为什么!!?

给定下一个代码:

class A
{
public:
    A() {cout <<"1" << endl;};
    A (const A& s) {cout << "2" << endl;}
    ~A () {cout << "3" << endl;}
    void f1() {cout << "4" << endl; f2();}
    virtual void f2() = 0;
    virtual void f3() {cout << "5" << endl;}
};

class B : public A
{
public:
    B() {cout << "6" << endl;}
    B(const B& b) : A(b) {cout << "7" << endl;}
    ~B() {cout << "8" << endl;}
    virtual void f1() {cout<<"9"<<endl;}
    void f2() {cout<<"lO"<<endl; f4();}
    virtual void f2(int i) {cout << "11" << endl;}
    virtual void f4() {cout << "12" << endl; f3();}
};

他说顺序是:

A's vtable : 
A::f2()
A::f3()
B's vtable : 
B::f2()
A::f3()
B::f1()
B::f2(int)
B::f4()

但是我不明白为什么它很重要?他说桌子是没用的,如果它不是按照正确的顺序,你能解释一下原因吗?

在c++标准中没有变量的概念。只是大多数实现(如果不是全部的话)将它用于虚拟分派。然而,确切的约定完全是由实现定义的。

说……函数的顺序很重要,但不是对程序员来说,而是对编译器来说——你可以在代码中随心所欲地安排函数。然而,编译器通常会将每个函数指针放入虚函数表中的特定位置,并将其专用于该函数。因此,当它需要调用f()时,它知道f()函数的索引,并从虚函数表中获取该指针。

这个问题可能对您也有帮助:虚拟调度实现细节

虚变量表的顺序对事情的正常工作很重要,但只对编译器(也就是说,你不需要关心,因为它会照顾它)。

如果编译器自己把它弄乱了,那么是的,事情会破裂,因为函数是通过偏移量查找的(所以偏移量会产生一个随机的函数,这将是灾难性的)。但是一般程序员不需要担心虚函数表的顺序

只有当类为外部ABI声明接口(例如COM/XPCOM)时才重要。

大多数时候它并不重要,没有理由去关心它

虚表的每个客户端都需要知道正确的顺序,以便找到要调用的正确方法。但只要各方都同意这个顺序,这个顺序是什么都无所谓。

虚值表是一个"查找"表。它基本上是指向类虚函数的指针的映射。如果顺序不对,指针将指向错误的函数。例如,如果您想调用不带参数的B:f1(),而要调用带int参数的B::f2(),则会发生不好的事情。

我不确定他是什么意思,但我会试着解释它是如何工作的:

首先,c++通过名称和签名来定义方法。因此,当c++初始化一个类的虚表时,它将用具有相同名称和签名的派生虚函数替换所有基类的虚函数。

当一个类派生另一个类时,它实际上是在它之上构建的。因此基类作为内存块的一部分存在(复杂,请阅读这里- 虚拟继承)

虚拟表只保存一个"指针"到正确的函数

在visual studio中,当您更改虚函数的声明顺序时,您可能需要在之后清理并重新构建整个解决方案。

我的猜测是虚表在特定情况下不同步。

因此,如果在更改虚拟函数后事情变得不稳定,只需重新编译即可解决问题。