2011-08-26 90 views
9

我是新来的,所以我很容易:) 从我的讲师早些时候讲的话来看,虚拟表的顺序很重要。 但我不明白这个原因!!?虚拟表的顺序是否重要?

由于下面的代码:

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

但我不明白为什么它是重要的?他说,如果这个虚拟表是 不是正确的顺序,你能解释一下为什么吗?

回答

8

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

如果编译器本身违背了它,那么事情就会中断,因为函数是通过偏移量来查找的(所以偏移量会产生一个随机函数,这将是灾难性的)。但是一般的程序员并没有这样做,不需要担心vtable在什么顺序。

1

该vtable是一个“查找”表。它基本上是一个指向班级虚拟功能的地图。如果发生故障,指针会指向错误的功能。例如,如果您想拨打B:f1()(不采用参数),而是拨打B::f2(),则需要输入int

16

在C++标准中没有vtables的概念。只是大多数实现(如果不是全部的话)将其用于虚拟调度。然而,确切的约定完全是实现定义的。

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

这个问题可以帮助你以及:Virtual dispatch implementation details

+0

非常感谢你!献给你们所有人 ! –

+1

@ Ron_s请确保您单击此答案左上角的复选标记,如果它回答您的问题。 –

2

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

5

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

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

+1

+1外部abi是一个非常好的点 –

+2

+1在Windows平台上,vtable布局是标准的唯一原因是因为COM。作为一个编译器厂商,如果你想支持COM,你必须遵循微软的实现。但是,COM未使用的更高级功能(例如虚拟继承或非接口的多重继承)与实现相关。 –

0

我不知道他是什么意思,但我会尽力解释它是如何工作的:

首先,C++定义了名称和签名的方法。所以当C++启动一个类的虚拟表时,它将用派生的具有相同名称和签名的虚函数替换所有基类的虚函数。

当一个类派生另一个类时,它实际上是建立在它上面的。因此,基类中存在的内存块的一部分(复杂,读到这里 - Virtual inheritance

虚拟表只是持有“指针”这取决于在运行时类型正确的函数。