首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

为什么带有vptr的对象要长12个字节?

带有vptr的对象要长12个字节的原因是因为vptr是虚函数表指针,用于实现C++的多态性。在C++中,当一个类中包含虚函数时,编译器会为该类生成一个虚函数表(vtable),vptr指向这个虚函数表。虚函数表中存储了该类的虚函数的地址,通过vptr可以在运行时动态地调用正确的虚函数。

为了实现vptr的功能,编译器会在带有虚函数的类的对象中插入一个指向虚函数表的指针vptr。这个指针的大小通常为4个字节(32位系统)或8个字节(64位系统)。因此,带有vptr的对象会比没有vptr的对象多占用这个指针的大小。

另外,对象在内存中的存储通常需要对齐,以提高访问效率。对齐要求使得对象的大小可能会增加到一个较大的值,例如8字节或16字节的倍数。因此,即使vptr本身只占用4个或8个字节,但为了满足对齐要求,对象的大小可能会增加到12个字节。

总结起来,带有vptr的对象要长12个字节是因为vptr本身的大小以及对齐要求导致的。需要注意的是,具体的对象大小可能会因编译器、操作系统和架构的不同而有所变化。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

为什么重写对象equals方法重写hashcode方法真正原因!

javaGuide里说到了为什么重写hashcode原因: 3)为什么重写 equals 时必须重写 hashCode 方法? 如果两个对象相等,则 hashcode 一定也是相同。...如果没有重写 hashCode(),则该 class 两个对象无论如何都不会相等(即使这两个对象指向相同数据) 但是我没太理解,两个对象有相同code他们不一定是相等又咋样,为什么就要重写hashcode...后面自己看了别的博文,理解了下,我觉得一定要重写hashcode主要原因是保障equals方法特性,即equals返回结果必须与其hashcode比较结果必须保持一致. 为什么这样保障呢?...hashcode确定其唯一性过程 当你把对象加入 HashSet 时,HashSet 会先计算对象 hashcode 值来判断对象加入位置,同时也会与其他已经加入对象 hashcode 值作比较...(摘自我 Java 启蒙书《Head First Java》第二版)。,为什么呢 ?这样我们就大大减少了 equals 次数,相应就大大提高了执行速度。

92030

实现多态必须满足什么条件

为什么要用虚函数 A: 为什么使用派生类和基类对象之间直接赋值不能实现?? 必须用用指针或者引用?...实现多态,必须使用指针或者引用 因为默认赋值运算符并不会操作虚函数表 验证如下:[ Print C++ vtables using GDB] 1.1 vptr 理解成指针 因为不知道vptr...一句话解释: 1.默认赋值运算符并不会操作虚函数表。 2.实现多态,必须使用指针或者引用 为什么要用虚函数 如果不没有声明虚函数 同名函数出现覆盖现象!...函数入口地址 图片可能和代码不符 你应该可以看懂 没有虚函数对象数据布局 成员类型相同: ?...有虚函数对象数据布局 跟深入地方请查看《Inside the C++ Object Model》 我理解 数据部分: 对象在执行赋值 ==操作时候,如果类型不同会发生强制转换 因此需要相同成员

75870

深入探索虚函数表(详细)

这篇博客可能有一点点,代码也有一点点多,但是仔细阅读分析完,会对虚函数表有一个深刻认识。 什么是虚函数表?        ...对于一个类来说,如果类中存在虚函数,那么该类大小就会多4个字节,然而这4个字节就是一个指针大小,这个指针指向虚函数表。...0; }        先看看运行结果,然后再去分析证明:        因为在包含一个虚函数表时候,含有一个虚函数表指针,所占用大小为4个字节,那么这里输出了8个字节,就说明Derive对象含有两个虚函数表指针...,那么我们实现动态联编,就需要用到指针或者引用形式。...总之,如果实现虚函数和多态,就需要通过指针或者引用方式。

54700

深入探索虚函数表(详细)

这篇博客可能有一点点,代码也有一点点多,但是仔细阅读分析完,会对虚函数表有一个深刻认识。 什么是虚函数表?        ...对于一个类来说,如果类中存在虚函数,那么该类大小就会多4个字节,然而这4个字节就是一个指针大小,这个指针指向虚函数表。...,这些代码用来给虚函数指针进行赋值,那么这些操作都是在我们执行memset之前进行,因此在执行了这些操作后,调用了memset函数,使得所有内容都清空了,那么虚函数指针就指向了0,那为什么我们还可以调用...,因此也不可缺少虚函数指针寻址过程,那么我们实现动态联编,就需要用到指针或者引用形式。...总之,如果实现虚函数和多态,就需要通过指针或者引用方式。

