1、 Data Member 的布局

  • 同一个Access Section(private, public等)中,data member的顺序按照声明顺序排列,但是没有规定需要连续排序。同时编译器可能会安插一些内部的data member(比如vptr),用来支持整个对象模型。
  • 不同Access Section中,member的排列顺序由编译器决定。

2、Data Member 的存取

  每一个member 的存取许可(private public protected),以及与class的关联,并不会导致任何空间上或执行时间上的额外负担——不论是在个别的class objects 或是在static data member 本身。

class X {};    //空虚基类          sizeof(X) = 1
class A: public virtual X {}; //sizeof(A) = 8
class B: public virtual X {}; //sizeof(B) = 8
class C: public A, public B {}; //sizeof(C) = 12

  -X是空基类,需要安插一个char,使得class的两个objects在内存上拥有唯一的地址; 
  -Size(A) = 4(为了支持virtual base class而额外增加的指针) + 1(base class本身) + 3(Alignment对齐) = 8 
  -Size(C) = 4(A) + 4(B) + 1(X,虚拟继承被A/B所共享) + 3(对齐) = 12

  static data members

  直接存放于Data Segment,拥有唯一实体,不存在于class object中;
  如果两个class声明了同名static members,编译器会对class中static data member名字进行修饰,使其独一无二;
  对static data member取址,得到该member数据类型指针;nonstatic data member取址将得到该member在类中偏移。

nonstatic data members
  欲对一个nonstatic data member 进行存取操作,编译器需要吧class object的起始地址加上data member的偏移量(在编译事情就可以获知)。

class A {public: int x; int y;};
A a;
a.y = ; //&a.y = &a + &A::y

3、继承与Data Member

  3.1 只要继承不要多态

  base class subobject会在derived class中保持原样。 这种情况并不会增加空间或存储时间上的额外负担。这种情况base class和derived class的objects都是从相同的地址开始,其差异只在于derived object 比较大,用以容纳自建的nonstatic data members,把一个derived class object指定给base class 的指针或引用,并不需要编译器去调停或修改地址,它很滋润的可以发生,而且提供了最佳执行效率。

  3.2 加上多态

  这种情况会带来空间和存取时间的额外负担:

  1.导入一个virtual table ,用来存储它所声明的每一个virtual functions的地址。

  2.在每一个class object中导入一个vptr,提供执行期的链接,使每一个object能够找到相应的virtual table。

  3.加强constructor,使它能够为vptr设定初始值,让它指向class 所对应的virtual table 。

  4.加强destructor,使它能够消抹“指向class 相关virtual table”的vptr。

  3.3 多重继承

  对于一个多重派生对象,将其地址指定给“最左端(第一个)base class的指针”,情况和单一继承时相同,因为二者都指向了相同的起始地址,至于第二个或后面的base class 的地址指定操作,则需要将地址修改过:加上(或减去,如果是downcast)介于中间的base class subobject(s)的大小。
  如果要存取第二个(或后面)的base class 中的一个data member ,不需要付出额外的成本,因为members的位置在编译时就固定了,因此存取member只是一个简单的offset的运算。
  注意,多继承的情况下,drived clas可能会有两个或两个以上虚函数表指针 。

  

  

  我们可以看到:

  1. 每个父类都有自己的虚表。
  2. 子类的成员函数被放到了第一个父类的表中。
  3. 内存布局中,其父类布局依次按声明顺序排列。
  4. 每个父类的虚表中的f()函数都被overwrite成了子类的f()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

  3.4 虚拟继承

  下图可以表现Vertex3d 的继承体系图。左为多重继承,右为虚拟多重继承。

  

  各个class的定义如下:

class Point2d{
...
protect:
float _x, _y;
}; class Vertex: public virtual Point2d{
...
protected:
Vertex *next;
}; class Point3d: public virtual Point2d{
...
protected:
float _z;
}; class Vertex3d: public Vertex, public Point3d{
...
protected:
float mumble;
};

  不论是 Vertex 还是 Point3d 都内含一个 Point2d 。然而在 Vertex3d 的对象布局中,我们只需要单一一份 Point2d 就好。如何使多重继承,那么Vertex3d对象中将有两个Point2d,那么对Point2d的引用可能会有歧义。所以引入虚拟继承。然而编译器要实现虚拟继承,实在是困难度颇高。虚拟继承的原则就是:让 Vertex 和 Point3d 各自维护的Point2d 折叠成一个有Vertex3d维护的单一Point2d,并且还可以保存base class 和derived class的指针之间的多台指定操作。

  如果一个class含有virtual base class subobjects, 那么,该对象将被分割为两部分:一个不变局部和一个共享局部。不变局部中的数据,不管后继如何演化,总是拥有固定的offset,所以这部分数据可以直接存取。至于共享局部(即virtual base class),这一部分的数据,其位置会因为每次的派生操作而有变化,所以他们只能被间接存取

  如何存取class的共享局部呢?cfront编译器会在每一个derived class中安插一个指向virtual base class的指针,这样就可以间接存取。这样的实现模型会有下面两个主要缺点:

  1.每一个对象必须针对其每一个virtual base class 背负一个额外的指针。

  解决方法有:第一个,Microsoft编译器引入所谓的virtual base class table。每一个class object如果有一个或多个virtual base class,就会由编译器安插一个指针,指向virtual base class table。至于真正的virtual base class 指针,当然是被放在该表格中。

  请看下面的虚拟继承对象模型,如图。

  

  红框内即所谓的“共享局部”,其位置会因每次派生操作而有所变化。 虚拟破坏了base class 的对象完整型,虚拟继承会在自己类中生成一个虚函数表指针。在virtual function table 中放置virtual base class的offset(不是地址)。

  

  这个方法的好处是,巧妙的利用了虚函数表的结构,使得drived class 能够节省一个指针的大小。上图中蓝色曲线是offset。

  2.由于虚拟继承串链的加长,导致间接存取层次的增加。

  例如:如果我们有三层虚拟衍化,我就需要三次间接存取(经由三个virtual base class指针)。

  这个问题的解决方案有:拷贝所有的virtual base class 的指针到drived class中。这样就解决了存取时间的问题,虽然会有空间的开销。

  总结:多继承,单继承都不会导致访问时间的增加,但是虚拟基类由于使用间接访问技术,会导致访问时间的增加。

