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

***C++对象模型***
在C++中,有两种class data members:static和nonstatic,以及三种class member functions:static、nonstatic和virtual。已知下面这个class Point声明:

class  Point  {
public:
Point(
float xval);
virtual ~Point();

float x() const;
static int PointCount();

protected:
virtual ostream& print(ostream &os) const;

float _x;
static int _point_count;
}
;

在Stroustrup当初设计的C++对象模型中,Nonstatic data members被配置于每一个class object之内,static data members则被存放在所有的class object之外。Static和nonstatic function members也被放在所有的class object之外,Virtual functions则以两个步骤支持之:
(1)每一个class产生出一堆指向virtual functions的指针,放在表格之中,这个表格被称为virtual table(vtbl)。
(2)每一个class object被添加了一个指针,指向相关的virtual table。通常这个指针被称为vptr。vptr的设定(setting)和重置(resetting)都由每一个class的constructor、destructor和copy assignment运算符自动完成。每一个class所关联的type_info object(用以支持runtime type identification,RTTI)也经由virtual table被指出来,通常是放在表格的第一个slot处。

 

***虚拟继承***
继承关系也可以指定为虚拟(virtual,也就是共享的意思):

class  istream :  virtual   public  ios  { ... } ;
class  ostream :  virtual   public  ios  { ... } ;
class  iostream :  public  istream, public  ostream  { ... } ;

在虚拟继承的情况下,base class不管在继承串链中被派生(derived)多少次,永远只会存在一个实体(称为subobject)。例如iostream之中就只有virtual ios base class的一个实体。

 

***指针的类型***
下面有一个ZooAnimal声明:

class  ZooAnimal  {
public:
ZooAnimal();
virtual ~ZooAnimal();

// ...
virtual void rotate();

protected:
int loc;
String name;
}
;

一个指向类的指针与一个指向整数的指针或一个指向template Array的指针之间的差异既不在其指针表示法不同,也不在于其内容(代表一个地址)不同,而是在其所寻址出来的object类型不同。也就是说,“指针类型”会教导编译器如何解释某个特定地址中的内存内容及其大小:
1.一个指向地址1000的整数指针,在32位机器上,将涵盖地址空间1000~1003。
2.如果String是传统的8-bytes(包括一个4-bytes的字符指针和一个用来表示字符串长度的整数),再加上指向vtbl的指针vptr,那么一个ZooAnimal指针将横跨地址空间1000~1015(4+8+4)。

 

假设Bear继承于ZooAnimal,如下所示:

Bear b;
ZooAnimal 
* pz  =   & b;
Bear 
* pb  =   & b;

一个Bear指针和一个ZooAnimal指针虽然都指向Bear Object的第一个byte,但pb所涵盖的地址包含整个Bear Object,而pz所涵盖的地址只包括Bear Object中的ZooAnimal subobject。

除了ZooAnimal subobject中出现的members,你不能够使用pz来直接处理Bear的任何members。唯一例外是通过virtual机制。

//  不合法:cell_block不是ZooAnimal的一个member
//  虽然我们知道pz当前指向一个Bear object
pz -> cell_block;

//  ok: 经过一个明白的downcast操作就没有问题!
((Bear * )pz) -> cell_block;

//  下面这样更好,但它是一个run-time operation(成本较高)
if (Bear *  pb2  =  dynamic_cast < Bear *> (pz))
pb2
-> cell_block;

//  ok: 因为cell_block是Bear的一个member
pb -> cell_block;

 

***切割引起的编译器仲裁***

Bear b;
ZooAnimal za 
=  b;   //  这会引起切割(sliced)
//  调用 ZooAnimal::rotate()
za.rotate();

为什么rotate所调用的是ZooAnimal实体而不是Bear实体?此外,如果初始化函数(应用于上述assignment操作发生时)将一个object内容完整拷贝到另一个object中去,为什么za的vptr不指向Bear的virtual table?
第二个问题的答案是,编译器在(1)初始化及(2)指定(assignment)操作之间做出了仲裁。编译器必须确保如果某个object含有一个或一个以上的vptrs,那些vptrs的内容不会被base class object初始化或改变。
至于第一个问题的答案是:za并不是(而且绝对不会是)一个Bear,它是(并且只能是)一个ZooAnimal。多态所造成的“一个以上的类型”的潜在力量,并不能够实际发挥在“直接存取objects”这件事情上。

 

***面对对象(OO)和基于对象(OB)***
基于对象的数据类型可以展示封装的非多态形式,但是不支持类型的扩充。一个OB设计可能比一个对等的OO设计速度更快而且空间更紧凑。速度快是因为所有的函数引发操作都在编译时期解析完成,对象建构起来时不需要设置virtual机制;空间紧凑则是因为每一个class object不需要负担传统上为了支持virtual机制而需要的额外符合。不过,OB设计比较没有弹性。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值