草庐IT

c++ - 虚拟表和 _vptr 存储方案

coder 2024-01-31 原文

有人能解释一下这个不同类的虚拟表是如何存储在内存中的吗?当我们使用指针调用函数时,它们如何使用地址位置调用函数?我们可以使用类指针获得这些虚拟表内存分配大小吗?我想看看一个类的虚拟表使用了多少内存块。我怎么能看到呢?

class Base
{
public:
    FunctionPointer *__vptr;
    virtual void function1() {};
    virtual void function2() {};
};

class D1: public Base
{
public:
    virtual void function1() {};
};

class D2: public Base
{
public:
    virtual void function2() {};
};
int main()
{
    D1 d1;
    Base *dPtr = &d1;
    dPtr->function1();
}

谢谢!提前

最佳答案

要记住的第一点是免责声明:标准实际上并不保证这些。该标准说明了代码需要是什么样子以及它应该如何工作,但实际上并没有具体说明编译器需要如何实现这一点。

也就是说,基本上所有 C++ 编译器在这方面的工作方式都非常相似。

所以,让我们从非虚拟函数开始。它们分为两类:静态和非静态。

两者中较简单的是静态成员函数。静态成员函数几乎就像一个全局函数,它是 friend类,除了它还需要类名作为函数名的前缀。

非静态成员函数稍微复杂一些。它们仍然是被直接调用的普通函数——但是它们被传递了一个指向它们被调用的对象实例的隐藏指针。在函数内部,您可以使用关键字 this引用该实例数据。因此,当您拨打类似 a.func(b); 的电话时,生成的代码与您为 func(a, b); 获得的代码非常相似。

现在让我们考虑虚函数。这是我们进入 vtables 和 vtable 指针的地方。我们有足够的间接性,最好画一些图表来看看它是如何布局的。这几乎是最简单的情况:一个类的一个实例具有两个虚函数:



因此,该对象包含它的数据和一个指向 vtable 的指针。 vtable 包含一个指向该类定义的每个虚函数的指针。然而,为什么我们需要如此多的间接性可能不是很明显。要理解这一点,让我们看下一个(稍微有点)更复杂的情况:该类的两个实例:



注意类的每个实例如何拥有自己的数据,但它们共享相同的 vtable 和相同的代码——如果我们有更多的实例,它们仍然会在同一个类的所有实例中共享一个 vtable。

现在,让我们考虑派生/继承。例如,让我们将现有类重命名为“Base”,并添加一个派生类。由于我感觉很有想象力,我将其命名为“派生”。如上,基类定义了两个虚函数。派生类覆盖其中一个(但不是另一个):



当然,我们可以将两者结合起来,每个基类和/或派生类都有多个实例:



现在让我们更详细地研究一下。派生的有趣之处在于,我们可以将指向派生类对象的指针/引用传递给编写以接收指向基类的指针/引用的函数,并且它仍然有效——但是如果您调用虚函数,你得到的是实际类的版本,而不是基类。那么,它是如何工作的呢?我们如何将派生类的实例视为基类的实例,并且仍然有效?为此,每个派生对象都有一个“基类子对象”。例如,让我们考虑这样的代码:

struct simple_base { 
    int a;
};

struct simple_derived : public simple_base {
    int b;
};

在这种情况下,当您创建 simple_derived 的实例时,你会得到一个包含两个 int 的对象s:ab . a (基类部分)在内存中对象的开头,b (派生类部分)遵循这一点。因此,如果您将对象的地址传递给需要基类实例的函数,它将在基类中存在的部分上使用,编译器将其放置在对象中与它们相同的偏移量处d 位于基类的对象中,因此函数可以在不知道它正在处理派生类的对象的情况下操作它们。同样,如果你调用一个虚函数,它只需要知道 vtable 指针的位置。就其关心而言,类似 Base::func1基本上只是意味着它遵循 vtable 指针,然后使用指向某个指定偏移量的函数的指针(例如,第四个函数指针)。

至少现在,我将忽略多重继承。它给图片增加了相当多的复杂性(尤其是当涉及到虚拟继承时)而且你根本没有提到它,所以我怀疑你真的关心。

至于访问其中的任何一个,或以任何方式使用而不是简单地调用虚函数:您可能能够为特定的编译器想出一些东西——但不要指望它是可移植的。尽管调试器之类的东西经常需要查看这些东西,但所涉及的代码往往非常脆弱且特定于编译器。

关于c++ - 虚拟表和 _vptr 存储方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45887575/

有关c++ - 虚拟表和 _vptr 存储方案的更多相关文章

  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-on-rails - 如何优雅地重启 thin + nginx? - 2

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

  3. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  4. ruby - Rack:如何将 URL 存储为变量? - 2

    我正在编写一个简单的静态Rack应用程序。查看下面的config.ru代码:useRack::Static,:urls=>["/elements","/img","/pages","/users","/css","/js"],:root=>"archive"map'/'dorunProc.new{|env|[200,{'Content-Type'=>'text/html','Cache-Control'=>'public,max-age=6400'},File.open('archive/splash.html',File::RDONLY)]}endmap'/pages/search.

  5. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  6. 在VMware16虚拟机安装Ubuntu详细教程 - 2

    在VMware16.2.4安装Ubuntu一、安装VMware1.打开VMwareWorkstationPro官网,点击即可进入。2.进入后向下滑动找到Workstation16ProforWindows,点击立即下载。3.下载完成,文件大小615MB,如下图:4.鼠标右击,以管理员身份运行。5.点击下一步6.勾选条款,点击下一步7.先勾选,再点击下一步8.去掉勾选,点击下一步9.点击下一步10.点击安装11.点击许可证12.在百度上搜索VM16许可证,复制填入,然后点击输入即可,亲测有效。13.点击完成14.重启系统,点击是15.双击VMwareWorkstationPro图标,进入虚拟机主

  7. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  8. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  9. ruby-on-rails - 为什么在 Rails 5.1.1 中删除了 session 存储初始化程序 - 2

    我去了这个website查看Rails5.0.0和Rails5.1.1之间的区别为什么5.1.1不再包含:config/initializers/session_store.rb?谢谢 最佳答案 这是删除它的提交:Setupdefaultsessionstoreinternally,nolongerthroughanapplicationinitializer总而言之,新应用没有该初始化器,session存储默认设置为cookie存储。即与在该初始值设定项的生成版本中指定的值相同。 关于

  10. ruby-on-rails - 尝试设置 Amazon 的 S3 存储桶 : 403 Forbidden error & setting permissions - 2

    我正在关注Hartl的railstutorial.org并已到达11.4.4:Imageuploadinproduction.我做了什么:注册亚马逊网络服务在AmazonIdentityandAccessManagement中,我创建了一个用户。用户创建成功。在AmazonS3中,我创建了一个新存储桶。设置新存储桶的权限:权限:本教程指示“授予上一步创建的用户读写权限”。但是,在存储桶的“权限”下,未提及新用户名。我只能在每个人、经过身份验证的用户、日志传送、我和亚马逊似乎根据我的名字+数字创建的用户名之间进行选择。我已经通过选择经过身份验证的用户并选中了上传/删除和查看权限的框(而不

随机推荐