从派生类调用基函数时会发生什么?

时间:2014-01-10 17:23:23

标签: c++

好吧,我的问题有点棘手。 假设我有三个类,如此:

    class GrandFather
    {
    public:
        virtual int DoSomething()
        {
            return 3;
        }   
    };

    class Father : public GrandFather
    {
    };

    class Child : public Father
    {
    public:
        virtual int DoSomething()
        {
            Father::DoSomething();      
        }   
    };

我在一些参考文献中发现,在调用基函数时,不使用整个虚拟机制,并且会发生名称重整。

现在我想知道如何做到这一点。

Child类有一个指向其自己的vtable的指针,它将指示在调用DoSomething()时调用子实现。

父类也有一个指向自己的vtable的指针,这表明在调用DoSomething()时,会调用GrandFather的实现。

当我在Child中使用Father::DoSomething()时,应该调用GrandFather::DoSomething(), 但是Child如何判断函数的位置?

如果确实使用了名称修改,那么如何?因为没有该名称的功能(类似_Father_DoSomething(this))。

孩子必须访问父亲的vptr才能到达GrandFather::DoSomething(),据我所知,他不能。{/ p>

这已经困扰了我很长一段时间,所以我非常感谢你的帮助。 谢谢:))

5 个答案:

答案 0 :(得分:1)

无论何时显式限定成员函数,都表示要在该特定类中调用该特定函数,并且不使用虚拟调用机制。虚拟调用仅在您调用成员函数而不限定它时发生,在这种情况下它使用虚拟机制来决定使用哪个类的成员函数。

调用Father::DoSomething调用GrandFather :: DoSomething的事实是由于继承,这是一个单独的机制。如果你引用了派生类的一个成员而它不在那里,它会上升一级并在基类中查找它。那里不需要虚拟电话。

答案 1 :(得分:1)

当您从Father::DoSomething调用Child时,编译器使用编译时信息来计算要调用的内容。因为Father中没有版本,所以它知道在GrandFather中调用该版本,因为它是Father范围内有效的版本。

当重写的方法调用基本实现时,不使用vtable。 vtable仅在通过指针或引用调用方法时使用。这就是在C ++中实现所有多态的方式。

名称修改与此无关。名称修改基本上是如何编码方法名称,因此为方法/函数指定唯一名称。没有两个方法/函数具有相同的错位名称。

答案 2 :(得分:1)

使用限定名称命名函数时,该函数 调用是在编译时确定的,使用静态类型 表达式,名称查找从给定的位置开始 由资格赛。因为要调用的函数是确定的 完全通过静态查找,不需要访问任何vtable 一点都不。

答案 3 :(得分:1)

g ++ file_name.cpp -S命令可以提供足够的信息以清除所有与vtable相关的问题。请检查生成的.s文件:

Child拥有自己的vtable,其中包含父vtable,它依次包含GrandFather的vtable:

vtable for Child:
    Child::DoSomething()
    vtable for Father

vtable for Father:
    GrandFather::DoSomething()
    vtable for GrandFather

vtable for GrandFather:
    GrandFather::DoSomething()
  

当我在Child中使用Father :: DoSomething()时,它应该调用   GrandFather :: DoSomething(),但是Child如何判断函数的位置   是

父亲的vtable引用了GrandFather :: DoSomething(),所以调用Father :: DoSomething()你实际上是在调用GrandFather :: DoSomething()

答案 4 :(得分:1)

  1. “名字错误”与它完全无关。被称为“名称修剪”的技术属于完全不同且不相关的区域。我不知道你有什么想法让它参与其中。

  2. 类没有任何“指向vtable的指针”。只有类的特定对象(也称为实例)可能有这样的指针。不要混合类类型的对象。在任何情况下,“vtable指针”是在语言级别不存在的实现细节。在语言层面理解代码的行为是完全没有必要的。

  3. 在您的示例中,您根本没有任何对象。你只是声明了一堆类。出于这个原因,要及早采取任何“指向vtable的指针”。您没有任何指向任何vtable的指针。

  4. 谈到Father::DoSomething()调用的问题,任何“虚拟表”的问题根本不会出现在图片中,即使是作为实现细节。 Father::DoSomething()调用使用目标函数的限定名称。这种合格的调用总是直接解决,不涉及任何vtable。即通过执行Father::DoSomething(),您明确要求编译器忽略任何多态(忽略任何“vtable”)并直接对类DoSomething中的名称Father执行编译时名称查找。根据名称查找规则,它将找到函数GrandFather::DoSomething()并调用它。

  5. 如果您实际声明了一个对象,例如

    Child child;
    

    然后您将GrandFather子对象嵌入到嵌入上述Father对象的Child子对象中。这些嵌套对象中的所有“vtable指针”都将指向Child的vtable(事实上,在典型的实现中,所有这些嵌套对象将共享一个vtable指针)。

    现在,如果你打电话

    child.DoSomething()
    

    此调用将根据Child的vtable解析并发送到Child::DoSomething()(大多数编译器实际上足够聪明以优化代码并直接调度呼叫)。但是,如上所述,正如您所问,从Father::DoSomething内部Child::DoSomething的合格调用是无条件直接执行的。它不关心任何vtable指针。它直接进入GrandFather::DoSomething()

  6. 这就是它的全部内容。