虚拟表和虚拟指针用于多个虚拟继承和类型转换 [英] Virtual tables and virtual pointers for multiple virtual inheritance and type casting

查看:148
本文介绍了虚拟表和虚拟指针用于多个虚拟继承和类型转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对于vptr和内存中对象的表示感到困惑,希望你能帮助我更好地理解这件事。

I am little confused about vptr and representation of objects in the memory, and hope you can help me understand the matter better.


  1. 考虑 B 继承 A ,并定义虚函数 f code>。从我学到的内存中类B的对象的表示形式如下: [vptr | A | B]
    vtbl 表示 vptr $ c> B :: f()
    。我还理解,将对象从 B 转换为 A 除了忽略 / code>部分在对象的结尾。是真的吗?这种行为不是错误吗?我们希望类型 A 的对象执行 A :: f()方法,而不是 B :: f()

  1. Consider B inherits from A and both define virtual functions f(). From what I learned the representation of an object of class B in the memory looks like this:[ vptr | A | B ] and the vtbl that vptr points to contains B::f(). I also understood that casting the object from B to A does nothing except ignoring the B part at the end of the object. Is it true? Doesn't this behavior is wrong? We want that object of type A to execute A::f() method and not B::f().

有多少 vtables 在系统中作为类数?

Are there a number of vtables in the system as the number of classes?

如何从 vtable 继承两个或更多的类看起来像什么?

How will a vtable of class that inherits from two or more classes look like? How will the object of C be represented in the memory?

与问题3相同,但具有虚拟继承。

Same as question 3 but with virtual inheritance.


推荐答案

下面是GCC的真实情况,但对于你使用的编译器也是如此。所有这些都是依赖于实现的,并且不受C ++标准的支配。但是,GCC编写了自己的二进制标准文档 Itanium ABI

The following is true for GCC, but may also be true for the compiler you're using. All these is implementation-dependant, and is not governed by C++ standard. However, GCC write its own binary standard document, Itanium ABI.

我试图解释虚拟表如何在更简单的单词中作为我的关于C ++中虚拟函数性能的文章,您可能会发现它有用。以下是您的问题的答案:

I tried to explain basic concepts of how virtual tables are laid out in more simple words as a part of my article about virtual function performance in C++, which you may find useful. Here are answers to your questions:


  1. 描述对象内部表示的更正确的方法是:

  1. A more correct way to depict internal representation of the object is:

| vptr | ======= | ======= |  <-- your object
       |----A----|         |
       |---------B---------|

B >它的基类 A ,它只是在结束后添加了一些自己的成员。

B contains its base class A, it just adds a couple of his own members after its end.

code> B * A * 确实没有什么,它返回相同的指针, vptr 保持不变。但是,简而言之,虚函数不总是通过vtable调用。有时候,它们就像其他函数一样被调用。

Casting from B* to A* indeed does nothing, it returns the same pointer, and vptr remains the same. But, in a nutshell, virtual functions are not always called via vtable. Sometimes they're called just like the other functions.

这里有更详细的解释。你应该区分两种调用成员函数的方法:

Here's more detailed explanation. You should distinguish two ways of calling member function:

A a, *aptr;
a.func();         // the call to A::func() is precompiled!
aptr->A::func();  // ditto
aptr->func();     // calls virtual function through vtable.
                  // It may be a call to A::func() or B::func().

事情是在编译时知道调用:通过vtable或只是一个通常的调用。而且事情是在编译时知道一个转换表达式的类型,因此编译器在编译时选择正确的函数。

The thing is that it's known at compile time how the function will be called: via vtable or just will be a usual call. And the thing is that the type of a casting expression is known at compile time, and therefore the compiler chooses the right function at compile time.

B b, *bptr;          
static_cast<A>(b)::func(); //calls A::func, because the type
   // of static_cast<A>(b) is A!

在这种情况下,它甚至不会在内部查看vtable!

It doesn't even look inside vtable in this case!

一般来说,没有。一个类可以有几个vtables,如果它继承自几个基地,每个都有自己的vtable。这种虚拟表集合形成一个虚拟表组(见第3页)。

Generally, no. A class can have several vtables if it inherits from several bases, each having its own vtable. Such set of virtual tables forms a "virtual table group" (see pt. 3).

类还需要一组构造虚函数,复杂对象的基础。您可以在我链接的标准中进一步阅读。

Class also needs a set of construction vtables, to correctly distpatch virtual functions when constructing bases of a complex object. You can read further in the standard I linked.

下面是一个例子。假设 C A B 继承, virtual void func(),以及 a b c 与其名称相关的虚函数。

Here's an example. Assume C inherits from A and B, each class defining virtual void func(), as well as a,b or c virtual function relevant to its name.

C 将有一个由两个vtable组成的vtable组。它将共享一个具有 A (vtable,其中当前类的自己的函数被称为primary)的vtable和用于 B的vtable 将附加:

The C will have a vtable group of two vtables. It will share one vtable with A (the vtable where the own functions of the current class go is called "primary"), and a vtable for B will be appended:

| C::func()   |   a()  |  c()  || C::func()  |   b()   |
|---- vtable for A ----|        |---- vtable for B ----| 
|--- "primary virtual table" --||- "secondary vtable" -|
|-------------- virtual table group for C -------------|

对象在内存中的表示形式与vtable的外观几乎相同。只需在组中的每个vtable之前添加 vptr ,就可以粗略估计数据在对象中的布局。您可以在GCC二进制标准的相关部分中阅读。

The representation of object in memory will look nearly the same way its vtable looks like. Just add a vptr before every vtable in a group, and you'll have a rough estimate how the data are laid out inside the object. You may read about it in the relevant section of the GCC binary standard.

虚拟基础(其中一些)布置在vtable组的末尾。这样做是因为每个类应该只有一个虚拟基础,并且如果它们与通常vtables混合,则编译器不能重新使用构造的vtables的部分来构造派生类。这将导致计算不必要的偏移并降低性能。

Virtual bases (some of them) are laid out at the end of vtable group. This is done because each class should have only one virtual base, and if they were mingled with "usual" vtables, then compiler couldn't re-use parts of constructed vtables to making those of derived classes. This would lead to computing unnecessary offsets and would decrease performance.

由于这样的放置,虚拟基础还会在其vtables中引入其他元素: vcall offset(当从指针到完整对象内部的虚拟基础跳转到覆盖虚拟函数的类的开始时,获得最终超控的地址)。每个虚拟基本还添加 vbase 偏移量,它们被插入到派生类的vtable中;它们允许找到虚拟基础的数据在哪里开始(它不能被预编译,因为实际地址取决于层次结构:虚拟基础在对象的结尾,并且从开始的移位根据多少非虚拟)。

Due to such a placement, virtual bases also introduce into their vtables additional elements: vcall offset (to get address of a final overrider when jumping from the pointer to a virtual base inside a complete object to the beginning of the class that overrides the virtual function) for each virtual function defined there. Also each virtual base adds vbase offsets, which are inserted into vtable of the derived class; they allow to find where the data of the virtual base begin (it can't be precompiled since the actual address depends on the hierarchy: virtual bases are at the end of object, and the shift from beginning varies depending on how many non-virtual classes the current class inherits.).

Woof,我希望我没有引入太多不必要的复杂性。在任何情况下,您可以参考原始标准,或您自己编译器的任何文档。

Woof, I hope I didn't introduce much unnecessary complexity. In any case, you may refer to the original standard, or to any document of your own compiler.

这篇关于虚拟表和虚拟指针用于多个虚拟继承和类型转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