《深度探索C++对象模型》读书笔记(3)

《深度探索C++对象模型》读书笔记(3)
2010年12月20日
  巨盾补补,今天你“补”了吗? 巨盾补补:是一款针对个人用户而设计的绿色漏洞补丁软件,集绿色、小巧、全面、精确于一身,是您升级系统漏洞的不二选择。 1、绿色、小巧:下载完双击即可运行,使用完即可完全删除。 2、全面:完全支持Windows XP和Windows 7 系统。 3、精确,采用的是和Windows Update相同的判定机制,必须补丁一个不漏。 4、迅捷,连接到微软官方服务器进行下载,保证下载速度及安全性。 官方下载地址:http://update.ggsafe.com/hotfix/GGBuBu.zip 官方反馈地址:http://bbs.ggsafe.com/thread-36769-1-1.html
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  在visual C++ 6.0中测试如下代码: #include "iostream"
  using namespace std;
  class X {};
  class Y : public virtual X {};
  class Z : public virtual X {};
  class A : public Y,public Z {};
  int main()
  {
  coutchunkSize = 250;
  Point3d::chunkSize = 250; (b)若取一个static data member的地址,会得到一个指向其数据类型的指针,而不是一个指向其class member的指针,因为static member并不内含在一个class object之中。
  &Point3d::chunkSize会获得类型如下的内存地址:const int*
  (c)如果有两个classes,每一个都声明了一个static member freeList,那么编译器会采用name-mangling对每一个static data member编码,以获得一个独一无二的程序识别代码。
  (2)Nonstatic Data Members以两种方法存取x坐标,像这样: origin.x = 0.0;
  pt->x = 0.0; "从origin存取"和"从pt存取"有什么重大的差异吗?
  答案是"当Point3d是一个derived class,而在其继承结构中有一个virtual base class,并且并存取的member(如本例的x)是一个从该virtual base class继承而来的member时,就会有重大的差异"。这时候我们不能够说pt必然指向哪一种 class type(因此我们也就不知道编译期间这个member真正的offset位置),所以这个存取操作必须延迟到执行期,经由一个额外的简洁导引,才能够解决。但如果使用origin,就不会有这些问题,其类型无疑是Point3d class,而即使它继承自virtual base class,members的offset位置也在编译时期就固定了。
  ***继承与Data Member***
  (1)只要继承不要多态(Inheritance without Polymorphism)
  让我们从一个具体的class开始: class Concrete{
  public:
  // ...
  private:
  int val;
  char c1;
  char c2;
  char c3;
  }; 每一个Concrete class object的大小都是8 bytes,细分如下:(a)val占用4 bytes;(b)c1、c2、c3各占用1 byte;(c)alignment需要1 byte.
  现在假设,经过某些分析之后,我们决定采用一个更逻辑的表达方式,把Concrete分裂为三层结构: class Concrete {
  private:
  int val;
  char bit1;
  };
  class Concrete2 : public Concrete1 {
  private:
  char bit2;
  };
  class Concrete3 : public Concrete2 {
  private:
  char bit3;
  }; 现在Concrete3 object的大小为16 bytes,细分如下:(a)Concrete1内含两个members:val和bit1,加起来是5 bytes,再填补3 bytes,故一个Concrete1 object实际用掉8 bytes;(b)需要注意的是,Concrete2的bit2实际上是被放在填补空间之后的,于是一个Concrete2 object的大小变成12 bytes;(c)依次类推,一个Concrete3 object的大小为16 bytes.
  为什么不采用那样的布局(int占用4 bytes,bit1、bit2、bit3各占用1 byte,填补1 byte)?
  下面举一个简单的例子: Concrete2 *pc2;
  Concrete1 *pc1_1, *pc1_2;
  pc1_1 = pc2; // 令pc1_1指向Concrete2对象
  // derived class subobject被覆盖掉
  // 于是其bit2 member现在有了一个并非预期的数值
  *pc1_2 = *pc1_1; pc1_1实际指向一个Concrete2 object,而复制内容限定在其Concrete subobject,如果将derived class members和Concrete1 subobject捆绑在一起,去除填补空间,上述语意就无法保留了。在pc1_1将其Concrete1 subobject的内容复制给pc1_2时,同时将其bit2的值也复制给了pc1_1. (2)加上多态(Adding Polymorphism)
  为了以多态的方式处理2d或3d坐标点,我们需要在继承关系中提供virtual function接口。改动过的class声明如下: class Point2d {
  public:
  Point2d(float x = 0.0, float y = 0.0) : _x(x),_y(y) {};
  virtual float z() ...{ return 0.0; } // 2d坐标点的z为0.0是合理的
  virtual void operator+=(const Point2d& rhs) {
  _x += rhs.x();
  _y += rhs.y();
  }
  protected:
  float _x,_y;
  }; virtual function给Point2d带来的额外负担:
  (a)导入一个和Point2d有关的virtual table,用来存放它声明的每一个virtual function的地址;
  (b)在每一个class object中导入一个vptr;(c)加强constructor和destructor,使它们能设置和抹消vptr. class Point3d : public Point2d {
  public:
  Point3d(float x = 0.0, float y = 0.0,float z = 0.0) : Point2d(x,y),_z(z) {};
  float z() { return _z; }
  void z(float newZ) { _z = newZ; }
  void operator+=(const Point2d& rhs) { //注意参数是Point2d&,而非Point3d&
  Point2d::operator+=(rhs);
  _z += rhs.z();
  }
  protected:
  float _z;
  }; 自此,你就可以把operator+=运用到一个Point3d对象和一个Point2d对象身上了。
  (3)多重继承(Multiple Inheritance)
  请看以下的多重继承关系: class Point2d {
  public:
  // ... // 拥有virtual接口
  protected:
  float _x,_y;
  };
  class Point3d : public Point2d {
  public:
  // ...
  protected:
  float _z;
  };
  class Vertex {
  public:
  // ... // 拥有virtual接口
  protected:
  Vertex *next;
  };
  class Vertex3d : public Point3d,public Vertex {
  public:
  // ...
  protected:
  float mumble;
  }; 对一个多重继承对象,将其地址指定给"第一个base class的指针",情况将和单一继承时相同,因为二者都指向相同的起始地址,需付出的成本只有地址的指定操作而已。至于第二个或后继的base class的地址指定操作,则需要将地址修改过,加上(或减去,如果downcast的话)介于中间的base class subobjects的大小。 Vertex3d v3d;
  Vertex3d *pv3d;
  Vertex *pv;
  pv = &v3d;
  // 上一行需要内部转化为
  pv = (Vertex*)((char*)&v3d) + sizeof(Point3d));
  pv = pv3d;
  // 上一行需要内部转化为
  pv = pv3d ? (Vertex*)((char*)pv3d) + sizeof(Point3d)) : 0; // 防止可能的0值 (4)虚拟继承(Virtual Inheritance)
  class如果内含一个或多个virtual base class subobject,将被分隔为两部分:一个不变局部和一个共享局部。不变局部中的数据,不管后继如何衍化,总是拥有固定的offset,所以这一部分数据可以被直接存取。至于共享局部,所表现的就是virtual base class subobject.这一部分的数据,其位置会因为每次的派生操作而变化,所以它们只可以被间接存取。
  以下均以下述程序片段为例: void Point3d::operator+=(const Point3d& rhs)
  {
  _x += rhs._x;
  _y += rhs._y;
  _z += rhs._z;
  } 间接存取主要有以下三种主流策略:
  (a)在每一个derived class object中安插一些指针,每个指针指向一个virtual base class.要存取继承得来的virtual base class members,可以使用相关指针间接完成。
  由于虚拟继承串链得加长,导致间接存取层次的增加。然而理想上我们希望有固定的存取时间,不因为虚拟衍化的深度而改变。具体的做法是经由拷贝操作取得所有的nested virtual base class指针,放到derived class object之中。 // 在该策略下,这个程序片段会被转换为
  void Point3d::operator+=(const Point3d& rhs)
  {
  _vbcPoint2d->_x += rhs._vbcPoint2d->_x;
  _vbcPoint2d->_y += rhs._vbcPoint2d->_y;
  _z += rhs._z;
  } (b)在(a)的基础上,为了解决每一个对象必须针对每一个virtual base class背负一个额外的指针的问题,Micorsoft编译器引入所谓的virtual base class table.
  每一个class object如果有一个或多个virtual base classes,就会由编译器安插一个指针,指向virtual base class table.这样一来,就可以保证class object有固定的负担,不因为其virtual base classes的数目而有所变化。
  (c)在(a)的基础上,同样为了解决(b)中面临的问题,Foundation项目采取的做法是在virtual function table中放置virtual base class的offset.
  新近的Sun编译器采取这样的索引方法,若为正值,就索引到virtual functions,若为负值,则索引到virtual base class offsets. // 在该策略下,这个程序片段会被转换为
  void Point3d::operator+=(const Point3d& rhs)
  {
  (this + _vptr_Point3d[-1])->_x += (&rhs + rhs._vptr_Point3d[-1])->_x;
  (this + _vptr_Point3d[-1])->_y += (&rhs + rhs._vptr_Point3d[-1])->_y;
  _z += rhs._z;
  } 小结:一般而言,virtual base class最有效的一种运用方式就是:一个抽象的virtual base class,没有任何data members.
  ***对象成员的效率***
  如果没有把优化开关打开就很难猜测一个程序的效率表现,因为程序代码潜在性地受到专家所谓的与特定编译器有关的奇行怪癖。由于members被连续储存于derived class object中,并且其offset在编译时期就已知了,故单一继承不会影响效率。对于多重继承,这一点应该也是相同的。虚拟继承的效率令人失望。 ***指向Data Members的指针***
  如果你去取class中某个data member的地址时,得到的都是data member在class object中的实际偏移量加1.为什么要这么做呢?主要是为了区分一个"没有指向任何data member"的指针和一个指向"的第一个data member"的指针。
  考虑这样的例子: float Point3d::*p1 = 0;
  float Point3d::*p2 = &Point3d::x;
  // Point3d::*的意思是:"指向Point3d data member"的指针类型
  if( p1 == p2) {
  cout*dmp;
  }
  void func2(Derived *pd)
  {
  // bmp将成为1
  int Base2::*bmp = &Base2::val2;
  // bmp == 1
  // 但是在Derived中,val2 == 5
  func1(bmp,pd);
  } 也就是说pd->*dmp将存取到Base1::val1,为解决这个问题,当bmp被作为func1()的第一个参数时,它的值必须因介入的Base1 class的大小而调整: // 内部转换,防止bmp == 0
  func1(bmp ? bmp + sizeof(Base1) : 0, pd); 系列文章:
  《深度探索C++对象模型》读书笔记(1)
  《深度探索C++对象模型》读书笔记(2)
  《深度探索C++对象模型》读书笔记(4)
  《深度探索C++对象模型》读书笔记(5)
  《深度探索C++对象模型》读书笔记(6)
  《深度探索C++对象模型》读书笔记(7)
  《深度探索C++对象模型》读书笔记 最后一记
