高山流水

----- 要黑就黑彻底

Inside The C++ Object Model 学习笔记--The Semantics of Data

Chapter 3. The Semantics of Data : Data 语义学

示例代码:
class X {};
class Y : public virtual X {};
class Z : public virtual X {};
class A : public Y, public Z {};


一个类对象的大小受三个因素的影响
i.   语言本身所造成的额外负担(overhead),  当语言要支持virtual base class 时,就会导致一些额外的负担.
ii.  编译器对特殊情况所提供的优化处理,  如virtual base class class X subobject 的1bytes大小会出现在子类Y, Z的身上.
      如: sizeof(Y) = sizeof(Z) = 4(8) // 这里的4(8)和编译器相关
     
      有时候有的编译器会用empty virtual base class 技术来优化, VC就是采用这一技术的, 这样virtual base class 就不用占用大小了.

iii.  Alignment 的限制, Y和Z的大小本来大小都是4, 加上virtual base subobject的1bytes的大小共5个字节, 但实际上去是8bytes,这里就是受到字节对齐的影响.

C++对象模型对数据的存放结构是:
i.   把nonstatic data members直接的放在class object之中, 对继承(不管是virtual 或 nonvirtual base class )而来的nonstatic data members也是这一样的.
ii.  没有强制定义其间的排列顺序
iii. 对static data members, 则被放置在一个global data segment中, 不会影响单个类的大小, 并且只保存一份实体. (template有所不同)

3.1 Data Member的绑定(The Binding of Data Member)
    示例代码:

 1 // A third party foo.h header file 
 2  // pulled in from somewhere 
 3  extern float x; 
 4 
 5  // the programmer's Point3d.h file 
 6  class Point3d 
 7  { 
 8  public
 9     Point3d( floatfloatfloat ); 
10 
11     // question:  which x is returned and set? 
12     float X() const { return x; } 
13 
14     void X( float new_x ) const { x = new_x; } 
15 
16     //  
17 
18  private
19     float x, y, z; 
20  }; 
21 
22 


 在早期的编译器中会出错, 不过在 C++2.0后就不会了, 在C++2.0后, 采用的是"rewriting rule" == "member scope rsolution rule" 规则来处理它.
 以前的编译器中, float X() const { return x; }, 它不知道要返回哪一个x, 这里它会返回全局的 extern float x, 所以是不正确的. 后来的编译器是会在整个class的声明都出现了后才会分析member functions, 所以它不会现错.

 对于下面的例子还是会出错, 因为对于member functions signatures的分析不会到类完成以后, 而是第一次出现的时候就会分析的. 如下面的:

  所以最好始终的把"nested type declare" 放在类的起始处. (这在STL中好像最明显, 都是先声明的)


3.2 Data Member的布局 (Data Member Layout)

示例代码: 

 1 class Point3d { 
 2  public
 3 
 4     //  
 5 
 6  private
 7     float x; 
 8     static List<Point3d*> *freeList; 
 9     float y; 
10     static const int chunkSize = 250
11     float z; 
12  }; 
13 

      Data Member的布局按如下的规则:
      i.   Nonstatic data member 在class object中的排列顺序和被声明的顺序是一样的, 任何中间介入的static data member都不会被放进对象的布局中.
      ii.  要求在同一access section中"较晚出现的members在class object中有较高的地址"这一条件就可以.
      iii. 编译器可能会合成一些内部使用的data members, 以支持整个对象模型, 如vptr指针.  对于它的具体位置, C++ Standard 没有规定, 由编译器产商自己决定. 不过传统上一般是放在所有声明的members的最后, 也有把vptr放在所有class object的最前端的.


3.3 Data Member的存取
示例:
      Point3d origin, *pt = &origin;
      origin.x    = 0.0;
      pt->x = 0.0

1. Static Data Members的存取
    每一个static data member只有一个实体,存在于程序的data segment中。每次程序取用这个static data member的时候,就会被转化为对该实体的唯一的extern实体的直接参考操作.  用指针存取一个数和用对象去存取一个数是一样的。
    
