zoukankan      html  css  js  c++  java
  • 《inside the c++ object model》读书笔记 之三:Data语意学

    Data语意学

    ...引子:

      C++对象模型尽量以空间优化和存取速度考虑来表现nonstatic data members,并且和C语言struct保持数据配置的兼容性,它把nonstatic data members 直接存放在每一个class object之中,对于继承而来的nonstatic data members(无论是virtual或是nonvirtual base class)也是如此,不过并没有强制定义其空间排列顺序,至于static data members,则被放置在程序的一个global data segment中,不会影响个别的objects的大小,static data members永远只存在一份实体(对于template class的static data members情况稍有不同).
      每一个class object 必须有足够的大小,有时会很大,因为:
      1)由编译器自动加上的额外data members,用以支持某些语言特性(主要是各种virtual 特性).
      2)因为alignment的需要.

    3.1 Data Member的绑定:
    ...一个inline函数实体,在整个class声明未被完全看见之前是不会被评估求值的,如果一个inline函数在class声明之后立刻被定义的话,那就还是会对其评估求值的.
      但是,对于member function的argument list并不为真,argument list中的名称还是会在它们第一次遭遇时被适当地决议完成,所以extern 和nested type names之间的非直觉绑定还是会发生,因此,应该始终把"nest type 声明"放在class的起始处.

    3.2 Data Member的布局:
    ...C++ Standard规定,在同一个access section(如public,private...)中,members的排列只需符合"较晚出现的members在class object之中有较高的地址即可",也就是说各个members不一定连续排列,在这之间可能有alignment填补的一些bytes,同时编译器可能会合成一些内部使用的data members,以支持整个对象模型(如vptr).

    3.3 Data Member存取
    1)static data members
      static data members被编译器提出与class之外,并视为一个global变量,每一个member的存取许可,以及class的关联并不会导致任何空间是或是执行时间上的额外负担-无论是在个别的class objects或是static data member本身,这是通过一个指针和通过一个对象来存取member,结论完全相同的唯一情况.
      为了解决static data member的命名冲突问题,一般采取name-mangling手法.

    2)nonstatic data members
      nonstatic data members直接存放在每一个class object之中,除非经由明确的(explicit)或是暗喻的(implicit)class object,没有办法直接存取它们.
      就存取效率来说,因为对于每一个nonstatic data member 的偏移量,在编译时期即可获知,甚至如果是一个base class subobject(派生自单一或是多重继承串连)也是一样,因此存取一个nonstatic data member和存取一个C struct member在效率上没有差别,但是对于虚拟继承,如果存取的变量正是从一个virtual base class中继承而来的member时,其效率就会差一些.

    3.4 "继承"与data member
    ...在C++继承模型中,一个derived class object所表现出来的东西,是其自己的members加上其base classes members的总和,至于derived class members和base classes members的排列顺序在C++ Standard并未强制规定,对于大部分编译器来说,base class membes总是先出现,但是属于virtual base class的除外.

    1)单一继承并且不含有多态:
      一般而言,单一无多态的继承并不会增加空间或是存取时间上的额外负担,但是把一个class分解为两层或是多层,有可能会为了"表现class体系之抽象化"而膨胀所需的空间,C++语言保证"出现在derived class 中的base class subobject保持其完整原样性",所以,有可能因为alignment填补的空间在derived class保持其原来的大小,而导致derived class的空间膨胀,这就会导致一些非预期的结果发生.

    2)加上多态:
      加上多态以后带来的空间和时间上的负担:
      1)导入一个virtual table,用来存放它所声明的每一个 virtual functions的地址,这个table的元素数目一般是被声明的virtual functions的数目在加上一两个slots(用以支持runtime type identification).
      2)在每一个class object中导入一个vptr,提供执行期的链接,使每一个object能够找到相应的virtual table.
      3)加强constructor使它能够为vptr设定初值,让它指向class所对应的virtual table.
      4)加强destructor,使它能够摸消"指向clas之相关virtual table"的vptr,vptr很可能已经在derived class destructor 中被设定为derived class的virtual table的地址.

    ...对于vptr的位置:在C++2.0以后由于虚拟继承和抽象基类的出现使得vptr,一般会被放置在class的起始处.

    3)多重继承:
    ...对于多重继承其复杂度在于derived class和其上一个乃至于上上一个base class......之间的"非自然"关系.

    ...对一个多重派生的对象,将其地址指定给"最左端(也就是第一个)base class",情况和单一继承时相同,因为二者都指向相同的起始地址,至于第二个和后继的base class的地址指定操作,则需要将地址进行一下修改,加上或是减去介于中间的base class subobjects大小.

    ...对于存取第二个(或后继)base class中的一个data memeber的效率问题,由于members的位置在编译时就固定利率,因此存取members只是一个简单的offset运算,就像单一继承一样简单,不管是经由一个指针,reference,或是一个object来存取.

    4)虚拟继承:
    ...一般虚拟继承的实现方法是,如果class内涵一个或是多个virtual base class subobject,将被分割为两个部分:一个不变局部和一个共享局部.
      不变局部中的数据,不管后继如何衍化,总是拥有固定的offset,所以这一部分数据可以被直接存取,至于共享局部,所表现的就是virtual base class subobject.这一部分的数据,其位置会因为每次的派生操作而有变化,所以它们只能间接存取,各个编译器的差异就在于间接存取的方法不同.
      一般的布局策略是先安排好derived class的不变部分,然后在建立其共享部分.
      在这中间如何存取class的共享部分是问题的关键,对于virtual base class的模型的实现有两种方法,一种是Microsoft 编译器的做法,就是引入所谓的virtual base class table,每一个class object如果有一个或是多个virtual base classes,就会有编译器安插一个指针,指向virtual base class table,真正的virtual base class指针,就被放在该表格当中,还有一种就是在virtual function table中放置virtual base class的offset(不是地址).

    注:一般而言,一个抽象的virtual base class没有任何data members.

    3.5 指data members的指针:
    ...取一个类的nonstatic data member的地址,得到的将是该数据成员在类中的偏移量,然而,实际得到值是该数据的偏移地址加一,这个问题在于,为了区分一个"没有指向任何data member"的指针和一个指向"第一个data member"的指针,每一个真正的member offset的值都被加一(在vc++ 2008中,没有加一).

    ...取一个"nonstatic data member"的地址将会得到它在class中的offset,取一个"绑定与真正class object身上的data member"的地址,将会得到该member在内存中的真正地址.

  • 相关阅读:
    ORACLE字符集基础知识
    Outlook 2013 在邮件里面点击超链接时弹出“组织策略阻止我们为您完成此操作”
    Apache Kafka: 优化部署的10个最佳实践
    经济学人使用Golang构建微服务历程回顾
    如何做自己的服务监控?spring boot 2.x服务监控揭秘
    web框架的前生今世--从servlet到spring mvc到spring boot
    如何做自己的服务监控?spring boot 1.x服务监控揭秘
    手机上不了网
    Cross-Browser, Event-based, Element Resize Detection(转)
    迷你MVVM框架 avalonjs 组件编写指南
  • 原文地址:https://www.cnblogs.com/suiyu/p/2508910.html
Copyright © 2011-2022 走看看