关闭

[读书笔记] 深入探索C++对象模型-第三章 Data语义学(上)

178人阅读 评论(0) 收藏 举报

注:以下部分图片来自原书

1. 关于类的大小,有三个因素会影响类的大小:

a. 语言本身所造成的额外负担,例如虚基类,相应的子类会存在某种形式的指针,或者直接指向虚基类子对象,或者指向一个存放着虚基类子对象地址或者偏移量的表格。又如,虚函数,继承自含有虚函数基类的子类会含有一个指向虚函数表的指针。总之这些都会增加类对象的大小。

b. 对齐操作,即为了使总线的传输效率达到最高,类大小会被进行对齐操作,调整为某一个数字大小的倍数,32位机器上,通常为4的倍数,也就是说,一个类的实际占用大小为3,那么会被调整为4,类似的东东。

例如以下例子:


c. 编译器对于特殊情况的优化处理,例如对于大小为1的虚基类,某些编译器会提供特殊的支持,在此种情况下,空的虚基类会被视为子类对象最开头的一部分,这样就不会占用任何额外的空间,在此模型下类Y和Z的大小如下:


注意虚拟继承中不管虚基类在继承体系中出现了多少次,它在子类中始终只有一份,这也是虚继承由来的原因之一,所以对于类A的大小,首先是虚基类X大小为1,加上Y的大小减去因虚基类X而配置的大小(因为虚基类在子类中只有一份,所以要减掉在Y中的大小),结果是4,同理Z的大小也是4,目前类A的大小是9,对齐后是12(VS2015中测试是8,估计是因为堆空虚基类进行优化了)。

2. 关于数据成员的存取。

a. 对于静态数据成员,使用指针操作和对象操作效率是一样的,例如:a.staticMember = 0和pA->staticMember = 0都会被转化成为:A::staticMember = 0,所以二者是一样的。另外,对一个静态成员取地址会得到相应类型的指针,而不是指向类成员的指针(关于类成员指针的概念后续会有详细记录)。

b. 对于非静态成员,通过类对象操作数据成员时,编译器会把类对象的起始地址加上类成员的偏移,例如:a._y=0.0,那么地址&a._y等于&a + (&A::_y - 1),(指向类成员的指针偏移量总是被加上1,这个之后会有详细记录),如果在子类中操作继承而来的虚基类(非虚基类的不会有间接性)的数据成员,需要进行一层间接性,这个之后会有详细的整理。

3. 对于操作a.x = 0.0和pA->x = 0.0在以下情况下效率是不一样的:当x来源于虚基类时,在编译期,我们无法pA的确切类型(只可能知道静态类型,而不知道动态类型),他可能指向某个基类,也可能指向某个子类,因此无法知道x确切的偏移位置,所以存取操作必须要延迟到执行期,经由一个额外的简介引导来解决。如果使用a,则不会存在这样的不确定性,因为a的类型是确定知道的,所以成员的偏移在编译期就是确定的。

注:关于指针的静态类型和动态类型,例如:

B b; //B public inheriance of A
A* pA = b;
此时pA的静态类型为A,动态类型为B,也就是说,静态类型时固定不可变的,而动态类型可变,实际上C++的多太机制就是通过父类指针指向不同的子类对象,变换其动态类型实现的。

后续会继续整理本章的其他内容。








0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:9817次
    • 积分:569
    • 等级:
    • 排名:千里之外
    • 原创:49篇
    • 转载:4篇
    • 译文:0篇
    • 评论:0条