2. Nostatic Data Members的存取
    Nostatic data member 直接存放在每一个class object之中,除非经由明确的或暗喻的class object,否则没有办法直接的存取它们。
    例如:
    Point3d  Point3d::translate( const Point3d &pt )
    {
    x += pt.x;
    y += pt.y;
    z += pt.z;
    }
    实际经过转换后为:
    Point3d  Point3d::translate( Point3d *const this, const Point3d &pt )
    {
     this->x += pt.x;
    this->y += pt.y;
    this->z += pt.z;
    }
    对nostatic data member的访问是这样的: 
 origin._y = 0.0; 
    实际转换操作是:
        &origin + (&Point3d::_y - 1 );
      
       注意:  这里的-1操作。指向data member的指针,其offset值总是被加上1, 这样可以使编译系统区分出:
 i.  一个用以指出class的第一个member的data member的指针.
 ii. 一个没有指出任何member的data member的指针.
  
    如果是virtual 继承的话,就可以不一样了,可能要多加层的访问层; 也可能要到运行时才能决定,由编译器所决定.


3.4 “继承”与Data Member
 示例数据:

 1  // supporting abstract data types 
 2  class Point2d
 3  { 
 4  public
 5   // constructor(s) 
 6   // operations 
 7   // access functions 
 8  private
 9   float x, y; 
10  }; 
11 
12  /// 
13  class Point3d
14  { 
15  public
16   // constructor(s) 
17   // operations 
18   // access functions 
19  private
20   float x, y, z; 
21  }; 
22 

    C++的继承模型:
 在C++的继承模型中, 一个derived class object 所表现出来的东西,是其自己的member加上其base class(es) member的总和。对于数据成员出现的顺序在C++ Standard 中没有规定。从下面几个方面来讨论数据继承:
 i.   单一继承且不含有virtual functions
 ii.   单一继承并含有virtual functions
 iii.  多重继承
 iV. 虚拟继承

1. 只要继承不要多态(Inheritance Without Polymoophism)
    继承一般不会增加空间或存取时间。但继承有时会有这样两种情况出现:
    i.   经验不足的人有时可能会重复的设计一些相同的函数.
    ii.  把一个类分解为多层,有可能会为了表现class的体系抽象化,使所需要的空间膨胀。
        因为C++语言要保证: 出现在derived class 中的base class subobject 有其完整原样性。

2. 加上多态(Adding Polymorphism)
    如:

 1  class Point2d 
 2  { 
 3  public
 4     Point2d( float x = 0.0float y = 0.0 ) 
 5        : _x( x ), _y( y ) {}; 
 6 
 7     // access functions for x & y same as above 
 8     // invariant across type: not made virtual 
 9   
10     // add placeholders for z ?do nothing  
11     virtual float z(){ return 0.0 }; 
12     virtual void z( float ) {} 
13 
14     // turn type explicit operations virtual 
15     virtual void operator+=const Point2d& rhs )
16     { 
17         _x += rhs.x(); _y += rhs.y();
18      } 
19   
20     //  more members 
21 
22  protected
23   float _x, _y; 
24  }; 
25 
26 


 //
 要支持多态,Point2d数据成员要做如下的工作:
 i.   导入一个和Point2d有关的virtual table(vtbl), 存放它声明的每一个virtual function的地址
 ii.   在每个class object中导入一个vptr, 提供执行期的链接,使每个object都能找到相应的virtual table.
 iii.  加强construtor, 使它能够为vptr设定初值,让它指向class所对应的virtual table.
 iV.  加强destructor, 使它能够抹消"指向class的相关"virtual table" 的vptr.

 Figure 3.3. Data Layout: Single Inheritance with Virtual Inheritance
 

3. 多重继承(Multiple Inheritance)
 
 
4. 虚拟继承(Virtual Inheritance)
    Class 中如果含一个或多个virtual base class subobjects, 它将被分为两个部分: 一个不变的局部和一个共享的局部.
    i.   不变的局部中的数据,不管后继如何衍化,总有固定的offset, 所这一部分的数据可以直接的被存取。
    ii.  共享的局部,所表现的就是virtual base class subobject, 这一部分的数据会因为每次派生的操作而有变化, 所以它们只能间接的存取。


3.5 对象成员的效率(Object Member Efficiency)

3.6 指向数据成员的指针(Point to Data Members)

posted on 2006-10-30 14:36 猩猩 阅读(181) 评论(0)  编辑 收藏 引用 所属分类: C&C++语言


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理