虚函数 - 指针在哪里?

时间:2011-04-10 17:48:54

标签: c++ function inheritance virtual

一个开始的例子。

class A
{
    public:
    virtual const char* GetName() { return "A"; }
};

class B: public A
{
public:
    virtual const char* GetName() { return "B"; }
};

class C: public B
{
public:
    virtual const char* GetName() { return "C"; }
};

    class D: public C
{
public:
    virtual const char* GetName() { return "D"; }
};

int main()
{
    C cClass;
    A &rBase = cClass;
    cout << "rBase is a " << rBase.GetName() << endl;

    return 0;
}

在此特定示例中,输出为:

rBase是C

以下是它的工作原理:

rBase是A类型的指针,因此它转到A类并查找GetName()。但GetName()是虚拟的,所以编译器会检查A和C之间的所有类,并从最派生的类中获取GetNAme()函数,即C

但是,我怀疑编译器将如何知道哪个是A类的子代,以及如何从Parent类转移到子类等等?一个孩子知道它的父母,但父母不知道它的孩子(我想!)。

从我的观点来看,正确的执行步骤应该是:

rBase是A类型的指针,因此它转到A类并查找GetName()。但GetName()在那里是虚拟的,因此编译器会检查指针指向的类。在这种情况下是C类对象,因此转到C类并检查它是否具有GetName()函数,因此使用它。如果C类中没有该函数(假设),编译器可以轻松跟踪C的父级并检查相同的内容,并且这可以继续,直到它返回到A(假设除A之外的所有类都不包含GetName())。

现在,这似乎是一种更合乎逻辑的方法,因为在继承树中向后移动(子级到父级)似乎比向前移动(父级到子级)更容易实现。

此致

2 个答案:

答案 0 :(得分:4)

您描述的第二个算法是“正确的”,因为它可以通过这种方式有效地解决问题。但是,编译器使用一个很好的技巧来快进该算法。基本上,它使用通常称为virtual method table的查找,通常缩写为“vtable”。

基本上,具有虚方法的类的实例包含指向其类的虚方法表的指针。编译器将虚拟方法名称映射到虚拟表中的偏移量,这样调用虚拟表就不需要复杂的算法:所有必要的是数组查找,然后在结果地址处调用。

答案 1 :(得分:2)

  

rBase是A类型的指针,因此它转到A类并查找GetName()。但GetName()是虚拟的,所以编译器会检查A和C之间的所有类,并从最派生的类中获取GetNAme()函数,即C

不,这不是它的工作原理。生成通过指向virtual GetName()的指针调用A的代码时,编译器只是将“调用与对象关联的虚拟表中的第7个条目”。生成覆盖GetName()的派生类时,编译器会将其GetName()实现放在虚拟表中的此条目中。

这样父类不需要知道它的子节点。孩子们有责任正确填写虚拟桌子。