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

What happens when calling base functions from derived classes?

本文关键字:什么情况 基函数 派生 调用      更新时间:2023-10-16

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

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()时调用子类的实现。

Father类也有一个指向它自己的 vtable 的指针,这将指示在调用DoSomething()时调用 GrandFather 的实现。

当我在 Child 中使用Father::DoSomething()时,它应该调用GrandFather::DoSomething(), 但是孩子怎么知道功能在哪里呢?

如果真的使用了名称重整,那么如何?因为没有具有该名称的函数(类似于_Father_DoSomething(this))。

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

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

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

事实上,称呼Father::DoSomething为祖父::D o某事是由于继承,这是一个单独的机制。 如果引用派生类的成员,但它不存在,它将上升一个级别并在基类中查找它。 那里不需要虚拟呼叫。

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

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

名称重整与此无关。名称重整基本上是方法名称的编码方式,因此请为方法/函数指定一个唯一的名称。没有两个方法/函数具有相同的损坏名称。

当使用限定名来命名函数时,函数 调用是在编译时确定的,使用静态类型 表达式和名称查找从给定位置开始 通过限定词。 由于要调用的函数已确定 完全通过静态查找,无需访问任何 vtable 完全。

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

孩子有自己的 vtable,其中包含父亲 vtable,而父亲 vtable 又包含祖父的 vtable:

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

当我在 Child 中使用 Father::D oSomething() 时,它应该调用 祖父::D oSomething(),但是孩子怎么知道函数在哪里 是?

vtable for Father 指的是祖父::D oSomething(),所以称父亲::D oSomething() 你实际上是在调用祖父::D oSomething()

  1. "名称重整"与它完全无关。被称为"名称重整"的技术属于完全不同的和不相关的领域。我不知道你从哪里想到把它卷入这里。

  2. 类没有任何"指向 vtable 的指针"。只有类类型的特定对象(也称为实例)才可能具有此类指针。不要混合类类型的对象。无论如何,"vtable 指针"是语言级别不存在的实现细节。在语言级别理解代码的行为是完全没有必要的。

  3. 在您的示例中,您根本没有任何对象。你只是声明了一堆类。出于这个原因,现在采取任何"指向 vtables 的指针"还为时过早。您发布的内容中没有任何指向任何 vtables 的指针。

  4. 当涉及到调用Father::DoSomething()问题时,任何"虚拟表"的问题都不会出现,即使是作为实现细节。Father::DoSomething()调用使用目标函数的限定名。此类限定调用始终直接解析,不涉及任何 vtables。即,通过执行Father::DoSomething(),您明确要求编译器忽略任何多态性(忽略任何"vtables"),并直接对类Father中的名称DoSomething执行编译时名称查找。根据名称查找的规则,它将找到函数GrandFather::DoSomething()并调用它。

  5. 如果你实际声明了一个对象,比如

    Child child;
    

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

    现在,如果您致电

    child.DoSomething()
    

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

仅此而已。

相关文章: