简体   繁体   English

vptr - 虚拟表

[英]vptr - virtual tables

there is something i still don't get.有些东西我仍然没有得到。

for every class i declare there is a hidden vptr member pointing to the class virtual table.对于我声明的每个类,都有一个隐藏的 vptr 成员指向类虚拟表。

let's say i have this declaration :假设我有这个声明:

class BASE
{
    virtual_table* vptr; //that's hidden of course , just stating the obvious
    virtual void foo();
}

class DERIVED : public BASE
{
   virtual_table* vptr; //that's hidden of course also
   virtual void foo();
   virtual void cho();
}

first i want to understand something, is it really the same member name for the vptr both for the derived and the base ?首先我想了解一些东西,对于派生和基础的 vptr 是否真的是相同的成员名称?

second, what happens in this situation :其次,在这种情况下会发生什么:

base* basic = new derived();

i get it, the basic variable gets derived's vptr, but how is that happening ?我明白了,基本变量得到了派生的 vptr,但这是怎么发生的? cause usually when conversion taking place , derived's base part (including base's vptr) should be assigned to basic, and not derived's vptr.因为通常在发生转换时,派生的基础部分(包括基础的 vptr)应该分配给基础,而不是派生的 vptr。 maybe it's different if there is a variable with the same name in both classes, i dunno.如果两个类中都有一个同名的变量,我不知道可能会有所不同。

third and last question : when i have第三个也是最后一个问题:当我有

 base* basic = new derived();

is there a way to call with basic - base's member function even though it's virtual ?有没有办法调用 basic-base 的成员函数,即使它是虚拟的?

thanks谢谢

first, yes, it is the same member.首先,是的,它是同一个成员。 It is automaticaly assigned a first time when running base constructor, and assigned a second time when running the derived constructor.它在运行基本构造函数时自动分配第一次,并在运行派生构造函数时自动分配第二次。 (In the case of default empty constructors, the useless assignements of base is optimized away.) (在默认的空构造函数的情况下,base 的无用赋值被优化掉了。)

second, there is no real conversion.第二,没有真正的转换。 In fact, the derivation can be named a "is a" relationship.事实上,推导可以命名为“是一个”关系。 In this case, derived "is a" base.在这种情况下,派生的“是一个”基础。 If you consider the first bytes of memory of a derived object, they have the same meaning than the first bytes of a base object.如果考虑派生对象的内存的第一个字节,它们与基础对象的第一个字节具有相同的含义。

third, you can call basic member function as follows: basic->base::foo();第三,可以调用基本成员函数如下: basic->base::foo();

First of all, virtual tables are not part of the C++ standard.首先,虚拟表不是 C++ 标准的一部分。 A C++ compiler is free to implement virtual functions any way they feel appropriate. C++ 编译器可以自由地以任何他们认为合适的方式实现虚函数。 Usually they will use virtual tables, true;通常他们会使用虚拟表,true; but in this case they can implement them any way they feel appropriate.但在这种情况下,他们可以以任何他们认为合适的方式实施它们。

In most usual implementations, basic does not get derived 's vptr ;在大多数常见的实现中, basic不会得到derivedvptr *basic 's vptr will point to derived 's vtable , which is not really the same. *basicvptr将指向derivedvtable ,这是不是真的一样。 basic is just a pointer, and it is the object pointed that has a vptr . basic只是一个指针,指向的对象有一个vptr No conversion involved.不涉及转换。 In any case, vptr is just an internal name for an implementation detail.无论如何, vptr只是实现细节的内部名称。 There is no real class member called vptr .没有真正的类成员称为vptr

And you can always call any base-class function just by qualifying it with the class name (in your case, basic->BASE::foo() ).并且您始终可以通过使用类名对其进行限定来调用任何基类函数(在您的情况下,是basic->BASE::foo() )。

UPDATE: Just for the record, I have tried creating a class in VC++2008 with a pointer called __vfptr (that is the internal name for the vptr in this compiler), and it works as expected, even though the debugger gets a bit confused by the variable name.更新:只是为了记录,我尝试在 VC++2008 中创建一个类,它带有一个名为__vfptr的指针(这是这个编译器中 vptr 的内部名称),并且它按预期工作,即使调试器有点被变量名弄糊涂了。

The vtable ( virtual_table , in your words) will point to different addresses for BASE and DERVIVED provided DERIVED overrides at least one virtual function from BASE .vtablevirtual_table ,在你的话)会指向不同的地址, BASEDERVIVED只要DERIVED覆盖从至少一个虚函数BASE Each instance of BASE will have the same vtable pointer however (ditto for DERIVED ).然而, BASE每个实例都将具有相同的vtable指针(同理DERIVED )。

When you do something like:当您执行以下操作时:

someobject->foo();

this is translated to:这被翻译成:

someobject->vtable[COMPILER_GENERATED_OFFET_FOR_FOO]();

The vtable is an array of function pointers where the offset to some virtual function (say foo ) is the same for all classes in the class hierarchy. vtable是一个函数指针数组,其中某个虚函数(比如foo )的偏移量对于类层次结构中的所有类都是相同的。

3). 3)。 Ya u can .base* basic = new derived();你可以 .base* basic = newderived();

basic->BASE::foo();基本->BASE::foo(); // will call base class method and resolved at compile time. // 将调用基类方法并在编译时解析。

