资源预览内容
第1页 / 共20页
第2页 / 共20页
第3页 / 共20页
第4页 / 共20页
第5页 / 共20页
第6页 / 共20页
第7页 / 共20页
第8页 / 共20页
第9页 / 共20页
第10页 / 共20页
亲,该文档总共20页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
C+中对象的内存映像1沐风书苑内容数据成员变量在内存中的布局数据成员变量在内存中的布局类在内存中的映像类在内存中的映像多态的实现机制多态的实现机制集体讨论集体讨论2沐风书苑typedef unsigned char BYTE;enum Color Red=0X01,Blue,Green, Yellow,Black ;struct car bool m_hasSkyLight; Color m_color; bool m_isAutoShift; double m_price; BYTE m_seatNum; ; 类型类型字节数字节数char 1bool 1int 4float 4double 8该结构体的对该结构体的对象在内存中占象在内存中占多少个字节?多少个字节? 32位平台下位平台下15 ?32 3沐风书苑自然对齐自然对齐基本数据类型(基本数据类型(short ,int ,doubleshort ,int ,double)的变量不能)的变量不能简单的存储于内存中的任意地址处,它们的起始地简单的存储于内存中的任意地址处,它们的起始地址必须能被它们占用的字节数整除。址必须能被它们占用的字节数整除。复合类型是由基本类型构成的,对象的起始地址要能复合类型是由基本类型构成的,对象的起始地址要能够满足要求最严格的成员变量自然对齐的要求,如果够满足要求最严格的成员变量自然对齐的要求,如果类中有复合类型的成员变量,依次往后类推。除了整类中有复合类型的成员变量,依次往后类推。除了整个对象要求自然对齐外,内部的成员变量也要自然对个对象要求自然对齐外,内部的成员变量也要自然对齐;编译器不仅考虑了单个对象的自然对齐要求,还齐;编译器不仅考虑了单个对象的自然对齐要求,还考虑到了对象数组的对齐要求。考虑到了对象数组的对齐要求。4沐风书苑m_pricem_color?(填充) m_hasSkyLight m_isAutoShift?(填充) m_seatNum?(填充)0x00031D10:0x00031D14:0x00031D18:0x00031D20:0x00031D28:4 4字节字节4 4字节字节8 8字节字节8 8字节字节8 8字节字节Car Car 的内存布局的内存布局Typedef unsigned char BYTE;Enum Color Red=0X01,Blue,Green, Yellow,Black ;Struct car bool m_hasSkyLight; Color m_color; bool m_isAutoShift; double m_price; BYTE m_seatNum; ;204048:末尾填充末尾填充?1.1.编译器按照成员变量声明的编译器按照成员变量声明的顺序分配空间。顺序分配空间。2.2.为满足各个成员变量自然对为满足各个成员变量自然对齐的要求,可能会在末尾填齐的要求,可能会在末尾填充若干字节。充若干字节。3.3.对象本身的自然对齐取决于对象本身的自然对齐取决于自然对齐要求最高的那个成自然对齐要求最高的那个成员变量。员变量。5沐风书苑 如上,编译器不会随便地在任意一个内存地址上创建一如上,编译器不会随便地在任意一个内存地址上创建一个个C+/CC+/C的变量和对象,它们在内存中的首地址要满足一定的变量和对象,它们在内存中的首地址要满足一定的条件,数据成员也不是紧挨在一起的,而且每个成员的地的条件,数据成员也不是紧挨在一起的,而且每个成员的地址也不是随意安排的,都是经过了编译器的精心规划和计算,址也不是随意安排的,都是经过了编译器的精心规划和计算,这样才能提高对象及其成员的访问效率。这样才能提高对象及其成员的访问效率。解决的办法解决的办法 按照成员变量所占字节从大到小的顺序依次声明。按照成员变量所占字节从大到小的顺序依次声明。如果满足所有成员的自然对齐,内存将会有如果满足所有成员的自然对齐,内存将会有大量的浪费,如何有效的利用内存呢?大量的浪费,如何有效的利用内存呢?6沐风书苑Typedef unsigned char BYTE;enum Color Red=0X01,Blue,Green, Yellow,Black ;Struct car double m_price; Color m_color; bool m_hasSkyLight; bool m_isAutoShift; BYTE m_seatNum; ;m_pricem_color m_hasSkyLight m_isAutoShift m_seatNum ?填充0x00031010:0x00031018:0x0003101C:0x0003101D:0x0003101E:0x0003101C:4 4字节字节4 4字节字节8 8字节字节调整后的内存布局调整后的内存布局调整后,对象所占内存变为调整后,对象所占内存变为1616个字节,末尾只有一个填充字节。个字节,末尾只有一个填充字节。如果想将末尾填充的字节也利用了,可以吸收掉末尾的填充字如果想将末尾填充的字节也利用了,可以吸收掉末尾的填充字节,在该例中,可以将节,在该例中,可以将m_seatNum m_seatNum 设置为设置为short short 型。型。综上所述,类的数据成员类型的选择,声明顺序即排列,采用综上所述,类的数据成员类型的选择,声明顺序即排列,采用的对齐方式,都将影响着对象的实际大小和访问效率。的对齐方式,都将影响着对象的实际大小和访问效率。7沐风书苑 在MS C+/C 中,支持用户在代码中显示的指定复合类型的对齐方式,可用的对齐方式有1,2,4,8,16. 复合类型对象在内存中创建后,每个成员本身的地址取决于它们相对于对象起始地址的偏移字节数,而这个偏移字节数不仅仅与排在它们前面的成员的大小有关,还与用户为这个对象类型指定的成员对齐方式有关。#ifdef _MSC_VER#prama pack(push,4) /按4字节对齐#endif typedef unsigned char BYTE;enum Color Red=0X01,Blue,Green, Yellow,Black ;Struct car bool m_hasSkyLight; Color m_color; bool m_isAutoShift; double m_price; BYTE m_seatNum; ;#ifdef _MSC_VER#pragma pack(pop)#endifm_pricem_color m_hasSkyLight m_isAutoShift m_seatNum0x00031D10:0x00031D18:0x00031D1C:0x00031D24:0x00031D14:4 4字节字节4 4字节字节8 8字节字节 ?填充 ?填充 ?填充4 4字节字节4 4字节字节 4 4字节对齐映像字节对齐映像8沐风书苑声明顺序和对齐方式一经确定,每个成员的地址偏移量就声明顺序和对齐方式一经确定,每个成员的地址偏移量就确定了,不随对象的改变而改变。一个复合类型的对象在确定了,不随对象的改变而改变。一个复合类型的对象在内存中满足了自然对齐的要求,但是其中一些数据成员本内存中满足了自然对齐的要求,但是其中一些数据成员本身却可能不是自然对齐的。上例中,按身却可能不是自然对齐的。上例中,按4 4个字节自然对齐时,个字节自然对齐时,其大小变为其大小变为2424个字节,而成员个字节,而成员m_price m_price 的地址却不能被的地址却不能被8 8整整除。除。9沐风书苑内容数据成员变量在内存中的布局数据成员变量在内存中的布局类在内存中的映像类在内存中的映像多态的实现机制多态的实现机制集体讨论集体讨论10沐风书苑Class Rectangle public: Rectangle():m_length(0),m_width(0) Rectangle(). float GetLength() const return m_length; float GetWidth() const return m_width; void Draw() protected: Rectangle (const Rectangle ©) . Rectangle & operator=(const Rectangle &assign) .private: float m_length; float m_width; static unsigned int m_count;11沐风书苑用户内存区用户内存区m_lengthm_widthRectangle rect 1 ;m_lengthm_widthRectangle rect 1;.程序静态数据区程序静态数据区m_count代码段代码段 Rectangle:Rectangle (const Rectangle ©) Rectangle : operator=(const Rectangle &assign) Rectangle:Rectangle()Rectangle:Rectangle()Rectangle:Getlength() Rectangle:Getwidth()Rectangle Rectangle 对象的内存映像对象的内存映像Rectangle:Draw()12沐风书苑内容数据成员变量在内存中的布局数据成员变量在内存中的布局类在内存中的映像类在内存中的映像多态的实现机制多态的实现机制集体讨论集体讨论13沐风书苑 class Shape public: Shape():m_color (0) virtual shape() float GetColor const return m_color; void SetColor (float color)m_color=color;) virtual Draw()=0; /纯虚函数private: float m_color;class Rectangle :public Shapepublic: .private: .14沐风书苑程序静态数据区程序静态数据区m_count代码段代码段 Rectangle:Rectangle (const Rectangle ©) Rectangle : operator=(const Rectangle &assign) Rectangle:Rectangle()Rectangle:Rectangle()Rectangle:Getlength() Rectangle:Getwidth()Rectangle:Draw()用户内存区用户内存区Rectangle rect 1 ;Rectangle rect 2;.m_lengthm_widthm_color_vptrm_lengthm_widthm_color_vptrtype_info type_info_ptr destructor_ptr draw_ptr加上继承和多态特性的加上继承和多态特性的Rectangle Rectangle 对象模型对象模型Shape:GetColor()Shape:SetColor()15沐风书苑 增加了继承和虚函数的类的对象模型更加复杂,增加了继承和虚函数的类的对象模型更加复杂,具体规则如下:具体规则如下: 为每一个多态类创建一个虚函数指针数组vtable,该类的所有虚函数(继承自基类的或者新增的)的地址都保存在这张表里。 多态类的每一个对象中安插一个指针成员vptr (隐藏的),其类型为指向函数指针的指针,它总是指向所属的vtable,也就是说,vptr当前所在的对象是什么类型,就指向这个类型的vtable. 如果基类已经插入了vptr,则派生类将继承和重用该vptr. 为了支持RTTI(Runtime Type Identification),为每一个多态类创建一个type_info 对象,并把其地址保存在vtable一个固定位置,一般为第一个位置,这取决于编译器。 只有虚函数访问需要经过vptr的间接寻址,增加了一层间接性,带来了一些额外的开销。16沐风书苑关于关于vtable vtable 中虚函数指针的排列顺序,有以下规则:中虚函数指针的排列顺序,有以下规则: 一个虚函数如果在当前class中第一次出现(如果在基类中已经出现过,不算第一次),则将其地址插入到该 class的每一个vtable 的尾部。 如果派生类改写了基类的虚函数,则这个函数的地址在派生类vtable中的位置与它在基类vtable中的位置一致;也就是说虚函数第一次出现的时,它在vtable中的位置一旦确定,就不会随着派生层次的增加而改变。 派生类没有改写的基类的虚函数被继承插入到派生类vtable中,且在派生类vtable中的位置与其在基类中的位置相同,即派生类的vtable 布局兼容其基类的vtable。17沐风书苑 class Shape public: Shape():m_color (0) virtual shape() float GetColor const return m_color; void SetColor (float color)m_color=color;) virtual Draw()=0; /纯虚函数private: float m_color;class Rectangle :public Shapepublic: . virtual int GetName() ;Private: .18沐风书苑 type_info_ptr destructor_ptr draw_ptrShape:_vtable type_info of Shape Shape:Shape()0 getName_ptrRectangle:_vtable type_info_ptr destructor_ptr draw_ptr type_info of RectangleRectangle:Rectangle() Rectangle:Draw() Rectangle:GetName()19沐风书苑vptr vptr 的初始化的初始化 vptr 并非static 成员,因此只能在构造函数中进行初始化。由于每一个派生类的构造函数会先调用基类的构造函数进行初始化,所以除了基类中的构造函数中vptr是初始化外,其他派生类的构造函数中都是不断的更改vptr 中的值,使其指向当前类的vtable。也就是说vptr必须随着对象类型的变化而不断改变它的指向,以保证其值和当前对象的类型一致。 数组中只能存放相同类型的元素,但是类中的成员函数类型肯定不可能全部相同,那vtable 如何存放这些成员函数的指针?20沐风书苑
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号