《深度摸索C++工具模子》念书条记(1)
副标题#e#
在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;
};
#p#副标题#e#
一个指向类的指针与 一个指向整数的指针或一个指向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)***
#p#分页标题#e#
基于工具的数据范例可以展示封装的非多态形式,可是不支 持范例的扩充。一个OB设计大概比一个对等的OO设计速度更快并且空间更紧凑。速度快是因为所有的函 数激发操纵都在编译时期理会完成,工具建构起来时不需要配置virtual机制;空间紧凑则是因为每一个 class object不需要承担传统上为了支持virtual机制而需要的特别切合。不外,OB设计较量没有弹性。