深度探索C++对象模型读书笔记3) 数据成员语意学 转自codepub,只做为本人学习使用,如果侵犯了您的权力,请立即通知我删除,谢谢。 在visual C++ 6.0中测试如下代码:  在visual C++ 6.0中测试如下代码: #include "iostream"using namespace std;class X {};class Y : public virtual X {};class Z : public 阅读详情

相关推荐

深度探索C++对象模型》第三章:Data 语意学

sizeof 内存对齐的一些规则: #pragma pack(n) 预编译指令,可用来设置多少个字节对齐,n的缺省数值是按照编译器自身设置,一般为8,合法的数值分别是1、2、4、8、16,其它的无效。 offset从0开始,每个数据成员开始存放的offset值为min(n, 数据成员大小)的整数倍。 在数据成员完成各自的存放之后,整个类也将进行内存对齐,其大小为min(n, 整个类中最大成员的大...

weixin_34363171的博客 180

深度探索C++对象模型读书笔记3C++中的多态,这跟指针有什么关系?

  1、  C++的运行多态因为每个派生类对象中都包含基类部分,所以可将基类类型的引用或者指针绑定到派生类对象中的基类部分,而对象本身并不会改变,仍为派生类对象。通过该指针或者引用调用虚函数,编译器将生成代码,在运行确定调用哪个函数,被调用的是与动态类型相对应的函数,也就是派生类中定义的虚函数实现。而且,只有通过引用或者指针调用,虚函数才会在运行确定。如果调用的是非虚函数,

