第1章 关于对象
2012-06-05 11:06:22
C++在布局及存取时间上主要额外负担是由virtual引起,将会在后面章节讨论C++布局问题。
本章一个重点讨论就是C++对象模式,这个内容非常重要,是基础,在后面将会被用到,尤其是涉及打virtual。
第1.1节 C++对象模型
简单对象模型
Members(指data member和function member)按照声明次序,各被指定一个slot。
图1.1说明这种模型
members本身并不放在object中,每一个slot存放的是“指向member的指针”,目的是避免members不同类型,导致占用不同存储空间带来的问题,一个class object大小等于指针大小乘以calss中声明members数目。
注意每一个slot固定大小,大小等于指针所占空间
表格驱动对象模型
区分data member 和function member,class只含有两个指针,一个指向member function table,一个指向data member table。
data member table直接含有数据,member function table则是一系列slot,每一个slot指出一个member function。
虽然这个模型也没有真正应用到C++编译器身上,但member function table这个观念却成为支持virtual functions的有效方案
图1.2说明这种模型
C++对象模型
1.静态数据成员和非虚函数成员放在class object体外
2.nonstatic data member放在每一个class object体内
3.虚函数采用表格驱动对象模型中对待function member的策略:
第一,每一个class产生出一堆指向虚函数指针,放在表格中,这个表格被称为virtual table(vtbl)
第二,每一个class object被添加了一个指针,指向相关的virtual table。通常这个指针被称为vptr。每一个class所关联的type_info object(用来支持runtime type identification,RTTI)也经由virtual table被指出来,通常放在表格slot处。
图1.3说明这种模型
问题:实际上对于type_info object作用还不是很清楚
加上继承
在虚继承情况下,base class不管在继承串链中被派生多少次,永远只会存在一个实体(称为subobject)。
base table模型
每一个class object内含有一个bptr,指向其base class table,base class table每一个slot内涵一个相关的base class地址,和virtual table内含每一个virtual function地址一样。
图1.4说明这种模型
优点:每一个class object对于继承都有一致的表现方式:第一,每一个class object都应该在某个固定位置上安放一个base table指针,与base classes大小和数量无关;第二,不需要改变class object本身,就可以放大,缩小,或更改base classtable。
缺点:间接性导致的空间和存取时间上的额外负担,这种间接性会随着继承的深度而增加。
个人观点:把members放在类体内减少了间接寻址次数,带来了时间和空间上的效率,同时也容易受到base class改变的影响,只能在二者之间寻求平衡
第1.2节 关键词带来的差异
如果不是为了努力维护与C之间的兼容性。C++远可以比现在更简单些。
核心问题:什么时候一个人应该在C++程序中以struct取代class?
从面向对象角度来说,不应该进行替换,struct更多意义上来说是C语言的遗留物,表达更多是一个数据集合体;而class则是包含数据和数据相应操作的抽象数据类型(ADT)。
之所以struct存在就是为了支持现存的C程序代码,因此struct更多可能是C++和C语言进行兼容的桥梁之一。
另一方面,class引入真的需要吗?从理性角度来说,可以不必引入,因为class能做的struct也能做,为什么保留则是因为它所支持的封装和继承的哲学。
C程序巧计有时候却成为C++程序的陷阱。比如C程序中的变长结构体,变长结构体要求把变长数组放在结尾处,但是由于C++中继承和虚函数引入,无法保证该变长数组放置在结尾,从而这种做法在C++中是无法使用的。
C struct在C++中一个合理用途,是当你要传递“一个复杂的class object的全部或部分”到某个C函数中去时,struct声明可以把数据封装起来,并保证拥有与C兼容的空间布局。(还是C++和C兼容问题)
第1.3节 对象的差异
多态只存在于一个个的public class体系中。
class Book:public Library_materials{...}; Library_materials thing1; Book book; thing1=book;//sliced thing1.chek_in(); Library_materials &thing2=book; thing2.check_in();
thing1是在编译阶段确定,thing2则是在运行阶段确定,这就是二者对象之间的差异。
class object大小包含3个部分:
1.nonstatic data members总和大小;
2.由于字节对齐要求填补空间;
3.为了支持virtual内部产生(编译器)的任何额外负担。
void* ptr指向一个地址,但是不知道它所含有空间大小,因此不能通过它操作所指object。
ZooAnimal za; ZooAnimal *pza; Bear b; Panda *pp=new Panda; pza=&b;
继承关系如下图
可能内存布局图如下
一个pointer或一个reference之所以支持多态,是因为并不引发内存中任何“与类型有关的内存委托操作”;会受到改变的只是他们所指向的内存的“大小和内容解释方式”而已。
C++也支持具体的ADT程序风格,被称为OB(object-based)最主要差别是非多态,,所有的函数引发的操作都在编译时期解析完成,不需要virtual机制,有更好的空间紧凑性,速度更快,没有弹性是它的缺点。
设计要在弹性(OO)和效率(OB)之间进行选择