草庐IT

c++ - vftable[0] 存储的是第一个虚函数还是 RTTI Complete Object Locator?

coder 2024-02-22 原文

我们都知道 C++ 使用 vftable 来动态决定应该调用哪个虚函数。而我想弄清楚我们调用虚函数时背后的机制。我已将以下代码编译为程序集。

using namespace std;

class Animal {
    int age;
public:
    virtual void speak() {}
    virtual void wash() {}
};

class Cat : public Animal {
public:
    virtual void speak() {}
    virtual void wash() {}
};

void main()
{
    Animal* animal = new Cat;
    animal->speak();
    animal->wash();
}

汇编代码非常庞大。我不太明白以下部分。

CONST   SEGMENT
??_7Cat@@6B@ DD FLAT:??_R4Cat@@6B@          ; Cat::`vftable'
    DD  FLAT:?speak@Cat@@UAEXXZ
    DD  FLAT:?wash@Cat@@UAEXXZ
CONST   ENDS

这部分定义了Cat的vftable。但它有三个条目。第一个条目是 RTTI Complete Object Locator。第二个是 Cat::speak。第三个是 Cat::wash。所以我认为 vftable[0] 应该暗示 RTTI Complete Object Locator。但是当我检查 main PROC 和 Cat::Cat PROC 中的汇编代码时,对 animal->speak() 的调用是通过调用 vftable[0] 实现的,而对 animal 的调用->wash()是通过调用vftable[4]实现的。为什么不是 vftable[4] 和 vftable[8]?

PROC main和Cat::Cat的汇编代码如下。

_TEXT   SEGMENT
tv75 = -12                      ; size = 4
$T1 = -8                        ; size = 4
_animal$ = -4                       ; size = 4
_main   PROC

; 23   : {

    push    ebp
    mov ebp, esp
    sub esp, 12                 ; 0000000cH

; 24   :    Animal* animal = new Cat;

    push    8
    call    ??2@YAPAXI@Z                ; operator new
    add esp, 4
    mov DWORD PTR $T1[ebp], eax
    cmp DWORD PTR $T1[ebp], 0
    je  SHORT $LN3@main
    mov ecx, DWORD PTR $T1[ebp]
    call    ??0Cat@@QAE@XZ
    mov DWORD PTR tv75[ebp], eax
    jmp SHORT $LN4@main
$LN3@main:
    mov DWORD PTR tv75[ebp], 0
$LN4@main:
    mov eax, DWORD PTR tv75[ebp]
    mov DWORD PTR _animal$[ebp], eax

; 25   :    animal->speak();

    mov ecx, DWORD PTR _animal$[ebp]
    mov edx, DWORD PTR [ecx]
    mov ecx, DWORD PTR _animal$[ebp]
    mov eax, DWORD PTR [edx]
    call    eax

; 26   :    animal->wash();

    mov ecx, DWORD PTR _animal$[ebp]
    mov edx, DWORD PTR [ecx]
    mov ecx, DWORD PTR _animal$[ebp]
    mov eax, DWORD PTR [edx+4]
    call    eax

; 27   : }

    xor eax, eax
    mov esp, ebp
    pop ebp
    ret 0
_main   ENDP
_TEXT   ENDS

; Function compile flags: /Odtp
;   COMDAT ??0Cat@@QAE@XZ
_TEXT   SEGMENT
_this$ = -4                     ; size = 4
??0Cat@@QAE@XZ PROC                 ; Cat::Cat, COMDAT
; _this$ = ecx
    push    ebp
    mov ebp, esp
    push    ecx
    mov DWORD PTR _this$[ebp], ecx
    mov ecx, DWORD PTR _this$[ebp]
    call    ??0Animal@@QAE@XZ
    mov eax, DWORD PTR _this$[ebp]
    mov DWORD PTR [eax], OFFSET ??_7Cat@@6B@
    mov eax, DWORD PTR _this$[ebp]
    mov esp, ebp
    pop ebp
    ret 0
??0Cat@@QAE@XZ ENDP                 ; Cat::Cat
_TEXT   ENDS

补充:MSVC编译器x86 19.00.23026

最佳答案

虚表的布局依赖于实现。在您的特定情况下,在编译您的示例代码时,Microsoft C++ 编译器为 Cat 生成一个 vtable,其中 speak 虚函数位于偏移量 0,而 wash 函数位于偏移量 4。RTTI 信息位于这些函数之前的偏移量 -4。

这里的问题是微软的程序集输出在说谎。生成的汇编代码将 RTTI 信息放在偏移量 0 处,将 speakwash 函数放在偏移量 4 和 8 处。但这并不是它在目标文件中的实际布局方式编译器生成。反汇编目标文件揭示了这个布局:

                .new_section .rdata, "dr2"
0000    00 00 00 00                                     .long   ??_R4Cat@@6B@
0004                          ??_7Cat@@6B@:
0004    00 00 00 00                                     .long   ?speak@Cat@@UAEXXZ
0008    00 00 00 00                                     .long   ?wash@Cat@@UAEXXZ

不幸的是,Microsoft 的 C/C++ 编译器的汇编输出仅供引用。它不是编译器生成的实际代码的准确和完整表示。特别是它不能可靠地组装成一个工作对象文件。

关于c++ - vftable[0] 存储的是第一个虚函数还是 RTTI Complete Object Locator?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46168052/

有关c++ - vftable[0] 存储的是第一个虚函数还是 RTTI Complete Object Locator?的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  3. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  4. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  5. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  6. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  7. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  8. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

  9. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  10. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

随机推荐