与虚拟函数相关的两个问题 [英] Two questions related to virtual functions

查看:81
本文介绍了与虚拟函数相关的两个问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在阅读这篇文章,在这个标题
基类vPtrs的继承,但不能理解在这段中他是什么意思:

I was reading this articles , was at this heading Inheritance of Base-class vPtrs , but couldn't understand what did he mean in this para :

但是,由于多重继承,一个类可以间接继承很多类。如果我们决定将所有基类的vtables合并为一个,vtable可能会变得非常大。为了避免这种情况,不是丢弃所有基类的vPtrs和vtables并将所有vtables合并成一个,编译器只对所有FIRST基类执行此操作,并保留所有后续基类及其基类的vPtrs和vtables

"However, due to multiple inheritance, a class may indirectly inherit from many classes. If we decide to merge the vtables of all the base classes into one, the vtable may become very big. To avoid this, instead of discarding the vPtrs and vtables of all base classes and merging all vtables into one, the compiler only does it to all the FIRST base classes, and retains the vPtrs and vtables of all the subsequent base classes and their base classes."

换句话说,在一个对象的内存占用中,除了所有的first-born,你可以找到所有基类的vPtrs。

In other words, in an object’s memory footprint, you can find the vPtrs of all its base classes all through the hierarchy, except for all the "first-borns". "

任何人请以简单易懂的形式解释此段落。

Can anyone please explain this para in simple understandable form.

在此答案:
跟随此回答

现在感兴趣的代码
(*(foo)((void **) *)(& a))[0]))[1])();
,谁能告诉发生了什么? ,特别是为什么这样做完全在c ++ (void **)(& a)。我知道这是铸造,但& a 返回类型为void *的vptr(在上面的问题链接2中)的地址。

now code of interest (*(foo)((void**)(((void**)(&a))[0]))[1])(); , can anyone tell what is going on? , specially why does this doing exactly in c++(void**)(&a) . i know this is casting but &a returns the address of vptr (in the above question link 2) whose type is void* .

感谢

推荐答案

关于您的第一个问题, '真的跟随
引用的段落是什么;它实际上听起来像作者不
真的了解vtables如何工作(或没有想到它在
细节)。重要的是要意识到,当我们谈到
“ merging”基类和派生类的vtables,我们是
谈论使基类vtable是派生类
表的前缀;基类vtable必须在派生的
类vtable的开始处开始工作;基本
和派生类中的vptr的偏移必须相同(实际上几乎总是为0),基类的
必须放在派生的最开始。和
当然,只能满足一个基础
类的条件。 (大多数编译器将使用出现在
a中的第一个非虚拟基址从左到右扫描代码。)

With regards to your first question, I don't really follow what the quoted passage is getting at; it actually sounds like the author doesn't really understand how vtables work (or hasn't thought about it in detail). It's important to realize that when we speak of “merging” the base and the derived classes' vtables, we are talking about making the base class vtable a prefix of the derived class table; the base class vtable must start at the start of the derived class vtable for this to work; the offset of the vptr in both the base and the derived must be identical (almost always 0, in practice), and the base class must be placed at the very beginning of the derived. And of course, it's only possible to fulfil those conditions for one base class. (Most compilers will use the first non-virtual base appearing in a left to right scan of the code.)

对于表达式,它是完全未定义的行为,
将不适用于某些编译器。或者可能或可能不工作,取决于
的优化级别。并且它中的 void * 被用作任何数量的指针类型的
占位符(包括可能的
指向函数类型的指针)。如果我们采取最内部的部分,我们是
说,& a 是指向(1或更多) void * 。这个指针是
然后解引用((X)[0] *(X) ,因此
((void **)(& a))[0]) * *)(& a)。( [0]
符号表示在此后面可能有更多的值;
[1] 等也可能有效,这不是这种情况。)这个
结果是一个 void * ,然后转换为 void ** 并再次
取消引用,这次真的使用索引是希望
到数组中);这个解引用的结果转换为 foo
(指向函数的指针),然后解引用并且函数
在没有任何参数的情况下被调用。

With regards to the expression, it's completely undefined behavior, and will not work with some compilers. Or may or may not work, depending on the level of optimization. And the void* in it is being used as a placeholder for any number of pointer types (including, probably, pointer to function types). If we take the inner-most part, we're saying that &a is a pointer to (1 or more) void*. This pointer is then dereferenced ((X)[0] is the same as *(X), so (((void**)(&a))[0]) is the same as *(void**)(&a). (The [0] notation suggests that there might be more values behind this one; i.e. that [1], etc. might also be valid. That's not the case here.) This results in a void*, which is then cast to a void** and once again dereferenced, this time really using an index (since it is hopefully into an array); the result of this dereferencing is converted to a foo (a pointer to a function), which is then dereferenced and the function is called without any arguments.

这不会真正工作,它产生了一些假设
,或者在一些情况下甚至一般为真:

None of this will actually work. It makes a number of suppositions which are not always, or in some cases even generally, true:


  • 对象中vptr的偏移量为0.(这一般是真的)

  • vptr本身具有与 void * 相同的大小。 (这几乎总是真的,并且是Posix所要求的)。

  • vtable本身是一个函数指针数组,
    指向函数的指针与 void * 。而
    是真的,函数的指针通常有和
    void * 相同的大小(同样,Posix需要它, Windows),
    很难想象一个实现,如果vtable
    只是一个指向函数的指针数组。

  • 调用的函数不会实际上使用这个指针:
    这只是在特殊情况下才是真的。

  • The offset of the vptr in an object is 0. (This one is generally true.)
  • The vptr itself has the same size as a void*. (This is almost always true, and required by Posix.)
  • The vtable itself is an array of pointers to functions, and that pointers to functions have the same size as a void*. And while it's true that pointers to functions do often have the same size as void* (again, Posix requires it, and it's also true under Windows), it's hard to imagine an implementation which would work if the vtable were just an array of pointers to functions.
  • That the function called doesn't actually use the this pointer: this is only true in exceptional cases.

他显然是使用VC ++ __ thiscall ,这是一个
的微软主义),我只分析了Sun CC的布局,这是
绝对不同。 (和Sun CC和g ++也是非常
不同的,为此,Sun CC 3.1,Sun CC 4.0和Sun CC 5.0
都是不同的。)

He apparently is using VC++ (based on the __thiscall, which is a Microsoft'ism), and I've only analysed the layout of Sun CC, which is definitely different. (And Sun CC and g++ are also very different—for that matter, Sun CC 3.1, Sun CC 4.0 and Sun CC 5.0 are all different.)

除非你真的在编写一个编译器,否则我会忽略所有这些。和
我一定会忽略你引用的表达式。

Unless you're actually writing a compiler, I'd ignore all of this. And I'd certainly ignore the expression you quote.

这篇关于与虚拟函数相关的两个问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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