c++ ---深度探索C++对象模型读书笔记3

3.Data 语意学

class X {};           // sizeof 得到1

class Y :public virtual X {};   // sizeof 得到8

class Z: public virtual X {};  // sizeof 得到8

class A:public Y, public Z {};  //sizeof 得到12

 

 X中没有任何成员,但是它也不为空,它是有一个隐晦的1byte,那是被编译器安插进去的一个char,这使得这个class 生成的两个objects 得以在内存中配置独一无二的地址。

至于Y,Z的大小与机器的位数和编译器有关,假如是32位:

  1. 语言本身的额外负担:支持virtual base classes ,在derived class 中,这个负担就反应在某种形式的指针身上,它或者指向virtual base class subobject,或者指向一个相关的表格;表格中存放的若不是virtual base class subobject的地址,就是其偏移量。
  2. 编译器对特殊情况的优化,virtual base class X subobject 的1bytes大小也会出现在class Y和Z身上。
  3. 内存对齐的限制,class Y和Z的大小截止目前是5bytes,补齐后就是8bytes.

注意:empty virtual base class 也称为virtual interface,被视为derived class object 最开头的一部分,所以就是也不会占用任何空间,包括那个1byte的char 也没有。

       一个virtual base class subobject 只会在derived class 中存在一份实体,不管它在class 继承体系中出现了多少次。

C++对象模型尽量以空间优化和存取速度优化的考虑,非静态成员nonstatic data member 直接存放在每一个class object之中,,对于继承而来的nonstatic data member也是如此,不过并没有强制定义其间的排列顺序,至于static data members, 则被放置在程序的一个global data segment中,不会影响个别的class object 的大小。不管该class 被产生出多少个objects, static data members 永远只存在一份实体,但是一个template calss 的static data members的行为稍有不同。

      

3.1 Data member 的绑定

       在一个inline member function 躯体之内的一个data member 绑定操作,会在整个class 声明完成之后才发生。

 

    1. Data Member 的布局

非静态成员在class object 中的排列顺序从将和其被声明的顺序一样,任何中间介入

的静态数据成员都不会被放进对象的布局之中。

在同一个access section (public、private、protected等区段)。Member的排列只需符合“较晚出现的members在class object中有较高的地址”。Members的边界调整(alignment)可能就需要填补一些bytes. 编译器也可能会合成一些内部使用的data members, 比如vptr,被合成存在于有虚函数的class object 中,位置就是放在一个class object的最前端。

编译器会把一个以上的access sections 连锁在一起,依照声明次序,称为一个连续的区块,Access sections 的多寡并不会招来额外的负担,例如在一个section中声明8个members ,或是在8个sections 中总共声明8个members,得到的object大小是一样的。

 

3.3Data Member 的存取

 A.static data members:

       每一个static data member 只有一个实体,存放在程序的data segment 之中,每次程序取用static member ,就会被内部转化为对该唯一的extern 实体的直接存取操作。

     若取一个static data member 的地址,会得到一个指向其数据类型的指针,而不是一个指向其class member的指针,因为,static member 并不内涵在一个class object 之中。

 

B. name-mangling

如果两个类,每一个都声明一个static member freelist,都被放在程序的data segment时,就会名称冲突,编译器会通过对每一个static data member 编码,称为 name-mangling。通常就是表格或者语法措辞等。但都要满足两点:

       1.一种算法,推导出独一无二的名称。

       2.万一编译器或者环境工具必须和使用者交谈,,那些独一无二的名称可以轻易被推导回到原来的名称。

 

C.Nonstatic data members

       Nonstatic Data Members 直接存放在每一个class object之中。除非经由明确的(explicit)或暗语的(implicit)class object, 否则没有办法直接存取它们。在一个成员函数中,直接处理一个非静态的数据成员,那么“implicit class object”就会发生了,其实际上由this指针表达完成。

       欲对一个非静态数据成员变量进行存取操作,编译器需要把class object的其实地址加上data member的偏移量(offset)

 

-1操作,指向data member的指针,其offset值总是被加上1,这样可以使,编译系统区分出“一个指向data member的指针,用以指出class的第一个member” 和“一个指向data member的指针没有指出任何member”两种情况。

每一个非静态数据成员的偏移量在编译期间就可获知,如果成员数据一个base class subobject (派生自单一或者多重继承串链)也是一样。因此,存取一个非静态数据成员,其效率和存取一个C 结构体成员或者一个nonderived class 的member 是一样的。虚拟继承将为“经由base class subobject”存取class members 导入一次新的间接。

3.4“继承”与 Data Member

一个derived class object 所表现出来的东西,是其自己的member加上其base class(es)members 的总和。一般base class members总是先出现,但是数据virtual base class的除外。出现在derived class 中的base class subobject有其完整原样性。

多重继承的一个语意上的副作用是,它必须支持某种形式的“shared subobject继承”。

这种菱形继承,实际上我们只需要一份 ios subobject就好了,语言层面的解决办法就是导入所谓的虚拟继承:

一般class如果内含一个或者多个virtual base class subobject ,将被分割成两部分:一个是不变局部,一个时共享局部。不变局部中的数据,不管后继如何衍化,总是拥有固定的offset(从object头算起),所以这一部分数据可以被直接存取,至于共享局部,所表现的就是virtual base class subobject ,其位置会因为每次派生操作而又变化,所以他们只能被间接存取, 一般的布局策略是先安排好derived class 的不变的部分,然后在建立其共享部分。至于共享部分的存取,不同的编译器基于不同的实现模型,每一种模型都是用来解决“存取shared subobject内的数据,因每次派生操作而有变化所引发的问题”

1.编译器cfront会在每一个derived class object 中安插一些指针,每一个指针指向一个virtual base class。要存取继承得来的virtual base class members 可以使用相关指针间接完成。但是继承多了存储的指针就多,间接存取层次也增加。

2.编译器MetaWare和其他编译器,会经由拷贝操作取得所有的嵌套虚基类指针,放到dervied class object中,解决层次增加的问题。

3.Microsoft编译器,引入virtual base class table ,每一个class object 如果有一个或多个virtual base classes,就会由编译器安插一个指针,指向这个表,真正的virtual base class 指针,当然是被放在这个表格中。来解决存储指针多的问题。

4.另一种就是在virtual function table 中放置 virtual base class的offset(而不是地址)。Sun编译器中,virtual function table 可由正值和负值来索引,如果为正值,就是所引导virtual functions, 如果是负值,则索引到virtual base class offsets。

一般而言,virtual base class 最有效的一种运用形式是:一个抽象的virtual base class,没有任何data members。

3.5指向data members 的指针

       &Point3d::z   //类的,取一个非静态数据成员的地址,将会得到它在class中的offset

       &origin.z     //实例的,取一个绑定在真正class object 身上的data member的地址,将会得到该member在内存中真正地址。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值