1.2K80

c++头脑风暴-多态、虚继承、多重继承内存布局

*) 0x613c28 (gdb) p &son->sisters $4 = (int *) 0x613c38 看出来对象指针所指一块内存,首地址是0x613c20,然后虚表指针占用8个字节,接着依次按照基类和派生类声明成员变量顺序来存放...,这很显然是不正确,因为执行类B构造函数时执行一次类A构造函数,执行类C时候也要执行一次类A构造函数,析构函数同理,到这里问题还不大,毕竟可以编译和运行。...为什么会有歧义呢,我们注释掉d.print()这一行,然后看下对象d内存布局,如下: (gdb) p d $1 = (D) { = { = { _vptr.A...而所谓有歧义,其实就是我们通常所说二义性问题,而二义性问题怎么解决呢?这就回答了我们上一章问题,需要使用虚继承。...五、总结 根据以上分析,总结出如下几点: 一个没有虚函数类,它大小其实就是所有成员变量大小,此时它就是一个由诸多成员变量组成结构体,计算大小时同样要按照字节对齐去计算,下面所有计算大小都需按照字节对齐去计算

64320

本周阅读:深度探索C++对象模型

Moreover, if memberwise initialization copies the values of one object to another, 问题2 通过用子类来拷贝构造 一个父类,为什么父类...译成中文就是,编译器必须要确保如果一个对象有一个或多个vptr,这些vptr不是由原对象来初始化或改变。 也就是说:当使用赋值方式或拷贝构造方式创建一个对象时,这个对象vptr与源对象无关。...;(普通基类就不会了) 情况3 带有虚函数类(添加额外信息) 情况4 带有虚基类类 有基类构造函数,有成员构造函数,还有自己构造函数,哪有优先执行(混乱)?...(我们一定不能拷贝右端类对象vptr地址, 因 为它可能是一个继承类对象) * 当类继承自一个虚基类(不论此基类有没有拷贝操作)时 https://github.com/wangcy6/weekly...上述完成之后, 对象vptr会被初始化, 指向相关虚表 如果有成员初始化列表的话, 将在构造函数体内扩展开来; 这必须在vptr被 设定之后才进行, 以免有一个虚成员函数被调用 最后执行程序员所提供代码

77520

为什么JVM要用到压缩指针?Java对象要求8字节整数倍?

铺垫这么多,指针寻址原理奉上 假如我们这里有3个对象A、B、C,他们大小分别为8、16、8字节为什么假设这几个值,我们先按下不表),并且在内存中连续存储,为更好理解,我们简化为下图: image.png...也就是说原本可表示4GB内存地址,因为1索引表示8个内存地址偏移量,现在可以表示最高存储32GB内存地址了。 伏笔回收:Java对象大小为什么必须是8字节整数倍?...上面的对象A、B、C我们假设大小是8字节、16字节、8字节;共同点你可能发现了,他们都是8字节倍数,其实Java对象大小就必须是8字节整数倍,如果没有这个条件,上面说索引说法也不成立。...JVM如何保证Java对象大小都是8字节整数倍? 用一个普通Java对象举个简单栗子?...从8字节压缩到4字节,听起来好像才少了4个字节,但要知道,因为Java对象补齐8字节倍数;假如一个Java对象刚好满足了8字节整数倍,但因为没有压缩指针多出来4字节,这时又因为补齐,还需要再补上4

84671

vptr? 指针偏移?多态数组? delete 基类指针 内存泄漏?崩溃?

vptr(一般在对象内存模型顶部)必须随着对象类型变化而不断地改变它指向,以保证其值和当前对象实际类型是一致。...]))(p, arg-list); 其中p是基类指针,vptr是p指向对象隐含指针,而slotNum 就是调用虚函数指针在vtable 编号,这个数组元素索引号在编译时就确定下来, 并且不会随着派生层增加而改变...由于基类fun不是虚函数,故p->fun() 调用是Base::fun()(规则2),而且delete p 还会崩溃,为什么呢?...因为此时基类是空类1个字节,派生类有虚函数故有vptr 4个字节,基类“继承”1个字节附在vptr下面,现在p 实际上是指向了附属1字节,即operator delete(void*) 传递指针值已经不是...将基类析构函数改成虚函数,fun() 最好也改成虚函数,只要有一个虚函数,基类大小就为一个vptr ,此时基类和派生类大小都是4个字节,p也指向派生类首地址,问题解决,参考规则3。