【C++对象模型】第三章 Data语义学的更多相关文章

  1. 《驾驭Core Data》 第三章 数据建模

    本文由海水的味道编译整理,请勿转载,请勿用于商业用途.    当前版本号:0.1.2 第三章数据建模 Core Data栈配置好之后,接下来的工作就是设计对象图,在Core Data框架中,对象图被表 ...

  2. C++对象模型(五):The Semantics of Data Data语义学

    本文是<Inside the C++ Object Model>第三章的读书笔记.主要讨论C++ data member的内存布局.这里的data member 包含了class有虚函数时 ...

  3. Data 语义学(1)

    一.Data Member 的绑定(The binding of Data Member) extern float x; class Point3d { public: Point3d( float ...

  4. 读高性能JavaScript编程 第三章

    第三章  DOM Scripting  最小化 DOM 访问,在 JavaScript 端做尽可能多的事情. 在反复访问的地方使用局部变量存放 DOM 引用. 小心地处理 HTML 集合,因为他们表现 ...

  5. 《Django By Example》第三章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:第三章滚烫出炉,大家请不要吐槽文中 ...

  6. Python黑帽编程3.0 第三章 网络接口层攻击基础知识

    3.0 第三章 网络接口层攻击基础知识 首先还是要提醒各位同学,在学习本章之前,请认真的学习TCP/IP体系结构的相关知识,本系列教程在这方面只会浅尝辄止. 本节简单概述下OSI七层模型和TCP/IP ...

  7. 《Entity Framework 6 Recipes》中文翻译系列 (11) -----第三章 查询之异步查询

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第三章 查询 前一章,我们展示了常见数据库场景的建模方式,本章将向你展示如何查询实体 ...

  8. 《Linux内核设计与实现》课本第三章自学笔记——20135203齐岳

    <Linux内核设计与实现>课本第三章自学笔记 进程管理 By20135203齐岳 进程 进程:处于执行期的程序.包括代码段和打开的文件.挂起的信号.内核内部数据.处理器状态一个或多个具有 ...

  9. KnockoutJS 3.X API 第三章 计算监控属性(5) 参考手册

    计算监控属性构造参考 计算监控属性可使用以下形式进行构造: ko.computed( evaluator [, targetObject, options] ) - 这种形式是创建一个计算监控属性最常 ...

随机推荐

  1. 2017-2018-2 20172323 『Java程序设计』课程 结对编程练习_四则运算

    结对编程的好丽友 - 20172323 王禹涵:中缀转后缀 - 20172314 方艺雯:后缀表达式的计算 - 20172305 谭鑫:中缀表达式的输出 需求分析 能随机生成由使用者确定的任意多道四则 ...

  2. android平台蓝牙编程(转)

    http://blog.csdn.net/pwei007/article/details/6015907 Android平台支持蓝牙网络协议栈,实现蓝牙设备之间数据的无线传输. 本文档描述了怎样利用a ...

  3. C# Excel2007 导出生成 2003兼容格式

    //导出2007格式 AppWb.Saved = true; //保存工作薄 AppExcel.ActiveWorkbook.SaveCopyAs(FilePath); //导出2003格式 AppW ...

  4. Jenkins系列-Jenkins升级、迁移和备份

    升级Jenkins Jenkins的开发迭代非常快,每周发布一个开发版本,长期支持版每半年更新一次(ps:大版本更新).如此频繁的更新,怎么升级呢? war:下载新版的war文件,替换旧版本war文件 ...

  5. HSF原理

    HSF(High-speed Service Framework),高速服务框架,是阿里系主要采用的服务框架,其目的是作为桥梁联通不同的业务系统,解耦系统之间的实现依赖.其高速体现在底层的非阻塞I/O ...

  6. Mybatis笔记二

    一对一查询 案例:查询所有订单信息,订单信息中显示下单人信息. 注意:因为一个订单信息只会是一个人下的订单,所以从查询订单信息出发关联查询用户信息为一对一查询.如果从用户信息出发查询用户下的订单信息则 ...

  7. AngularJS注入依赖路由总结

    属性 描述 $dirty  表单有填写记录 $valid 字段内容是合法的 $invalid 字段内容是非法的 $pristine 表单没有填写记录 什么事依赖注入? 依赖注入是一种软件设计模式,在这 ...

  8. hdu 2768 Cat vs. Dog (二分匹配)

    Cat vs. Dog Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  9. C# 面向对象——多态

    多态分三种:1.虚方法 2.抽象类 3.接口 1.虚方法1.将父类的方法标记为虚方法 ,使用关键字 virtual,这个函数可以被子类重新写一个遍. 如: class Program { static ...

  10. html的body内标签之超链接

    一,先来个简单的练练手,target="_blank" 的作用是在新的tab中打开页面.href 是超链接的意思. <!DOCTYPE html> <html l ...