2). 2)。 It is possible when there is not virtual stuff in class(if derived class does not override any base class functions), but if derived class has virtual functions (overrided )so to achieve run time polymorphism its necessary for compiler to initialize vptr of base by vptr of derived and it augments that code.当类中没有虚拟的东西时是可能的(如果派生类没有覆盖任何基类函数),但是如果派生类有虚函数(覆盖)所以为了实现运行时多态性,编译器必须通过以下方式初始化基类的 vptr派生的 vptr 并增加了该代码。

1).Yes the interface function prototype has to be same else base class function get hidden by derived class function (eg if parameter defers in derived class function ) . 1).是的,接口函数原型必须相同,否则基类函数会被派生类函数隐藏(例如,如果派生类函数中的参数延迟)。

On the second question.关于第二个问题。 (And this is not stated in the standard, and can be implemented in other ways, so just take the general ideas out of it. It is also oversimplified for single inheritance hierarchies, multiple inheritance makes everything more complex). (而这在标准中并没有说明,可以通过其他方式实现,所以只是把一般的想法去掉。对于单继承层次结构也过于简单,多继承使一切变得更加复杂)。

The memory layout of a derived and a base object coincides 100% in as much as the size of the base object (including any data injected by the compiler).派生对象和基础对象的内存布局与基础对象的大小(包括编译器注入的任何数据)的大小一致 100%。 This means that a pointer that has base type and actually points to derived will actually point to a piece of memory that can be interpreted as a base object, the layout is the same even if the contents (vptr value) are different.这意味着具有base类型并实际指向derived的指针实际上会指向一块可以解释为base对象的内存,即使内容(vptr 值)不同,布局也是一样的。

base          derived

base_vptr     base_vptr
base_attrs    base_attrs

              derived_vptr
              derived_attrs

When you create an instance of derived the compiler will call the appropriate derived constructor, whose initialization list starts by calling the base constructor.当您创建derived的实例时,编译器将调用适当的derived构造函数,其初始化列表从调用base构造函数开始。 At this point the vtable pointer base_vptr is set to point to the virtual table of the base class, and as such all pointers there refer to base::method .此时,vtable 指针base_vptr被设置为指向基类的虚拟表,因此所有指针都指向base::method After the base constructor has completed, the base_vptr is updated in derived and it is set to point to the derived vtable, so instances there point to derived::method if the method is overriden in derived . base构造函数完成后, base_vptr在导出中更新,并设置为指向derived vtable,因此如果该方法在derived被覆盖,则那里的实例指向derived::method At this point derived_vptr points to the derived vtable for virtual methods added in derived and would point to derived::new_method ...此时derived_vptr点,在添加的虚拟方法导出虚函数表derived和将指向derived::new_method ...

Just to make a point: the vtable does not necessarily store pointers into the actual methods.只是为了说明一点:vtable 不一定将指针存储到实际方法中。 In some cases, intermediate code must be executed whenever a virtual method is called, and that happens whenever multiple inheritance comes into play.在某些情况下,每当调用虚拟方法时都必须执行中间代码,并且每当多重继承发挥作用时就会发生这种情况。 Things get more complicated, as the derived class can only be aligned with one of its bases (usually the first one declared).事情变得更加复杂,因为派生类只能与其基类之一(通常是声明的第一个基类)对齐。 This is where things really get tricky.这就是事情真正变得棘手的地方。 Upcasting to base2 modifies the pointer so that it points to the memory location that can be directly be interpreted as a base2 instance, so the contents of a pointer (memory location) of type base2 pointing to an object of type derived will not coincide with a pointer of type derived into the same object.向上转换为base2修改指针,使其指向可以直接解释为base2实例的内存位置,因此指向derived类型的对象的base2类型的指针(内存位置)的内容将不会与derived到同一对象的类型指针。 At this point, if a virtual method from base2 is called, the system must perform some magic to recalculate the correct position of the implicit this argument that is handed into the derived::method_from2 virtual method (that must point back to the whole derived object and not just the base2 subobject.此时,如果调用来自base2的虚拟方法,系统必须执行一些魔术来重新计算传递给derived::method_from2虚拟方法(必须指向整个derived对象)的隐式this参数的正确位置而不仅仅是base2子对象。

从派生的孩子调用父方法: 老问题

first : yes: each instance will get that member variable declared by the base.首先:是的:每个实例都将获得由基类声明的成员变量。

second : in that situation you'll be able to access only members of the base class.第二:在这种情况下,您将只能访问基类的成员。

third : you can call it if it is virtual, as long as it's not pure virtual (which means it doesn't have any implementation and it's declared like this: virtual void foo() = 0; ).第三:如果它是虚拟的,你就可以调用它,只要它不是纯虚拟的(这意味着它没有任何实现,并且声明如下: virtual void foo() = 0; )。

You can check vtable implementation details here ;你可以在这里查看 vtable 的实现细节; this is what the GNU compilers, and AFAIK some other mainly Unix compilers use.这是 GNU 编译器和 AFAIK 其他一些主要 Unix 编译器使用的。 Especially the "Vtable layout" examples page is instructive.尤其是“Vtable 布局”示例页面很有启发性。

As others have noted, this is an implementation detail, the C++ standard itself is silent on the matter.正如其他人所指出的,这是一个实现细节,C++ 标准本身对此事保持沉默。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM