虚拟表的顺序是否重要?

时间:2011-08-26 16:53:49

标签: c++

我很新,所以对我很轻松:) 从我讲师前面讲过的内容来看,虚拟表的顺序非常重要。 但我不明白这个原因!!?

给出下一个代码:

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()

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

7 个答案:

答案 0 :(得分:17)

C ++标准中没有vtable的概念。只是大多数实现(如果不是全部)都将它用于虚拟调度。但是,确切的约定完全是实现定义的。

那就是说...函数的顺序很重要,但不是程序员,而是编译器 - 你可以在你的代码中安排你想要的函数。但是,编译器通常会将每个函数指针放入vtable中的特定位置,该位置专用于该函数。因此,当它需要调用f()时,它知道f()函数的索引并从vtable中获取该指针。

这个问题也可能对您有所帮助:Virtual dispatch implementation details

答案 1 :(得分:9)

vtable的顺序对于正常工作很重要,但仅限于编译器(即你不需要关心,因为它会处理它)。

如果编译器把它自己搞定了,那么是的,事情会破坏,因为函数是通过偏移查找的(所以偏移会产生一个随机函数,这将是灾难性的)。但普通的程序员不会需要担心vtable的订单。

答案 2 :(得分:7)

当类声明外部ABI的接口(例如COM / XPCOM)时,这一点很重要。

大部分时间它并不重要,没有理由关心它。

答案 3 :(得分:2)

vtable的每个客户都需要知道正确的顺序,以便他们能够找到正确的调用方法。但只要各方就订单达成一致,那订单是什么并不重要。

答案 4 :(得分:1)

vtable是一个“查找”表。它基本上是指向类的虚函数的指针映射。如果它出现故障,指针将指向错误的功能。如果您想要调用B:f1(),而不需要参数,而是调用B::f2(),则需要int

答案 5 :(得分:0)

我不确定他的意思,但我会尝试解释它的工作原理:

首先,c ++按名称和签名定义方法。因此,当c ++启动类的虚拟表时,它将使用具有相同名称和签名的派生虚函数替换所有基类的虚函数。

当一个类派生另一个类时,它实际构建在它上面。因此基类作为内存块的一部分存在(复杂,在此处阅读 - Virtual inheritance

虚拟表只是在运行时根据类型保存到右侧函数的“指针”。

答案 6 :(得分:0)

在Visual Studio中,当您更改虚拟函数的声明顺序时,您可能需要随后清理并重建整个解决方案。

我的猜测是,vtable在特定情况下会不同步。

因此,如果在对虚拟函数进行更改后事情变糟了,那么只需重新编译就可以解决问题。