简单对象内存分布 首先以一个最简单的 Basic 类为例,来看看只含有基本数据类型的对象是怎么分配内存的。...不过大多时候我们不需要手动管理内存对齐,编译器和操作系统会自动处理这些问题。 带方法的对象内存分布 带有方法的类又是什么样呢?...看来对于成员函数来说,只是编译期不让直接调用,运行期并没有保护,我们可以绕过编译限制在对象外部调用。...对于 ptr->printB(); 调用,由于派生类中没有定义 printB() 方法,所以会调用基类的 printB() 方法。 那么在有虚函数继承的情况下,对象的内存布局是什么样?...不同编译器的实现也可能不一样,许多编译器为了访问效率,将虚函数表指针放在对象内存布局的开始位置。这样,虚函数的调用可以快速定位到虚函数表,然后找到对应的函数指针。
什么是虚函数,虚函数的实现原理是什么 直观上来说,虚函数就是类中使用 virtual 关键字描述的函数。...详细请参考 什么是虚表,虚表的内存结构布局如何,虚表的第一项(或第二项)是什么 对于每个存在虚函数的类来说,其都含有一个虚函数表与至少一个虚指针。...对于虚基类来说,虚表中按声明顺序依次保存所有虚函数地址。...对于存在虚函数的类来说,至少存在一个虚函数指针,指针大小与机器相关(int),在64位的机器上,应为8字节,在32位的机器上为4字节。在进行计算的时候还要注意1....不同的数据类型会进行对齐 2.对于多重继承,多重继承几个基类就有几个虚指针。
子类对象 , 父类指针 值为 子类对象 在 堆内存 的地址 , 也就是 将 子类对象 地址 赋值给 父类类型指针 ; 引用 : 父类引用 引用 子类对象 , 将 子类对象 赋值给 父类类型的引用 ; 二...指向 子类对象 定义 一个子类对象 Child child ; 定义父类的指针 , 将 指针 指向 子类对象 的地址 , 这是合法的 ; 代码示例 : // 父类对象 Parent parent...类型兼容性原则 : 父类指针 指向 子类对象 Parent* p_parent2 = NULL; p_parent2 = &child; 该原则的应用场景如下 : 定义函数 , 接收 父类指针...// 子类对象 可以调用 父类公有函数 child.funParent(); // 将指向子类对象的指针传给接收父类指针的函数 // 也是可以的 fun_pointer...// 通过父类指针调用父类函数 p_parent->funParent(); // 将指向子类对象的指针传给接收父类指针的函数 // 也是可以的 fun_pointer
struct的内容直接存放在栈区, class中存放着的指向堆区的指针(猜测),堆区又指针指向的才是class的内容 xcode - Cat address 内存地址属于哪片区域,除了猜测还是有工具可以查看的...} 这就是一个最典型的值类型-结构体。...类的初始化 观察到了堆内存的申请(alloc_ref),以及类应用到堆空间的apply方法. [总结] 引用类型地址中存在的是指针地址而不是值....a是值类型,所以修改不会影响其他副本 情况二 ? a.sub是引用类型,所以在深拷贝的时候会把sub的指针进行浅拷贝。两个变量中的sub指针指向同一片内存空间,所以修改会导致2者都发生变化。...如果理解不了,可以参考OC中的NSString声明需要使用copy关键字。 lldb验证 ? 通过lldb更加直观的看到a的内存布局. ? 通过lldb更加直观的看到aa的内存布局.
对于一个独立开发的类来说,很少需要基类中的某一个类是虚基类,况且新类的开发者也无法改变已经存在的类体系。 4、虚继承在标准库中的使用 C++标准库中的iostream就是一个虚继承的典型案例。...D,里面的内容其实也是一个指针,指向的是类D的运行时信息,这些玩意都是为了支持RTTI的。..._vptr.B - 2:这里存储的是offset_to_top,这个表示的是当前的虚表指针距离类开头的距离,可以看到对于_vptr.B来说这个值就是0,因为_vptr.B就存在于类D的起始位置,而对于_...offset_to_top深度解析:在多继承中,由于不同基类的起点可能处于不同的位置,因此当需要将它们转化为实际类型时,this指针的偏移量也不相同。...在向上动态转换到实际类型时(即基类转派生类),让this指针加上这个偏移量即可得到实际类型的地址。
那么,对于一线经理类,即既要从上级经理那里领取任务干活,又要向下级工人分任务的角色来说,如何在类层次中表达呢?单继承在此就有点力不胜任。...该变量指向一个全类共享的偏移量表,表中项目记录了对于该类 而言,“虚基类表指针”与虚基类之间的偏移量。 其它的实现方式中,有一种是在派生类中使用指针成员变量。...(在G中,虚基类对象C的地址与G的“虚基类表指针”之间的偏移量 ( 当对于所有的派生类来说偏移量不变时,省略“d”前的前缀))。比如,在32位平台上,GdGvptrC是8个字节。...问题出现了:R的地址与P和S的地址不同。表达式(R*)ps与表达式(P*)ps指向类布局中不同的位置。因为函数S::pvf希望获得一个S*作为隐藏的this指针参数,虚函数必须把R*转化为S*。...MSC++的实现不是这样,MSC++有意将S::rvf编译为接受一个指向S中嵌套的R实例,而非指向S实例的指针(我们称这种行为是“给派生类的指针类型与该虚函数第一次被引入时接受的指针类型相同”)。
2.2运行时的数据区域 vm会将管理的内存划分为不同的区域,不同的区域间有各自的用途,以及创建和销毁时间。具体的区域划分如下图: ? ...局部变量表中存放的是:编译期可知的各种基本数据类型(八个基本数据类型),对象引用(可能是指向对象地址的引用指针,也可能是执行代表对象的句柄)和returnAddress类型(指向了一条字节码指令的地址)...VM规范对于方法区来说,也可以不选择连续的物理内存,还可以选择固定大小或者可扩展,甚至你还可以选择不实现垃圾回收。 ...类型指针:即对象指向它的类元数据的指针,VM通过指针确定对象属于哪个类。 注:如果对象是一个数组,那么对象头中还必须有一块用于记录数组长度的数据。 实例数据:对象真正存储的有效信息。 ...如果采用直接指针访问,那么java堆对象的布局就必须考虑如何放置访问类型数据的相关信息,reference中存储的是对象地址。 ?
我们都知道C++多态是通过虚函数表来实现的,那具体是什么样的大家清楚吗?开篇依旧提出来几个问题: 普通类对象是什么布局? 带虚函数的类对象是什么布局? 单继承下不含有覆盖函数的类对象是什么布局?...单继承下含有覆盖函数的类对象是什么布局? 多继承下不含有覆盖函数的类对象是什么布局? 多继承下含有覆盖函数的类对象的是什么布局? 多继承中不同的继承顺序产生的类对象布局相同吗?...,因为Derive是多继承,一般情况下继承了几个带有虚函数的类,对象布局中就有几个虚表指针,并且子类也会继承基类的数据,一般来说,不考虑内存对齐的话,子类(继承父类)的大小=子类(不继承父类)的大小+所有父类的大小...: vbase_offset(8):对象在对象布局中与指向虚基类虚函数表的指针地址的偏移量 vcall_offset(-8):当虚基类Base的引用或指针base实际接受的是Derive类型的对象,执行...: vbase_offset(8):对象在对象布局中与指向虚基类虚函数表的指针地址的偏移量 vcall_offset(-8):当虚基类Base的引用或指针base实际接受的是Derive类型的对象,
我们可以把它想像成保存的是成员函数在类布局中的“相对”地址。让我们来展示一下二者的不同。...这个例子证明了成员函数指针不是常规指针。另外,为什么C++如此费心地去发明这样的语法?很简单,因为它和常规指针是不同的东西,而且这样的类型转换也是违反直觉的。...它让这些繁琐的定义变得清晰起来。关键是,fptr是什么类型?它的类型是: int (Foo::*) (char*); 或者等价地说——FPTR。...我们可以将一个指向派生类的指针赋值给一个指向其基类的指针(即"is-a"关系),而所谓的“逆变性规则”(翻译君:不知道是啥,原文是contravariance rule)正是这种规则的反面。...因为 “一个指向虚成员的指针能在不同地址空间之间传递,只要二者使用的对象布局一样” (此话来自C++老爸 Bjarne Stroustrup 的 《C++程序设计语言》 )。
2.构成布局(what)1.对齐填充先讲一下对齐填充,这个比较简单。HotSpot虚拟机的自动内存管理系统要求对象的起始地址必须是8字节的整数倍,也就是对象的大小是8字节的整数倍。...2.对象头对象头是对象的另一个重要组成部分,它包含了一些关于对象的元信息。具体来说,对象头包括Mark Word和类元信息(类型指针)。...类元信息(类型指针)Phone ph = new Phone(),创建一个对象,会在JVM方法区存放对象的Klass类元信息,对象头的类型指针就是这个对象指向它的类元信息的指针,JVM通过类型指针确定这个对象是哪个类的实例...,比如我创建Animal类,new Animal()实例的类型指针就会指向方法区中的Animal的Klass类元信息。...对象头包括Mark Word和类型指针,Mark Word记录一些对象的关键信息,主要包括锁的状态,类型指针是可以确定这个对象是哪个类的实例。
@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的 @property 的本质是什么?...runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。...objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果,在最顶层的父类中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常...每一个对象内部都有一个isa指针,指向他的类对象,类对象中存放着本对象的 对象方法列表(对象能够接收的消息列表,保存在它所对应的类对象中) 成员变量的列表, 属性列表, 它内部也有一个isa指针指向元对象...类对象中也有一个isa指针指向它的元类(meta class),即类对象是元类的实例。元类内部存放的是类方法列表,根元类的isa指针指向自己,superclass指针指向NSObject类。
而改变布局是有成本的。这对于程序语言设计本身尤为明显,尤其是当语言自身的实现需要与数据绑定的时候。布局确定下来后,基本上语言的设计只能围绕这个布局来展开。...例如为了实现多态,我们知道需要需要在类的实例里面留有一个叫vptr(virtual pointer)以指向虚函数表(virtual function table)。...不管哪种方式,一旦确定下来了,与此相关的虚函数表的引用方式也就确定了。如在多重虚拟继承场景下,把一个子类对象指定给基类对象的指针或引用时,对于是否需要编译器去调整或修改地址这个问题的答案也就确定了。...我们来思考三个问题: 接口值到底是什么?它是指针吗?如果是的话,指针所指向的那块内存内容是什么? any.(Stringer)这样的类型断言到底是如何实现的? 反射所依赖的底层数据结构到底是什么?...two-word结构体的data部分也是一个指针,它指向实际类型所对应的数据部分。比如这个例子里Binary所需要占据的8个字节内存。
0.前言 文章较长,而且内容相对来说比较枯燥,希望对C++对象的内存布局、虚表指针、虚基类指针等有深入了解的朋友可以慢慢看。 本文的结论都在VS2013上得到验证。...多态,简单来说,是指在继承层次中,父类的指针可以具有多种形态——当它指向某个子类对象时,通过它能够调用到子类的函数,而非父类的函数。...这是一种运行期多态,即父类指针唯有在程序运行时才能知道所指的真正类型是什么。这种运行期决议,是通过虚函数表来实现的。...我们强行把类对象的地址转换为 int* 类型,取得了虚函数指针的地址。虚函数指针指向虚函数表,虚函数表中存储的是一系列虚函数的地址,虚函数地址出现的顺序与类中虚函数声明的顺序一致。...vfptr中有两个指针类型的数据(地址),第一个指向了Base类的析构函数,第二个指向了Base的虚函数print,顺序与声明顺序相同。 这与上述的C++对象模型相符合。
RAII是什么? RAII技术的核心是获取完资源就马上交给资源管理。标准库中的智能指针和锁便是比较常用的RAII工具。RAII类需要慎重考虑资源拷贝的合理性。 9. 右值引用有什么作用?...不同的参数类型可以是不同的参数类型,不同的参数个数,不同的参数顺序(参数类型必须不一样)。 2、不能通过访问权限、返回类型、抛出的异常进行重载。 3、方法的异常类型和数目不会对重载造成影响。...纯虚函数和虚函数表 如果类中存在虚函数,那么该类的大小就会多4个字节,然而这4个字节就是一个指针的大小,这个指针指向虚函数表,这个指针将被放置与类所有成员之前。...对于多重继承的派生类来说,它含有与父类数量相对应的虚函数指针。 2. 为什么基类的构造函数不能定义为虚函数?...RTTI是什么?其原理是什么? RTTI是Runtime Type Identification的缩写,意思是运行时类型识别。
# 指针和数组 # 数组与指针截然不同 在 C 语言中,数组和指针是截然不同的两种东西: 数组是相同类型的对象排列而成的集合,而指针的值是地址,表示指向某处。...1 array_p = array; 这是因为,“指向 int 的指针”与“指向 int 的数组的指针”是不同的类型。 array 和 &array 指向的是相同的地址。那么它们到底有何不同呢?...另外,print_array 还需要通过参数 size 来接收数组的长度。因为对于 print_array 来说,array 只是一个指针,它无法知道调用方传递的数组的长度。...下面是运行时的内存布局: # 双指针 双指针”并不是一个严格的标准术语,所谓双指针,其实就是指向指针的指针。...一开始可以在纸上画出堆栈和堆的内存布局,这将有助于你更直观地理解双指针的工作原理。 # 纵横可变的二维数组 我们知道,在 C 语言中没有真正的二维数组,只有数组的数组。
多态意指相同的消息给予不同的对象会引发不同的动作(一个接口,多种方法)。其实更简单地来说,就是“在用父类指针调用函数时,实际调用的是指针指向的实际类型(子类)的成员函数”。...而类A,B都是由类base派生的子类,并且都对成员函数进行了重载。然后我们定义三个base类型的指针Base、a、b分别指向类base、A、B。...可以看到,当使用这三个指针调用func函数时,调用的都是基类base的函数。而使用这三个指针调用虚函数vir_func时,调用的是指针指向的实际类型的函数。...以上,我们可以得出结论当使用类的指针调用成员函数时,普通函数由指针类型决定,而虚函数由指针指向的实际类型决定。...下面是对于子类实例中的虚函数表的图: 我们可以看见,三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样,我们就可以任一静态类型的父类来指向子类,并调用子类的f()了。
关于Simple Bar Chart Simple bar chart是XYChart大类中的Bar chart类型中的最简单的例子。...Bar chart的表现形式简单直观,在数据量较少、数据维度简单等场景下有较好的适用性。对于一个Bar chart,具有俩个重要的组成部分:XY坐标系,Bars。...关键代码解读 源代码主要分成两个部分:第一部分是主窗口布局,以XYChartDemo类为主;第二部分实现图表显示,以ImagePanel类为主。...下面分别说明: xychartdemo.h:XYChartDemo类继承自QDialog类,负责窗口绘制和显示。成员包含一个指向ImagePanel对象的指针,其他的成员主要用于窗口布局。...该类包含一个指向QChartViewer对象的指针和一个指向XYChart对象的指针。
类型指针 对象头第二部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。...实例数据 实例数据部分是对象真正存储的有效信息,包括了程序里各个类型的字段类型,无论是父类继承下来的,还是子类中定义的。一般来说,父类定义的变量总会出现在子类之前。...由于 HotSpot VM 的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是 8 字节的整数倍。...总结 本篇文章我们介绍了 Java 对象在 JVM 中的内存布局,整体可以分为:对象头、实例数据、对齐填充三个部分。 第一部分的对象头包括了对象运行时数据和类型指针。...其中对象运行时数据包括:哈希码、GC 分代年龄、锁状态标志等,类型指针指向对象类型元数据,确定对象是哪个类的实例。第二部分是实例数据,是真正存储的有效信息,包括各个类型的字段。
1 类布局 本节讨论不同的继承方式造成的不同内存布局。 由于C++基于C,所以C++也“基本上”兼容C。...可见即使对于多重继承来说,访问成员变量开销仍然不大。...的成员函数来说,调用哪个成员函数是在编译 时,根据“->”操作符左边指针表达式的类型静态决定 的。...因为pf非虚函数,而pq的类型为Q*,故应该调用到Q的pf函数上,从而该函数应该要求一个Q* const类型的this指针。 对于虚函数 调用来说,调用哪个成员函数在运行时 决定。...不管“->”操作符左边的指针表达式的类型如何,调用的虚函数都是由指针实际指向的实例类型所决定 。比如,尽管ppq的类型是P*,当ppq指向Q的实例时,调用的仍然是Q::pvf()。
多态在我们日常工作中用的算是比较多的一种特性,业界编译器往往是通过虚函数来实现运行时多态,而涉及到虚函数的内存布局往往是最麻烦且容易出错的,本文从一个简单的例子入手,借助gcc和gdb,对内存布局进行分析...PS:(需要注意的是并不是指向Vtable的头,这块一定要注意) 那么,call()函数在运行的时候,因为不知道其参数b所指向具体类型是什么,所以只能通过其它方式进行调用。...::_ZThn16_N7Derived2f2Ev 偏移值为-16,因为是多重继承,所以class Base1和class Base2类型的指针或者引用都可以指向class Derived对象,那么又是如何调用正确的成员函数呢...Base2* b2 = new Derived; b2->f2(); //最终调用Derived::f2(); 由于不同的基类起点可能处于不同的位置,因此当需要将它们转化为实际类型时,this指针的偏移量也不相同...通过让this指针加上offset_to_top的偏移量,就可以让this指针指向实际类型的起始地址。
领取专属 10元无门槛券
手把手带您无忧上云