从派生类调用基函数时会发生什么情况
What happens when calling base functions from derived classes?
好吧,我的问题有点棘手。 假设我有三个这样的类:
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()
-
"名称重整"与它完全无关。被称为"名称重整"的技术属于完全不同的和不相关的领域。我不知道你从哪里想到把它卷入这里。
-
类没有任何"指向 vtable 的指针"。只有类类型的特定对象(也称为实例)才可能具有此类指针。不要混合类和类类型的对象。无论如何,"vtable 指针"是语言级别不存在的实现细节。在语言级别理解代码的行为是完全没有必要的。
-
在您的示例中,您根本没有任何对象。你只是声明了一堆类。出于这个原因,现在采取任何"指向 vtables 的指针"还为时过早。您发布的内容中没有任何指向任何 vtables 的指针。
-
当涉及到调用
Father::DoSomething()
问题时,任何"虚拟表"的问题都不会出现,即使是作为实现细节。Father::DoSomething()
调用使用目标函数的限定名。此类限定调用始终直接解析,不涉及任何 vtables。即,通过执行Father::DoSomething()
,您明确要求编译器忽略任何多态性(忽略任何"vtables"),并直接对类Father
中的名称DoSomething
执行编译时名称查找。根据名称查找的规则,它将找到函数GrandFather::DoSomething()
并调用它。 -
如果你实际声明了一个对象,比如
Child child;
然后,您将有一个嵌入到
GrandFather
子对象中Father
子对象嵌入到上述Child
对象中。这些嵌套对象中的所有"vtable 指针"都将指向 vtable ofChild
(实际上,在典型的实现中,所有这些嵌套对象将共享一个 vtable 指针)。现在,如果您致电
child.DoSomething()
此调用将根据 vtable of
Child
解析并调度给Child::DoSomething()
(大多数编译器实际上足够聪明,可以优化代码并直接调度调用)。但是,正如我上面所说,正如您所问的,从内部Child::DoSomething
对Father::DoSomething
的合格调用是无条件直接执行的。它不关心任何 vtable 指针。它只是直接进入GrandFather::DoSomething()
.
仅此而已。
- 为什么或在什么情况下,你会将参数作为C++中的引用(或指针)传递给函数?
- 在使用 boost 共享互斥体时,我应该在什么情况下使用 owns_lock() 函数
- 如果从在其他函数中调用的函数引发异常会发生什么情况
- 如果在调用 DLL 中的函数时没有传递足够的参数,会发生什么情况?
- 在声明纯虚函数的父类的实例上调用纯虚函数时会发生什么情况
- 当声明了虚拟析构函数但没有实现时会发生什么情况
- 通过 std::async 启动的函数引发的异常会发生什么情况
- visual在什么情况下调用C++复制构造函数
- 从派生类调用基函数时会发生什么情况
- 未分配返回未定义对象类型引用的 C++ 函数的返回值时会发生什么情况
- 如果 new-handler 函数未正确编写或无法在 c++ 中释放更多内存,会发生什么情况
- 如果没有指针会发生什么情况,获取工厂函数指针
- 在参数中传递给 exec*() 函数系列的内存会发生什么情况
- 一个函数调用另一个函数时使用的内存会发生什么情况
- 在什么情况下,C++函数中允许缺少模板参数
- 函数执行结束时,向量中的线程会发生什么情况
- 在什么情况下,都不会调用c++析构函数
- 取消引用不返回的函数时会发生什么情况?
- 在C语言中,函数realloc在什么情况下会返回Null ?
- 当异步回调调用虚拟函数,而基类构造函数尚未返回时,会发生什么情况