深度探索C++对象模型--读书笔记
深度探索C++对象模型
第一章:关于对象
封装之后的布局成本
-
C++在布局以及存取时间上主要的额外负担是由virtual引起
- 1、VIrtual function机制:用以支持一个有效率的“执行期绑定”(runtime binding)
- 2、virtual base class:用以实现“多次出现在集成体系中的base,有一个单一而被共享的实例”
1、C++对象模式
-
简单对象模式
- 成员都不在对象中:一个对象都有一些列的slots,每个slot指向一个member,member按照声明顺序,各被指定一个slot
-
表格驱动对象模型
- 将members抽象出来,分化成data member table和function member table,类对象中含有分别指向这两个表的指针,member function table中是一些列的slots,每个slot指向一个member function;data member table则直接持有Data本身。
-
C++对象模型
-
Nonstatic data member被配置在每一个class object之内,static data member则放在个别的class object之外
-
static 和 nonstatic function members被放在个别的class object之外
-
virtual function
-
1、每个class产生出一堆指向virtual functions的指针,放在表格中,被称为virtual table(vtbl)
-
2、每个class object被安插一个指针,指向相关的virtual table。通常该指针被称为vptr。vptr的setting和resseting都由每个类的constructor、destructor和copy assignment运算符完成。
- 每个class所关联的type_info object(用以支持runtime type identification,RTTI),也经由virtual table被指出来,通常放在表格的第一个slot
-
-
2、关键词带来的差异
-
关键字带来的困扰
- 使用class或struct关键字是否可以给予“类型的内部声明”以某种承诺。即若struct关键字的使用是实现了C的数据抽象概念;而class关键字实现的C++的ADT(abstract Data Type)观念,这种不一致性就是一种错误的语言语法
-
策略正确的struct
- C Struct在C++中第一个合理用途就是当你传递“一个复杂的class object的全部或部分”到某个C函数去时,struct声明可以将数据封装起来,并保证拥有与C兼容的空间布局(z这种保证旨在组合composition的情况下存在),如果是继承,不是组合,编译器会决定是否应该有额外的data members被安插到base struct object之中
3、对象的差异
-
C++程序设计模型直接支持三种程序设计范式(programming paradigms)
-
程序模型(procedural model)
-
抽象数据类型模型(abstract data type model,ADT)
- 所谓的“抽象”是和一组表达式(public接口)一起定义
-
面向对象模型(object-oriented model)
-
此模型中有一些彼此相关的类型,通过一个抽象的base class(用以提供共同接口)被封装起来
-
支持多态的场景
-
1、经过一组隐式的转换操作,如把派生类指针转换成基类指针
-
2、经由virtual function机制
-
3、经由dynamic_cast和typeid运算符
- if(circle pc = dynamic_cast<circle>(ps)) ....
-
多态的主要用途:仅有一个共同的接口来影响类型的封装,这个接口通常被定义在一个抽象的base class中。
-
-
class object 的大小
-
1、其nonstatic data members的总和大小
-
2、加上任何由于alignment的需求而填补(padding)上去的空间
- alignment就是将数值调整到某数的倍数,在32位计算机上,通常alignment为4bytes(32位),以使bus的“运输量”达到最高效率
-
3、加上为了支持virtual而由内部产生的任何额外负担
-
-
-
第二章:构造函数语意学
1、Default Constructor的构造操作
C++ Annotated Reference Mannual(ARM)
什么时候才会合成一个default constructor?
当编译器需要它的时候。
对于class X,如果没有任何user-declared constructor,那么会有一个default constructor被隐式(implicitly)声明出来,一个被隐式声明出来的default constructor将是一个trivial(浅薄无能的,没啥用的)constructor
-
带有default constructor的member class object
- 在C++不同的编译模块中(不同的编译模块意指不同的文件),编译器如何避免合成多个default constructor?解决办法就是把合成的default constructor、copy constructor、destructor、assignment copy operator都以inline方式完成。一个inline函数有静态连接(static linkage),不会被文件以外者看到
- 被合成的default constructor只是满足编译器的需要,并不满足程序的需要
-
带有default constructor 的base class
- 如果一个没有任何constructors的class派生自一个“带有default constructor”的base class,那么这个derived class的default constructor会被视为nontrivial,并因此需要被合成出来
-
带有一个virtual function的class
-
另外两种情况也需要合成default constructor
- 1、class声明(或继承)一个virtual function
- 2、class派生自一个继承串链,其中有一个或更多的virtual base classes
-
-
带有一个virtual base class 的class
-
小结
- 上述四种情况,会造成“编译器必须为未声明constructor的classes合成一个default constructor”,C++标准将这些合成物称为implicit nontrivial default constructors,被合成出来的constructor只能满足编译器而非程序的需要,
- 它之所以能够完成任务,是借着“调用member object或base class的default constructor”或是“为每一个object初始化其virtual function机制或virtual base class机制”而完成
- 在合成的default constructor中,只有base class subobjects和member class objects会被初始化;所有其他的nonstatic data member(如整数、整数指针、整数数组等等)都不会被初始化
- 误解:1、任何class如果没有定义default constructor,就会被合成一个(需要的时候合成);2、编译器合成出来的default constructor会显示设定“class内每一个data member的默认值”:(只满足编译器的需要)
2、Copy Constructor的构造操作
-
default memberwise initialization
- 当class object以“相同class的另一个object”作为初始值,其内部是以所谓的default memberwise initialization手法完成,即把每一个内建的或派生的data member(如指针或一个数组)的值,从某一个object 拷贝一份到另一个object身上,以递归的方式实现memberwise initialization
- 一个class object可用copy constructor和copy assignment operator两种方式复制得到
-
bitwise copy semantics(位逐次拷贝)
-
不要bitwise copy semantics
-
一个class不展现出“bitwise copy semantics”的四种情况
- 1、当class内含一个member object而后者的class声明中有一个copy constructor时(不管是显示声明还是被编译器合成的)
- 2、当class继承自一个base class而后者存在一个copy constructor
- 3、当class声明了一个或多个virtual function时
- 4、当class派生自一个继承串链,其中有一个或多个virtual base classes时
-
-
重新设定virtual table的指针
-
只要有一个class声明一个或多个virtual function就会有如下操作:
- 1、增加一个virtual function table(vtbl),内含每一个有作用的virtual function的地址
- 2、一个指向virtual function table的指针(vptr),安插在每一个class object内
-
-
处理virtual base class subobject
- 一个class object如果以另一个object作为初值,而后者有一个virtual base class subobject,那么也会使“bitwise copy semantics”失效
- 每一个编译器对于虚拟继承的支持承诺,都代表必须让“derived class object中的virtual base class subobject位置”在执行期就准备妥当。维护“位置的完整性”是编译器的责任。而“bitwise copy semantics”可能会破坏这个位置,因此编译器必须在自己合成出来的copy constructor中做出仲裁
3、程序转化语意学
-
显式的初始化操作
X x0;
void foo_bar(){
X x1(x0); //定义了x1
X x2(x0); //定义了x2
X x3(x0); //定义了x3
}-
转化需要两个阶段
- 1、重写每一个定义,其中的初始化操作会被剥除(定义:指“占用内存”的行为)
- 2、class的copy constructor调用操作会被安插进去
-
-
参数的初始化
-
把一个class object当做参数传给一个函数(或作为一个函数的返回值),相当于 X xx = arg;的初始化操作,其中xx嗲表形式参数(或返回值),arg代表真正的参数值。
- 它会引入一个临时性object,调用copy constructor将它初始化,然后将此临时object交给函数,实现初始操作
-
另一种实现方法:“拷贝构造”(copy construct)把实际参数直接构建在其应该的位置上,此位置视函数活动范围的不同,记录于程序堆栈中。
-
-
返回值的初始化
-
双阶段转换
- 1、首先加上一个额外参数,类型是class object的一个reference,这个参数将用来放置被”拷贝构造(copy construct)“而得的返回值
- 2、在return指令之前安插一个copy constructor调用操作,以便将欲传回之object的内容当做上述新增参数的初值
-
-
使用者层面做优化
-
编译器层面做优化
-
copy constructor要还是不要
成员们的初始化队伍
-
必须使用初始化列表的情况:
- 1、当初始化一个reference member时
- 2、当初始化一个const member时
- 3、当调用一个base class的constructor,而它拥有一组参数时
- 4、当调用一个member class的constructor,而它拥有一组参数时
第三章:Data语意学
data member的绑定
- 一个inline member function躯体之内的一个data member绑定操作,会在整个class声明完成后发生
data member的布局
-
nonstatic data members在class object中的排列顺序和其被声明的顺序是一致的
-
static data members不会放在对象布局之中,而是程序的data segment中
-
C++ standard要求,在同一个access section(即private、public、private等区段中),members的排列只需符合“较晚出现的members在class object中有较高的地址”这一条即可
-
编译器也会合成一些内部使用的data members,以支持整个对象模型
- 如vptr,目前所有的编译器都把他安插在每一个“内含virtual function之class”的object内。传统上它会被放在所有显示声明的members的最后
data member的存取
-
static data members
-
每一个static data member只有一个实例,存放在程序的data segment之中,每次程序取用static member时,就会被内部转换为对该唯一extern 实例的直接参考操作
-
static member的存取并不需要通过class object
-
若取一个static data member的地址,会得到一个指向其数据类型的指针,而不是一个指向其class member的指针,因为static member并不含在一个class object之中
如
&Point3d::chunkSize
会得到const int *类型的指针 -
如果两个类中,都声明了一个static member freelist,那么他们都会被放在程序的data segment时,就会产生冲突,那怎么办?
-
编译器的解决办法是暗中对每一个static data member编码(被称为:name-mangling),以获得独一无二的程序识别代码
-
任何name-mangling有两个重点
- 1、一个算法,推导出独一无二的名称
- 2、万一编译系统(或环境工具)必须和使用者交谈,那些独一无二的名称可以轻易被推倒回到原来的名称
-
-
-
nonstatic data members
-
nonstatic data member直接存放在每一个class object中,通过显示(explicit)或隐式(implicit) class object对其进行存取
-
对nonstatic data member的存取,经由一个”implicit class object“(由this指针表达)完成
Point3d
Point3d::translate(const Point3d &pt){
x += pt.x;
y += pt.y;
z += pt.z;
}//member function内部转化
Point3d
Point3d::translate(Point3d *const this,const Point3d &pt){
this->x += pt.x;
this->y += pt.y;
this->z += pt.z;
} -
要对一个nonstatic data member进行村去操作,编译器需要把class object的起始位置加上data member的偏移位置(offset)
如:
origin._y= 0.0
那地址&origin._y等于
&origin + (&origin._y -1);-1操作是指向data member的指针,其offset值总是被加1,这样就可以使编译器区分出“一个指向data member的指针,用以指出class的第一个member”和“一个指向data member的指针,没有指出任何member”两种情况
-
“继承”与data member
-
在C++继承模型中,一个derived class object所表现出来的东西,是其自己的members加上其base class(es) members的总和
-
只要继承不要多态
- 把一个class 分解成两层或更多层,有可能会为了“表现class体系之抽象化”而膨胀所需的空间
-
加上多态
-
会带来空间和存取时间上的额外负担
-
1、导入了一个virtual table,用来存放它所声明的virtual function的地址。这个table的元素个数一般而言是被声明的virtual function的个数,再加上一个或两个slots(用以支持runtime type identification)
-
2、在每一个class object中导入一个vptr,提供执行期的链接,使每一个object能够找到相应的virtual table
-
3、加强constructor,使它能够为vptr设定初值,让它指向class所对应的virtual table
-
4、加强destructor,使它能够抹消“指向class 之相关virtual table”的vptr
- destructor的调用顺序是相反的:从derived class到base class
-
-
-
多重继承
- 多重继承的问题主要发生于derived class objects和其第二或后继的base class objects之间的转换,不论是直接转换还是经由所支持的virtual function机制做转换
- 如果要存取第二个(或后继)base class中的一个data member,由于members的位置在编译时就固定了,因此存取members只是一个简单的offset运算。
-
虚拟继承
-
典型的一个例子是早期的iostream
-
不论是istream还是ostream都内含一个ios subobject,然后再iostream的对象布局中,只需要单一一份ios subobject,语言层面的解决办法就是导入所谓的虚拟继承
-
class如果内含一个或多个virtual base class subobject,像iostream那样,将被分割成两部分,一个不变区域和一个共享区域。
- 不变区域的数据,不管后继如何衍化,总是拥有固定的offset(以object的开头算起),所以这部分数据科技直接存取
- 共享区域,所表现出来的就是virtual base class subobject这部分数据,其位置会因为每次的派生操作而有变化,所以他们只可以间接存取
-
-
如何存取class的共享部分呢?
- cfont编译器会在每一个derived class object中安插一些指针,每个指针指向一个virtual base class,要存取继承而来的virtual base class members,可以通过相关指针间接完成。
-
一般而言,virtual base class最有效的一种运用形式就是,一个抽象的virtual base class,没有任何data members
-
对象成员的效率
指向data members的指针
-
C++ standard允许vptr被放在对象中的任何位置,通常编译器将其放在对象的头或尾
-
C++语言要求同一个access level中的members的排列顺序和其声明顺序相同
-
如何区分“没有指向任何data member”的指针和一个指向“第一个data member”的指针?
- 每一个真正的member offset值都被加1,因此,不论编译器或是使用者都必须记住,在真正使用该值以指出一个member之前,请先减掉1
第七章:站在对象模型的顶端
1、Template
-
Template的“实例化”行为
-
Template的错误报告
-
Template中的名称决议法
- “scope of the template declaration”,用以专注于一般的template class
- "scope of the template instantiation",用以专注于特定的实例
-
Member function的实例化行为
-
至于template function的 实例化,目前编译器提供了两种策略
- 1、一个是编译时期策略,程序代码必须在program text file中备妥可用
- 2、另一个是链接时期策略,有一些meta-compilation工具可以导引编译器的实例化行为
-
编译器设计者必须回答三个主要问题
-
1:编译器如何找出函数的定义?
- 答案之一是包含template program text file,它就像一个header文件一样;另一种方法是要求一个文件命名规则
-
2、编译器如何能够只实例化程序中用到member functions
- 解决办法之一就是,根本忽略这项要求,把一个已经实例化的class的所有member function都产生出来;另一种策略是模拟链接操作,检查看看哪个函数真正需要,然后只为它(们)产生实例
-
3、编译器如何组织member definition在多个.o文件中都被实例化呢
- 解决办法之一就是产生多个实例,然后从链接器中提供支持,只留其中一个实例,其余忽略。另一个办法就是由使用者来导引“模拟链接阶段”的实例化策略,决定哪些实例才是所需要的
-
-
2、异常处理
- Exception Handling快速检阅
- 对Exception Handling的支持
3、执行期类型识别(Runtime Type Identification,RTTI)
- Type-Safe Downcast(类型安全向下转换)
- Type-safe Dynamic cast(类型安全动态转换)
- Reference 并不是Pointers
- Typeid运算符
4、弹性
- 动态共享函数库
- 共享内存
第六章:执行期语意学
1、对象的构造和析构
-
Destructor 必须被放在每个离开点之前
-
全局对象
-
C++程序中所有的global objects都被放置在程序的data segment中,如果显示指定给他一个值,此object将以该值为初值,否则object所配置到的所有内存内容为0
-
munch策略:一个可移植但成本颇高的静态初始化(以及内存释放)方法
- 1、为每一个需要静态初始化的文件产生一个_sti()函数,内含必要的constructor调用操作或inline expansions
- 2、在每一个需要静态的内存释放操作(static deallocation)的文件中,产生一个_std()函数,内含必要的destructor调用操作,或是其inline expansions
- 3、提供一组runtime library "munch"函数:一个_mina()函数(用以调用可执行文件中的所有_sti()函数),以及一个exit()函数(以类似方式调用所有的_std()函数)
-
如何手机一个程序中各个object files的_sti()函数和_std()函数
- 使用nm命令:nm命令会dump出object file的符号表格项目(symbol table entries)
-
-
局部静态对象
-
local static class object 保证了什么样的语意
const Matrix &
identity(){
static Matrix mat_identity;
//....
return mat_identity;
}- mat_identity的constructor必须只能施行一次,虽然上述函数可能会被调用多次
- mat_identity的destructor必须只能施行一次,虽然上述函数可能会被调用多次
-
-
对象数组
-
在cfont中,使用一个被命名为vec_new()的函数,产生出以class object构造而成的数组。
- vec_new()的主要功能是把default constructor施行于class objects所组成的数组的每一个元素身上
-
比较新近的编译器,包括Borland、Microsoft和sun,则是提供两个函数,一个用来处理“没有virtual base class”的class,另一个用来处理“内含virtual base class”的classes,后一个函数通常被称为vec_vnew()函数
-
-
default constructors和数组
2、new和delete运算符
-
int *pi = new int(5),由两步完成
-
1、通过适当的new运算符函数实例,配置所需的内存
// 调用函数库中的new运算符
int *pi = __new(sizeof(int)); -
2、将配置得来的 对象设立初值
*pi = 5
- 子主题 1
-
-
new 运算符实际上总是以标准的 C malloc()完成,delete运算符总是以标准的C free()完成
-
针对数组的new语意
-
在个别的数组元素构造过程中,如果发生exception,destructor就会被传递给vec_new(),只有已经构造妥当的元素才需要destructor的施行,因为它们的内存已经被配置出来了,vec_new()有责任在exception发生的时机吧那些内存释放掉
-
delete []p_array
- 只有中括号出现,编译器才去寻找数组的维度,否则它便假设只有单独一个object被删除
-
如果记录元素个数?
- 为vec_new()所传回的每一个内存区块配置一个额外的word,然后把元素个数包藏在那个word中
-
-
placement operator new的语意
3、临时性对象
第五章:构造、析构、拷贝语意学
纯虚函数的存在
- 可以定义和调用一个pure virtual 粉刺通:不过它只能被静态地调用,不能经由虚拟机制调用
- 不要将virtual destructor 声明为pure
虚拟规格的存在
- 一般而言,把所有的成员函数都声明为virtual function,然后再靠编译器的优化操作把非必要的virtual invocation(调用)去掉,并不是一个号的设计理念
虚拟规格中const的存在
- 不把函数声明为const,意味着此函数不能够获得一个const reference或const pointer
- 声明一个函数为const,然后发现其derived instance必须修改一个data member----头大了
- 作者的意思就不用const了
重新考虑class的声明
1、“无继承”情况下的对象构造
-
一个object的生命,是该object的一个执行期属性。
- local object的生命从定义开始,该作用域结束
- global object的什么和整个程序的生命周期相同
- heap object的生命从被new运算符配置出来开始,到delete运算符摧毁为止
-
抽象数据类型
-
为继承做准备
2、继承体系下的对象构造
-
constructor可能内含大量的隐藏码,因为编译器会扩充每一个constructor,扩充程度视class T的继承体系而定。一般而言编译器所做的扩充操作大致如下:
-
1、记录在member initialization list中的data members初始化操作会被方静constructor的函数本地,并以member的声明顺序为顺序
-
2、如果有一个member并没有出现在member initialization list中,但它有default constructor,那么该default constructor必须被调用
-
3、在那之前,如果class object有virtual table pointer(s),它(们)必须被设定初值,指向适当的virtual table(s)
-
4、在那之前,所有上一层的base class constructors必须被调用,以base class的声明顺序为顺序(与member initialization list中的顺序没有关联)
- 如果base class被列于member initialization list中那么任何显示指定的参数都应该传递过去
- 如果base class没有被列于member initialization list中,而它有default constructor(或default memberwise copy constructor),那么就调用之
- 如果base class是多重继承下的第二或后继的base class,那么this指针必须有所调整
-
5、在那之前,所有virtual base class constructors必须被调用,从左到右,从最深到最浅
- 如果class被列于member initialization list中,那么如果有任何显示指定的参数,都应该传递过去。若没有列于list之中,而class有一个default constructor,亦应该调用
- 此外,class中每一个virtual base class subobject的偏移位置(offset)必须在执行期被存取
- 如果class object是最底层(most-derived)的class,其constructors可能被调用;某些用以支持这一行为的机制必须被放进来
-
-
虚拟继承
-
vptr语意学
-
在一个class的constructor(或destructor)中,经由构造中的对象来调用一个virtual function ,其函数实例应该是在此class中有作用的那个。
-
constructor的调用顺序是:有根源而末端,由内而外
-
什么决定一个class的virtual functions名单的关键?virtual table;virtual table如果被处理?通过vptr。所以为了控制一个class中有所作用的函数,编译系统只要简单地控制住vptr的初始化和设定操作即可
-
vptr何时被初始化
- 在base class constructors调用操作之后,但是在程序员供应的代码或是“member initialization list中所列的members初始化操作”之前
-
constructor的执行算法通常如下
- 1、在derived class constructor中,“所有virtual base classes”及“上一层 base class”的constructors会被调用
- 2、上述完成后,对象的vptr(s)被初始化,指向相关的virtual table(s)
- 3、如果有member initialization list的话,将在constructor体内扩展开。者必须在vptr被设定之后才做,以免有一个virtual member function被调用
- 4、最后,执行程序员所提供的代码
-
3、对象赋值语意学
-
只有在默认行为所导致的语意不安全或不正确时,才需要设计个copy assignment operator
-
一个class对于默认的copy assignment operator,在以下情况,不会表现出bitwise copy语意
- 1、当class内含一个member object,而其class有一个copy assignment operator时
- 2、当一个class的base class有一个copy assignment operator时
- 3、当一个class声明了任何virtual functions(一定不要拷贝有段class object的vptr地址,因为它可能是一个derived class object)时
- 4、当class继承自一个virtual base class(不论此base class有没有UC偶朋友 operator)
-
建议:尽可能不要允许一个virtual base class的拷贝操作
4、对象的效能
5、析构语意学
- 如果class没有定义destructor,那么只有在class内含的member object(抑或class自己的base class)拥有destructor的情况下,编译期才会自动合成一个来。否则destructor被视为不需要,也就不需被合成(当然更不需要被调用)
- 为决定class是否需要一个程序层面的destructor(或是constructor),请务必想清楚class object的生命在哪里结束或开始?需要什么操作保证对象的完整?
第四章:Function语意学
1、Member的各种调用方式
-
Nonstatic member function
-
C++的设计准则之一就是:nonstatic member function至少必须和一般的nonmember function有相同的效率
-
编译器内部会将nonstatic member function转换为nonmember function,步骤如下:
- 1、改写函数的signature(函数原型)以安插一个额外的参数到member function中,用以调用一个存取管道,使class object得以将此函数调用。该额外参数被称为this指针
- 2、将每一个“对nonstatic data member的存取操作”改为经由this指针来存取
- 3、将member function重新写成一个外部函数。将函数名称经过“mangling”处理,使它在程序中成为独一无二的命名
-
-
virtual member function
-
举例
若normalize()是一个virtual member function,其调用如下:
ptr->normalize()将会被内部转换为:
(* ptr -> vptr[1])(ptr);- vptr表示由编译器产生的指针,指向virtual table,它被安插在每一个“声明(或继承自)一个或多个virtual functions”的class object中,其实,其名称也会被mangled,因为在一个复杂的class派生体系中,可能存在多个vptrs
- 示例中的1,代表virtual table slot的索引值,关联到normalize()函数
- 示例中的第二个ptr表示this指针
-
-
static member function
-
只有当一个或多个nonstatic data members在member function中被直接存取时,才需要class object
-
静态成员函数的主要特性是它没有this指针
-
依据主要特性有以下的次要特性
- 1、它不能够直接存取其class中的nonstatic members
- 2、他不能够被声明为const、volatile或virtual
- 3、他不需要经由class object才被调用
-
2、virtual member function
-
在C++中,多态(polymorphism)表示“以一个public base class的指针(或reference),寻址出一个derived class object”的意思
-
ptr的多态机能主要扮演一个输送机制(transport mechanism)的角色,经由它,可以在程序的任何地方采用一组public derived类型。这种多态形式被称为是消极的,可以在编译时期完成--virtual base class的情况除外
-
识别一个class是否支持多态,唯一适当的方法就是看它是否有任何virtual function,只要class拥有一个virtual function,它就需要这份额外的执行期信息
-
什么信息才能使在执行期调用正确
- ptr所指对象的真实类型
- 函数的地址,以便能够调用它
-
实现上,在每一个多态的class object身上增加两个members
- 1、一个字符串或数字,表示class的类型
- 2、一个指针,指向某表格,表格中持有程序的virtual functions的执行期地址
-
举例:ptr->z()
-
一般而言,在每次调用z()时,并不知道ptr所指对象的真实类型,但知道经由ptr可以存取到该对象的virtual table
-
虽并不知道哪个z()函数实例被调用,但知道每个z()函数地址被放在slot4中:(*ptr->vptr[4])(ptr);
- vptr表示编译器所安插的指针,指向virtual table;4代表z()被指派的slot编号(关系到Point体系的virtual table)。
-
-
-
多重继承下的virtual function
- 多重继承下中支持virtual functions,其复杂度围绕在第二个及后继的base classes上,以及“必须在执行期调整this指针”这一点
- 一般规则,经由指向“第二或后继的base class”的指针(或引用)来调用derived class virtual function
-
虚拟继承下的virtual function
- 不要在一个virtual base class中声明nonstatic data members
3、函数的效能
4、指向member function 的指针
-
取一个nonstatic member function的地址,如果该函数式nonvirtual,得到的结果就是它在内存中的真实地址
-
指向member function的指针的声明语法,以及指向“member selection就是.运算符”的指针,其作用是作为this指针的空间保留者
-
支持“指向virtual member function”的指针
- virtual function其地址在编译时期是未知的,所能知道的virtual function在其相关的virtual table中的索引值。即对一个virtual member function 取地址,得到的是一个索引值
-
在多重继承下,指向member functions的指针
-
"指向member functions的指针"的效率
5、inline function
-
一般处理inline函数有两个阶段
-
1、分析函数定义,已决定函数的“intrinsic ability”(本质的inline能力),“intrinsic”(本质的,固有的),这里意指”与编译器相关“
- 如果函数因其复杂度,或因其建构问题,被判断不可成为inline,它会被转为一个static函数,并在“被编译模块”颞产生对应的函数定义
-
2、郑州的inline函数扩展操作是在调用的那一点上,这会带来参数的求值操作以及临时性对象的管理
-
-
形式参数
- 一般而言,面对“会带来副作用的实际参数”,通常都需要引入临时性对象,即如果实际参数是一个常量表达式,在替换之前先完成其求值操作;后继的inline替换,就可吧常量直接“绑”上去;如果既不是常量表达式,也不是带有副作用的表达式,那就直接替换
-
局部变量
- 一般而言,inline函数中的每一个局部变量都必须被放在函数调用的一个封闭区段中,拥有一个独一无二的名称