宁静致远 1702

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 生成的...

heary29的专栏 404

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

深度探索C++对象模型读书笔记3)。 <br />  在visual C++ 6.0中测试如下代码:<br /> #include "iostream"<br />using namespace std;<br /><br />class X {};<br />class Y : public virtual X {};<br />class Z : public virtual X {};<br />class A : public Y,public Z {};<br /><br />int mai

Tommy(凌飞)的专栏 997

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

对于一个类的成员变量的存取,如果成员是静态变量,那么通过指针(->)和通过对象(.)存取是一样的;

午夜飞行 493

读书笔记深度探索c++对象模型》 (3

第四章:Function语意学4.2 虚拟成员函数a) 单一继承下的virtual function    一个class只会有一个virtual table。每一个table内含其对应的class object的所有active virtual functions的函数实体地址。包括:   这个class自己定义的virtual function,它override了一个可能存在的base class virtual function函数实体。   继承自base class的函数实体。   一个纯虚函数

cesc 785

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

***纯虚拟函数***在设计抽象基类,需要注意以下几点:(1)不要将destructor声明为pure virtual function;如果将destructor声明为pure virtual function,则设计者一定得定义它。因为每一个derived class destructor会被编译器加以扩展,以静态调用得方式调用其“每一个virtual base class”以及“上一层