98120

VC和GCC内成员函数指针实现研究(一)

最近在《C++对象模型》一书里说到virtual成员函数指针,低于128被cfront编译器认为是虚表偏移量(支持子类对父类函数覆盖)。...不出所料是,(b.*vptr)() 这一行执行是foo_binfo函数(虽然赋值时候给是foo_a)。...GCC成员函数指针占用了两个word,第一个用于记录函数指针或指针偏移,第二个用于记录虚表偏移。 而由于x86架构下默认是2字节对齐,ARM是4字节对齐或者8字节对齐。...GCC正好利用了字节对齐特点,用最后一位来标识是不是虚函数。如果是虚函数则查找虚表,计算实际函数地址;否则就直接跳转到该函数。...所以和VC不同是,VCvcall是跳转,而gcc这种写法是短跳转。按个人理解,GCC方式更利于CPU指令流水线指令缓存。

83330

VC和GCC内成员函数指针实现研究(一)

最近在《C++对象模型》一书里说到virtual成员函数指针,低于128被cfront编译器认为是虚表偏移量(支持子类对父类函数覆盖)。...不出所料是,(b.*vptr)() 这一行执行是foo_binfo函数(虽然赋值时候给是foo_a)。...**GCC成员函数指针占用了两个word,**第一个用于记录函数指针或指针偏移,第二个用于记录虚表偏移。 而由于x86架构下默认是2字节对齐,ARM是4字节对齐或者8字节对齐。...GCC正好利用了字节对齐特点,用最后一位来标识是不是虚函数。如果是虚函数则查找虚表,计算实际函数地址;否则就直接跳转到该函数。...所以和VC不同是,VCvcall是跳转,而gcc这种写法是短跳转。按个人理解,GCC方式更利于CPU指令流水线指令缓存。

50420

【C++】多态 ⑩ ( 不建议将所有函数都声明为 virtual 虚函数 | 多态理解层次 | 父类指针和子类指针步长 )

对象可以直接获取到自身封装 普通函数 , 如果访问虚函数 , 需要增加一次寻址操作 , 因此 这里建议不需要将有 多态 需求函数声明为 虚函数 ; C++ 中 指向某类型对象 指针 运算 ,...获取到 非虚函数 地址 , 不必通过 vptr 指针 从 虚函数表 中获取 函数地址 ; 显然 , 对象可以直接获取到自身封装 普通函数 , 如果访问虚函数 , 需要增加一次寻址操作 , 因此...; 有 虚函数 类 , 在 编译时 , 会生成 虚函数表 , 对应类中生成一个 vptr 指针指向 虚函数表 ; vptr 指针 是 与 对象绑定 , 调用时 从 对象 虚函数表 中查找虚函数...; 通过 父类指针 访问虚函数时 , 直接根据 实际对象 vptr 指针找该对象 虚函数表 , 然后调用 虚函数表 中 虚函数 ; 多态意义 : 多态是 设计模式 基础 , 是 软件框架 基础...自定义 Student 类型 , 则 p++ 计算结果是 p 指针地址值 加上 sizeof(*p) 对象字节长度 ; 显然 父类 与 子类 对象 字节大小是不同 , 在进行数组操作 ,

23350

vptr? 指针偏移?多态数组? delete 基类指针 内存泄漏?崩溃?

vptr(一般在对象内存模型顶部)必须随着对象类型变化而不断地改变它指向,以保证其值和当前对象实际类型是一致。...]))(p, arg-list); 其中p是基类指针,vptr是p指向对象隐含指针,而slotNum 就是调用虚函数指针在vtable 编号,这个数组元素索引号在编译时就确定下来, 并且不会随着派生层增加而改变...由于基类fun不是虚函数,故p->fun() 调用是Base::fun()(规则2),而且delete p 还会崩溃,为什么呢?...因为此时基类是空类1个字节,派生类有虚函数故有vptr 4个字节,基类“继承”1个字节附在vptr下面,现在p 实际上是指向了附属1字节,即operator delete(void*) 传递指针值已经不是...将基类析构函数改成虚函数,fun() 最好也改成虚函数,只要有一个虚函数,基类大小就为一个vptr ,此时基类和派生类大小都是4个字节,p也指向派生类首地址,问题解决,参考规则3。

93000

二进制学习系列-堆溢出

