在class中,一个non-inline member function只会诞生一个函数实体,而inline function会在每一个使用者(模块)身上产生一个函数实体。
个人理解:inline函数在每一个使用了这个函数的地方都会进行代码替换,所以会产生多个函数实体,而成员函数只会有一个实体,并且多个该class的对象公用相同的函数(即只含有该函数的入口地址)。
virtual function 机制: 用以支持一个有效率的“执行期绑定”(virtual binding)
virtual base class 多次出现在集成体系中的base class,有一个单一而被共享的实体。
简单对象模型(Simple Object Model)
members本身不放在object中,只有“指向该member的指针”才放在object内,可以避免member有不同的类型,因而需要不同的存储空间所导致的问题。
表格驱动模型
目的:使所有该类的对象有一致的表达方式,该对象模型将数据成员和函数成员分成两个表,class object中则只有两个指向两个表格的指针。
【注】该member function table成为支持virtual function的一种方案。
C++对象模型(The C++ Object Model)
特点:
- non-static data members被配置于每一个class object中,也就是说每一个对象都拥有它独一无二的变量成员。
- static data members则被存放在所有的class object 外。
- static和nonstatic function members也被放在所有的class object之外
- virtual functions则以两个步骤来支持它。
①每一个class有一个只想virtual functions的指针,放在表格之中,这个表格称为 virtual table(vtbl)
②每一个class object都添加了一个指针,只想相关的virtual table,通常这个指针被称为vtpr,vtpr的设定和重置都用该class的constructor,destructor和copy assignment运算符自动完成,每一个class关联的对象信息也由virtual table指出
缺点:如果应用程序代码本省未曾更改,但是用到的那些nonstatic data member修改,也要重新编译这些代码。
关于继承的模型(多重继承)
可以采用base table,将所有的父类地址全都放在一个表中,该子类中增加一个指针指向该表即可。
优点:每一个class object对继承都有一致性,不用改变class object本身就可以放大,缩小或者更改base class table
C struct在C++中一个合法用途:是当你要传递一个复杂的class object的全部或者部分到某个C含糊中时,struct声明可以将数据封装起来,并且保证与C兼容的空间布局,然而这项保证只在组合(composition,这里我尚不清楚什么意思)的情况下而非集成的情况下,否则编译器会决定是否应该有额外的数据成员被安插到base struct subobject中去。
C++程序设计模型直接支持的三种程序设计典范
1.程序模型
2.抽象数据类型
3.面向对象模型
在OO paradigm中,程序员需要处理一个未知实体,类型有无限的可能,在真正到达特定的执行期之前,是无法真正解析出它的类型的,在C++中只有通过pointer和reference的操作才能完成。
相反的,在ADT paradigm中,程序员处理的是一个拥有固定而单一类型的实体,在编译期就已经完全定义好的。
C++中,多态只存在于一个个的public class体系中,Nonpublic的派生行为以及类型为void*的指针可以说是堕胎,但是并不被语言明白支持,也就是说它们不许由程序员通过明白的转型才做来管理
C++以下列方式支持多态:
①把一个派生类的指针类型转化成一个公有继承的鸡肋指针;
father* ptr=new son();
②由虚函数机制
father->eat()//实际使用的是儿子的吃饭动作
③由dynamic_cast和typeid运算符
if(son*sptr=dynamic_cast<son*>(fptr))......
virtual机制在执行期根据object的真正类型解析出到底哪一个函数实体被调用。
表示一个类对象需要多少内存?
①nonstatic data members的总和大小
②加上内存对齐所增加的空间(使bus的传输率达到最高效率,该空间可能位于成员之间,也可能在集合体边界)
③加上为了支持virtual而在内部产生的负担(overhead)
指针的类型
ZooAnimal *a;
int*b;
Array<String>*pta;
三者有什么不同?
以内存的观念来说,没有什么不同,它们都需要足够的内存来存放一个机器地址,指针之间的差异,不在于其表示的形式不同或者内容(地址)不同,而在于它所寻址出来的object类型不同,也就是说,“指针类型”会导致编译器如何解释某个特定地址中的内存中内容以及其大小。
而void*指针,只含有一个地址,而不能知道该地址中object的类型。
由上可知,cast(转型)其实是一种编译器指令,大部分情况下它不会改变一个指针所含的真正地址(那么什么时候会改变呢?不清楚),它只影响“被指内存中内容和内存大小”的解释方式。
一个Animal类的指针和一个Bear类的指针有什么不同?
它们都只想动物类的第一个地址,差别在于覆盖范围不同。pbear指针覆盖了整个空间1000-1016,而pzoo指针只覆盖了1000-1008,不能用pzoo来处理pbear中的任何成员(除了虚函数成员);
总结:当一个基类对象被直接初始化为一个派生类对象时,派生类就会被切割,以塞入较小的基类对象中,多态将不再呈现,而一个严格的编译器可以在编译时期解析一个“通过该object出发的virutal function调用操作”(这里不理解?)