技术资料库 1556

[读书笔记] - 《深度探索C++对象模型》第3章 Data语意学

一个空的class,如: // sizeof X == 1 class X { }; 事实上并不是空的,它有一个隐晦的1 byte,那是被编译器安插进去的一个char。这使得这个class的两个objects得以在内存中配置独一无二的地址。 1.Data Memeber的绑定 在下面的程序中,length的类型在两个member function signatuures中都解析为global typedef,也就是int。当后续再有length的nested typedef声明出现C++ St

浮生的博客 386

深度探索C++对象模型读书笔记——2.3 程序转化语意学

C++

smallcat20121230的专栏 373

深度探索C++对象模型读书笔记】【第3章】Data语意学

1、例子,用VS 2013编译器 #include<iostream> using namespace std; class X{}; class Y :public virtual X{}; class Z :public virtual X{}; class A :public Y, public Z{}; int main(){ cout << siz...

weixin_30790841的博客 139

3章 Data语意学-《深度探索C++对象模型读书笔记

本章主要讲述了以下内容:Data Member的邦定,Data Member的布局,Data Member的存取,继承与Data Member,对象成员的效率,指向Data Member的指针。Data Member的邦定主要讲述了以下内容:该部分讲述了一个例子,通过例子和说明我们可以得出一个结论。那就是:在一个类一面要把类型定义和变量定义方在最开始的位置,特别是在模板代码中。Data

worldvacuum的专栏 324

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

` C++在布局以及存取间上主要的额外负担是由virtual引起的,包括:虚函数机制和虚基类机制(即用以实现“多次出现在继承体系中的base class,有一个单一而被共享的尸体”) C++对象模式:在C++中有两中成员数据:static和nonstatic以及三种成员函数:static、nonstatic和virtual C++编译器的采用的对象模型: Nonstatic成员对象...

qq_15054345的博客 200

[读书笔记]《深度探索C++对象模型

文章目录前言第一章 关于对象第二章 构造函数语意学构造函数拷贝构造函数初始化列表第三章 Data 语意学第四章 Function 语意学非静态成员函数静态成员函数虚成员函数第五章 构造、析构、拷贝语意学RTTI模板函数的实现逻辑 前言 俗话说得好,好记性不如烂笔头,本文主要是重温了一下经典书籍《深度探索C++对象模型》,然后一些内容摘录,主要记录的是我自己的精简版,建议读者有相关的知识需要还是慢慢去啃一下原书比较好。 第一章 关于对象 首先介绍了 C++ 类的情况, C++类的内存存储方式是:只包含非静态成

Chris_zhangrx的博客 1475

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

深度探索C++对象模型读书笔记<二> 在使用C++,常常会好奇或者抱怨,编译器为我们做了什么事呢? 为什么构造函数没有为我初始化呢?为什么我还要写默认构造函数呢? 2.1 Default Constructor 的构造操作 如果没有声明默认构造函数,编译器会在需要的候帮我们产生出来。 为了避免在多个地方被需要导致重复,则编译器将产生的构造函数声明为inline方式。 class ...

Travelerwz的博客 262

深度探索C++对象模型》笔记目录

读书笔记,特此记录。 😇😇😇 《深度探索C++对象模型》阅读笔记 第一章 关于对象 《深度探索C++对象模型》阅读笔记 第二章 构造函数语意学 《深度探索C++对象模型》阅读笔记 第三章 Data语意学 《深度探索C++对象模型》阅读笔记 第四章 Function语意学 《深度探索C++对象模型》阅读笔记 第五章 构造、解构、拷贝语意学 《深度探索C++对象模型》阅读笔记 第六章 执行期语意学 《深度探索C++对象模型》阅读笔记 第七章 站在对象模型的尖端...

哇咔咔的博客 359
上一篇: 第五章Materials, Lights, and Shading
下一篇: papervision3d学习笔记:卷轴-清明上河图
iteye_14913
博客等级 码龄8年 0粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值