Function语意学
static member functions不能直接存取nonstatic数据,不能被声明为const
4.1Member的各种调用方式
Nonstatic Member Functions(非静态成员函数)
nonstatic member function至少必须和一般的nonmember function有相同的效率,这是因为编译器内部已将”member函数实例“转换为对等的”nonmember函数实例“
转换步骤:
1.改写函数的signature(译注:意指函数原型)以安插一个额外的参数到member function中,用以提供一个存取管道,使class object得以将此函数调用。该额外参数被称为this指针
2.将每一个”对nonstatic data member的存取操作“改为经由this指针来存取
3.将member function重新写成一个外部函数。将函数名称经过”mangling“处理,使它在程序中成为独一无二的词汇
名称的特殊处理(Name Mangling)
member的名称前面加上class名称,再加上它们的参数链表(可以从函数原型参考得到),制造出独一无二的结果
Virtual Member Functions(虚拟成员函数)
virtual member function的调用会被转化为对编译期产生的指向virtual table的指针vptr的访问;并通过索引确定virtual table slot的位置;和非静态成员函数类似,也为其安插一个this指针
Static Member Functions(静态成员函数)
在引入static member functions之前使用
((class*)0)->member function();//将0强制转换为一个class指针,因而提供一个this指针实例
Static member functions的主要特性就是它没有this指针,下面的特性都是根源于其主要特性:
1.它不能够直接存取其class中的nonstatic members
2.它不能够被声明为const,volatile,virtual
3.不需要经由class object才被调用
foo().static_member_function();
//会被转换为
(void)foo();//仍需要评估求值 class::static_member_function();
取一个static member function的地址,指针类型是”nonmember函数指针“
4.2Virtual Member Functions(虚拟成员函数)
多态(Polymorphism)表示”以一个public base class的指针(或reference),寻址出一个derived class object“的意思
识别一个class是否支持多态,唯一适当的方法就是看它是否有任何virtual function。只要class拥有一个virtual function,它就需要一份额外的执行期信息(runtime type identification)
在执行期调用正确的virtual function实例,需要知道:
1.ptr所指对象的真实类型。这可使我们选择正确的virtual function实例(一个字符串或数字,表示class的类型)
2.virtual function实例的位置,以便能够调用它(一个指针,指向某表格,表格中持有程序的virtual functions的执行期地址,地址和地址指向的表格在执行期都是固定不变的)
为了找到函数地址,每个virtual function被指派一个表格索引值
一个class只会有一个virtual table。每一个table内含其对应之class object中所有active virtual functions函数实例的地址:
1.这一class所定义的函数实例。它会改写一个可能存在的base class virtual function函数实例
2.继承自base class的函数实例。这是在derived class决定不改写virtual function时才会出现的情况
3.一个pure_virtual_called()函数实例,它既可以扮演pure virtual function的空间保卫者角色,也可以当作执行期异常处理函数
单一继承
单一继承的三种可能性:
- 将base class virtual table中的地址拷贝到derived class virtual table的相对应slot之中
- 可以使用自己的函数实例(被放到对应slot之中)
- 可以加入新的virtual function, 就增加virtual table尺寸, 新的函数实例地址被放入该slot之中
从上面可以看到, 单继承下的虚函数效率高, 实现简单, 但是多继承和虚拟继承则要复杂很多
多重继承下的Virtual Functions
和第三章相似,多重继承中支持virtual functions的复杂度主要围绕在第二个及其后继的base classes身上,以及“必须在执行期调整this指针”
“Derived 支持virtual functions”的困难度
1.virtual destructor
2.被继承下来的class virtual function
3.一组virtual functions实例
将一个derived class object地址指定给一个base class指针,地址必须调整以指向base class subobject;
要删除base class指针时,指针必须被再次调整以指向derived class object的起始处(由于指针所指的真正对象只有在执行期才能确定,所以“this指针调整”也必须在执行期完成)
通过第一个base class指针访问时, 不需要调整this指针。其virtual table slot需放置真正的destructor地址
通过第2,3,...个base class指针访问时, 其virtual table slot需要相关的thunk地址,多重继承下一个derived class内含n-1额外的virtual tables
由于执行期链接器的出现导致函数名称的链接可能变得缓慢,为此将多个virtual tables连锁为一个,通过offset获取指向次要表格的指针
虚拟继承下的Virtual Functions
当一个virtual base class从另一个virtual base class派生而来,并且两者都支持virtual functions和nonstatic data members时,编译器对virtual base class的支持简直就像进了迷宫。所以不要在virtual base class中声明nonstatic data members。
4.3函数的效能
inline函数不只能够节省一半函数调用所带来的额外负担,也提供了程序优化的额外机会
对virtual function的调用,因为调用操作经过了虚拟机制会导致效率降低,而且无论是多重继承还是单一继承都会消耗相同的成本(因为调用)
4.4指向Member Function的指针
指向member function的指针的声明语法,以及指向“member selection 运算符”的指针,其作用是作为this指针的空间保留者,如果该指针不用于virtual function、多重继承、virtual base class等情况,编译器可以为其提供和nonmember function指针相同的效率
支持“指向Virtual Member Functions”的指针
对一个virtual member function取其地址,所能获得的只是一个索引值
#include <iostream> #include <vector> #include <string> using namespace std; class Base1 { protected: float data_Base1; public: Base1(/* args */) {} virtual ~Base1() {} virtual void speakClearly() {} virtual Base1* clone() const { return const_cast<Base1*>(this); } }; class Base2 { protected: float data_Base2; public: Base2(/* args */) {} virtual ~Base2() {} virtual void memble() {} virtual Base2* clone() const { return const_cast<Base2*>(this); } }; class Derived :public Base1, public Base2 { protected: float data_Derived; public: Derived(/* args */) {} virtual ~Derived() {} virtual Derived* clone() const { return const_cast<Derived*>(this); } }; int main() { void (Base2:: * pmf)()=&Base2::memble; Base2* pbase2 = new Derived; (pbase2->*pmf)(); cout << &Base2::memble; }
在多重继承下,指向Member Functions的指针
Microsoft供应了三种风味:
1.一个单一继承实例
2.一个多重继承实例
3.一个虚拟继承实例
“指向Member Functions之指针”的效率
4.5Inline Functions
在下面的情况下, 一个函数是inline函数:
1.声明中包含inline关键字的函数
2.当一个函数(成员函数或非成员友元函数)的定义在类内部时
3.被声明为constexpr的函数(since C++11)
关键词inline只是一个请求,并不能强制将任何函数都变成inline,处理一个inline函数有两个阶段:
1.分析函数定义,以决定函数的“intrinsic inline ability”(本质的inline能力)
2.真正的inline函数扩展操作是在调用时编译器决定是否“不可为inline”
形式参数
在inline扩展期间,每一个形式参数都会被对应的实际参数取代。而面对可能对实际参数多次求值操作则会引入临时性对象
局部变量
如果inline函数以单一表达式扩展多次,则每次扩展都需要自己的一组局部变量,如果以分离的多个式子,那么只需一组局部变量
inline是对C程序中大量使用的#define的一个安全替代品——特别是宏中的参数有副作用的话。但inline函数如果被调用太多次的话会产生大量的扩展码,使程序大小暴涨