1.C++在布局以及存取时间上主要的额外负担是由virtual引起,包括:
1>virtual function机制:用以支持一个有效率的“执行期绑定”(runtime binding).
2>virtual base class: 用以实现“多次出现在继承体系中的“base class, 有一个单一而被共享的实体”。
2.在C++中,有两种class data members: static和nonstatic,以及三种class member functions: static、nonstatic和virtual。
3.C++对象模型。在此模型中,Nonstatic data members被置于每一个class object之内,static data members则被存放在所有的class object之外。Static和non static 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 identificatio,RTTI)也经由virtual table被指出来,通常是放在表格的第一个slot处。
class Point
{
public:
Point(flot xval);
virtual ~Point();
float x() const;
static int PointCount();
protected:
virtual ostream& print(ostream& os) const;
float _x;
static int _point_count;
};
4.虚拟继承。
class iostream:
public istream,
public ostream
{...};
class istream: virtual public ios {...};
class ostream: virtual public ios {...};
在虚拟继承的情况下,base class不管在继承串链中被派生(derived)多少次,永远只会存在一个实体(称为subobject)。例如iostream之中就只有virtual ios base class的一个实体。
5.C++中凡处于同一个access section的数据,必定保证以其声明次序出现在内存布局当中。然后被放置在多个access sections中的各笔数据,排列次序就不一定了。
6.C++以下列方法支持多态:
1>经由一组隐含的转化操作。例如把一个derived class指针转化为一个指向public base type的指针:
shape *ps = new circle();
2>经由virtual function机制:
ps->rotate();
3>经由dynamic_cast和typeid运算符:
if ( circle* pc = dynamic_cast<circle*>(ps) )
...
7.需要多少内存才能够表现一个class object? 一般而言要有:
1>其nonstatic data members的总和大小;
2>加上任何由于alignment的需求而填补(padding)上去的空间(可能存在于members之间,也可能存在于集合体边界);
3>加上为了支持virtual而由内部产生的任何额外负担(overhead)。
8.指针的类型。一个指向ZooAnimal的指针是如何地与一个指向整数的指针或一个指向template Array(如下,与一个String一并产生)的指针有所不同呢?
ZooAnimal* px;
int* pi;
Array<String>* pta;
以内存需求的观点来说,没有什么不同。它们三个都需要有足够的内存来放置一个机器地址(通常是个word)。“指向不同类型之各指针”间的差异,既不在其指针表示法不同,也不在其内容(代表一个地址)不同,而是在其所寻址出来的object类型不同。也就是说,“指针类型”会教导编译器如何解释某个特定地址中的内存内容及其大小:
1>一个指向地址1000的整数指针,在32位机器上,将涵盖地址空间1000~1003;
2>如果String是传统的8-bytes(包括一个4-bytes的字符指针和一个用来表示字符串长度的整数),那么一个ZooAnimal指针将横跨地址空间1000~1015(4+8+4)
那么,一个指向地址1000而类型为void*的指针,将涵盖怎样的地址空间呢?是的,我们不知道!这就是为什么一个类型为void*的指针只能够含有一个地址,而不能够通过它操作所指之object的缘故。
所以,转型(cast)其实是一种编译器指令。大部分情况下它并不改变一个指针所含的真正的地址,它只影响“被指出之内存的大小和其内容”的解释方式。
9.加上多态之后
class Bear: public ZooAnimal
{
public:
Bear();
~Bear();
// ...
void rotate();
virtual void dance();
// ...
protected:
enum Dances { ... };
Dances dances_known;
int cell_block;
};
Bear b("Yogi");
Bear* pb = &b;
Bear& rb = *pb;
假设Bear object放在地址1000处,一个Bear指针和一个ZooAnimal指针有什么不同?
Bear b;
ZooAnimal* pz = &b;
Bear* pb = &b;
它们每个都指向Bear object的第一个byte,其间的差别是,pb所涵盖的地址包含整个Bear object,而pz所涵盖的地址只包含Bear object中的ZooAnimal subobject。除了ZooAnimal subject中出现过的members,你不能够使用pz来直接处理Bear的任何members,唯一例外是通过virtual机制:
10.总而言之,多态是一种威力强大的设计机制,允许你继一个抽象的public接口之后,封装相关的类型。需要付出的代价就是额外的间接性——不论是在“内存的获得”或是在“类型的决断”上。C++通过class的pointers和references来支持多态,这种程序设计风格就成为“面向对象”。