vptr每个对象都会有一个,而vptable是每个类有一个,vptr指向vtable,一个类中就算有多个虚函数,也只有一个vptr;做多重继承时候,继承了多个父类,就会有多个vptr 详情知识请移步:...至少符合以下2个条件: 1.用户可以控制该对象大小 2.用户空间可以对该对象写入数据 Pwnable-UAF详解: 源代码: ? ?...可以看到原来man对象分配到空间是0x30,即48字节,所以当我们再次分配时候也要分配48字节,保证自己拿到是原先被free掉地址空间。 利用: ?...因为这题是从文件中读出内容覆盖,所以我们可以使用python -c来写入转变成不可见字符(由于我试过直接在文档里面写十六进制地址没法被读取,所以才明白转变成不可见字符)。...程序在case2中读取数据填充到data空间时候,开始字节就是vtable。之后是类数据。

89441

C++虚函数知识点总结

---- 虚函数原理——虚函数表 对应虚函数类,该类对象所占内存大小为,数据成员大小+一个指向虚函数表指针 (4字节)。...4+4一共占了8个字节,再加上一个虚函数表指针(4个字节),一共是12个字节 ( 如果该类中没有虚函数,就没有虚函数表指针,也就少4个字节) 如下图所示: 思考:它尽然是个指针,那我们就能通过这个指针来访问它所指向内存所对应内容...(+上偏移量先转成int) 多态使用:父类指针指向子类对象 Father* father1 = &son; father1->Func1();//调用对应func1函数,son中 使用继承虚函数表...如果这个指针指向是子类对象,那么会先调用该子类析构函数,再调用父类析构函数。 如果指向是父类对象,那么只调用父类析构函数。...此时这个函数就可以定义为"纯虚函数",包含纯虚函数类,就叫做抽象类(不能创建对象)。 继承该抽象类子类如果不重写这个纯虚函数,那么它也是不能创建对象

19700

浅析C++类内存布局

4字节,这里是为了对齐8字节。...为方便观察,之后讨论中,我们统一把数据成员都改为int类型,占4字节。...所以上述类设计其实有错误,带多态性质基类应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。...而且,从布局上看,class B部分放在前面,虚基类A部分放在后面。在class B中虚基类A成分相对内存起始处偏移offset等于class B大小(8字节)。C内存布局和B类似。...这个布局与之前不一样:为什么基类subobject反而放到后面了? Class如果内含一个或多个virtual base subobjects,将被分割成两部分:一个不变区域和一个共享区域。

49610

深入浅出C++虚函数vptr与vtable

首先,每个使用虚函数类(或者从使用虚函数类派生)都有自己虚拟表。该表只是编译器在编译时设置静态数组。虚拟表包含可由类对象调用每个虚函数一个条目。...与this指针不同,this指针实际上是编译器用来解析自引用函数参数,vptr是一个真正指针。 因此,它使每个类对象分配大一个指针大小。这也意味着vptr由派生类继承,这很重要。...= (void *)*(unsigned long *)obj; //64位操作系统,占8字节,通过*(unsigned long *)obj取出前8字节,即vptr指针 printf("vptr_addr...:%p\n",vptr_addr); /** * @brief 通过vptr指针访问virtual table,因为虚表中每个元素(虚函数指针)在64位编译器下是8个字节,因此通过*...(unsigned long *)vptr_addr取出前8字节, * 后面加上偏移量就是每个函数地址!

4K30

【C++】多态 ⑧ ( 验证指向 虚函数表 vptr 指针 | 对比定义了虚函数类和没有定义虚函数类大小 )

对比 定义了 虚函数 类 与 没有定义虚函数大小 , 其它成员都相同 , 定义了虚函数类多出了 4 字节 , 多出 4 字节就是 vptr 指针占用内存空间 ; 一、验证指向 虚函数表... vptr 指针 是否存在 1、虚函数表与 vptr 指针由来 " 虚函数表 " 由 C++ 编译器 负责 创建 与 维护 , 被 virtual 关键字 修饰 虚函数 , 会自动 被 C++ 编译器...1 个虚函数 ; 如果 没有虚函数 , 就不会生成虚函数表 ; 如果 类 中有 virtual 虚函数 , 则 该类 每个对象 中 , 都有一个 指向 虚函数表 vptr 指针 ; 虚函数表 存储...判断两个类区别 ; 最终得到 , 有 虚函数 类 , 比 没有 虚函数 类 , 多 4 字节 , 也就是一个指针大小 , 定义了 虚函数 类 , 多出 4 字节就是 vptr 指针大小...// 发现有 virtual 虚函数 会创建 虚函数表 // 在对象中使用 vptr 指针指向 虚函数表 首地址 Child c; // 将父类指针指向子类对象 p = &c; // 通过父类指针调用子类对象

18740
领券