您的位置:首页 > 编程语言 > C语言/C++

深度探索C++对象模型 【第四章1】

2017-11-06 09:23 281 查看
1:静态成员函数(static member function)不可能是做到以下两点:

不可能直接存取非静态成员变量
不可能被声明为const
2:类成员函数发展历史

原始的class只支持非静态成员函数
20世纪80年代加入了虚函数机制
静态成员函数在1987年最后加入,并由cfront2.0实现
3:C++的设计准则:非静态成员函数至少和非成员函数有着相同的效率。这就产生了内在的成员函数被内化为非成员函数的过程

改写函数的原型(signature),安插一个和额外的参数到成员函数中,以提供一个存取管道,使得对象得以调用该函数,改参数被称为“this指针”
将成员函数中的每一个“非静态成员数据的存取操作”改变为通过this指针来存取
最后将成员函数重写为一个外部函数,再经由编译器的mangling处理,变成了程序中独一无二的存在
4:名称的特殊处理,一般而言,成员的名称之前都会加上class的 名称,变得独一无二。编译器还会有跟更加优化的处理,假设在基类和派生类中都声明了一个同名的变量,那么该如何区分它们呢?

ival_3BAR 和 ival_3FOO,编译器将BAR和FOO两个类中的同名变量分别命名,FOO继承自BAR这样在派生类中就不会引起冲突。
由于成员函数还可以被重载,所以就需要更广泛的name_mangling手法,以提供独一无二的名称。

5:在函数前声明“extern C”会压制编译器的非成员函数的name_mangling。

6:C++目前的name_mangling编码方法并未统一。目前编译器未将其中的内部实现名称展示给程序员,cfront决议非虚函数方式为func1_fv(),Func_fl()。

7:对于虚成员函数(virtual member functions),使用对象显示调用虚函数将和非虚函数的决议方式相同;但使用指针或者引用调用时,其决议方式会发生改变
A a;
A *b = &a;
b->func1();//func1为虚函数
(*b->vptr[1])(b);//内部转化结果,1是vbtl的索引值,指向虚函数func1;第二个b为this指针;vptr[0]中存的可能是RTTI


vptr是由编译器产生的指针,指向vbtl,它会出现在每一个含有虚函数类的类对象中。

8:将一个虚成员函数声明为inline,将会带来极大的效率利益。

9:静态成员函数将会被转化为一般的非成员函数调用,其主要特性就是没有this指针,不需要对象就能够使用(但是也可以通过对象调用),但其不能被声明为const/volitale/virtual。

10:如果获取一个静态成员函数的地址,将会获得其在内存中的位置,也就是其地址,且其地址的类型不是一个指向class member function的指针,而是一个非成员的指针。

11:在C++中,多态最初的意思就是使用一个基类指针寻址出一个派生类对象的意思,1993年RTTI被引入,产生了积极多态,我们可以在执行期查询一个多态的指针或者引用。

12:识别一个class是否支持多态,唯一适当的方法时就是看它是否有任何的虚函数。

编译期:找到vbtl,找到虚函数的地址
执行期:激活虚函数(纯虚函数如果被意外的调用,通常的做法是直接结束掉这个程序)
13:单一继承中的虚函数机制行为良好,但虚拟继承和多重继承中,就没这么简单了。对于多重继承来说,Base1和Base2共同派生一个Derived.

Base1 *p1 = new Derived;  //p1不需要调整this指针,因为Base1为最左端的基类,它已经指向派生类对象的起始处。虚函数表中存放的是真正的虚函数指针。
Base2 *p2 = new Derived; // p2需要调整this指针,其虚函数表中需要相关的thunk地址
在多重继承之下,一个派生类中内含n-1个虚函数表,n表示其上一层的基类个数。对于上例来说,会有两个虚函数表被编译器产生,一个主要实例,与Base1共享;一个次要实例,与Base2有关。这两个实例将会以外部对象的方式产生,两个表格的命名有差异。

14:虚拟继承中的虚函数机制由于太过于复杂,所以本书中Lippman也没有解释。只留下了一个建议:不要在虚基类中定义一个非静态数据成员。

15:对于成员函数的效能,有以下的测试结果

inline函数有明显的提升,消除了额外的时间负担,也提供了程序优化的额外机会
每多一层继承,虚函数的执行时间就有明显的增加
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息