【C++】深度探索C++对象模型之Function语意学
2016-01-01 20:26
691 查看
一、Member的各种调用方式
1. Nonstatic Member Functions
首先给出nonstatic member function 和 nonmember functionfloat magnitude3d( const Point *_this ) { ... } // nonmember function float Point3d::magnitude3d() const { ... } // nonstatic member function对于C++中class中的非静态成员函数,都会转化成对等的 “ nonmember 函数实例 ”, 也就是
float magnitude3d( const Point3d* _this ) { return sqrt( _this->x * _this->x + _this->y * _this->y + _this->z * _this->z ); }上式就是通过对 nonstatic member function
float Point3d::magnitude3d() const { ... } // nonstatic member function改写成nonmember function的过程。转化步骤如下:
改写函数的原型以安插一个额外的参数到member function中,此例为_this指针。以及const nonstatic member
对每个“nonstatic data member的存取操作” 改成this指针来存取,也就是 _this->x 等
将member function重新写成一个外部函数。 也就是对上式改写成类似
extern magnitude__7Point3dFv( register Point3d *const this );
调用操作抑由 obj.magnitude() 改写成 magnitude__7Point3dFv( &obj );
而 ptr->magnitude() 改写成 magnitude__7Point3dFv(ptr) ;
上述经过named return value函数的内部转化还没被改写成
void normalize_7Point3dFv( register const point3d *const this, Point3d &__result ) { register float mag = this->magnitude(); _result.Point3d::Point3d( this->x_mag, this->y_mag, this->z_mag ); return; }
名称的特殊处理
对于member的名称,一般前面会加上class名称,以形成独一无二的命名。 也就是mangling 手法来提供独一无二的名称。
Name mangling 就是通过内部的编码对函数名称申明唯一的实例。
如果名称不一致,会导致编译器在链接时期因无法决议而失败,也就是确保类型安全的链接行为。
2. virtual Member funtions
如果normalize()是一个virtual member function,那么以下调用ptr->normalize();会被转换为:
(* ptr->vptr[1])( ptr );
vptr即指向virtual table的指针
1是virtual table slot的索引值,也就是指向virtual table上的哪个函数的下标,这里是关联到normalize
第二个ptr表示this 指针。
3. static member function
如果 Point3d::normalize()是一个static member function, 那么以下调用:obj.normalize(); ptr->normalize();一般都会转化为以下nonmember函数的调用,也就是
// obj.normalize(); normalize_7Point3dSFv(); // ptr->normalize(); normalize_7Point3dSfv();
在引入static member function之前,C++要求所有的成员函数必须由class的object来调用
而实际上,只有当一个或者多个nonstatic data member在member function中被直接存取时,才需要class object。这个this指针绑定着object对应的member之上。
因此,如果class的设计者,把static data member声明为nonpublic,那么就必须提供一个或多个member function来存取该member。因此可以不靠class object来存取一个static member,但其存取函数却得绑定于一个class object之上。
如果希望支持“没有 class object 存在”,那么程序的解决之道是把0强制转化为一个class指针,因而提供一个this指针实例。
object_count( (Point3d*) 0);
static member function 的主要特性是没有this 指针,其余特性如下:
它不能直接存取其class中的nonstatic members
它不能被申明为const , volatile 或者 virtual
它不需要经由class object才被调用。
【如果取一个static member function的地址,获得的讲师其在内存中的位置,也就是其地址。】
【由于static member function没有this指针,所以其地址类型并不是一个“指向class member function的指针”,而是一个“nonmember 函数指针”。】
也就是说调用
&Point3d::object_count();获取的是
unsigned int(*) ();而不是
unsigned int( Point3d::* ) ();
因此static member function的好处是成为一个callback函数,也可以应用于线程threads之上。
二、virtual member function
在C++中,多态(polymorphism)表示:以一个public base class的指针(或reference),寻址出一个derivedclass object。
比如:
Point *ptr; ptr = new Point2d; //寻址出一个Point2d的对象 ptr = new Point3d; //寻址出一个Point3d的对象
欲鉴定哪些classes展现多态特性,我们需要额外的执行期信息,因此识别一个class是否支持多态的唯一适当方法,是看看是否含有virtual function。
virtual function的一般实现模型:每一个class有一个virtual table,内含class之中有作用的virtual function的地址。然后每个object都有一个vptr,指向virtual table的所在。
为了支持virtual function的机制,必须首先对于多态对象有某种形式的“执行期类型判断法(runtime type resolution)”。
假如有例子如下:
Point *ptr1, *ptr2; ptr1 = new Point2D; ptr2 = new Point3D; ptr1->z();
其中z()是一个virtual function, 为了在执行期调用正确的z(), 需要知道
ptr所指对象的真实类型
z()实例的位置
所以在实现上,我们需要为每一个多态的class object增加两个members
一个字符串或数字,表示class的类型
一个指针,指向某表格,表格中有程序的virtual function的执行期的地址。
注意,上述所说的地址在程序执行之后,表格的大小和内容都不会改变。
如何寻址?
为了找到表格,每一个class object被安插了一个由编译器内部产生的指针,指向该表格
为了找到函数地址,每一个virtual function被指派一个表格索引值。
每一个class只会有一个virtual table, virtual table的存储内容?
每一个table内含其对应的class object中所有active virtual function函数实例的地址, 包括:
class所定义的函数实例,overriding可能存在的base class virtual function的函数实例
继承自base class的函数实例。即derived class决定不改写virtual function时才出现的情况
pure_virtual_called()函数实例,扮演pure virtual function的空间保卫者角色,也可以当做执行期异常处理函数。
每一个virtual function都被指派一个固定的索引值也就是slot,这是为了索引在上述所说的active virtual function在virtual table中的编号或者地址。
在单一继承中,一般每个上述的active virtual function的实例在virtual table中的布局的slot是一致的。所以对于调用
ptr->z();以上信息足够使得编译器将调用转化为:
(*ptr->vptr[ 4 ])(ptr);唯一在执行期才能够知道的东西是:slot 4到底是指向哪个z()的实例。
1. 多重继承下的virtual function
假设我们有以下的继承关系:class Derived : public Base1, public Base2;其中三个类中分别有虚析构函数,成员函数mumble(), 以及对应对象的clone()函数。如Base2
virtual ~Base2(); virtual void mumble(); virtual Base2 *clone() const;当执行下述语句时:
Base2 *pbase2 = new Derived;此时新的Derived对象的地址必须调整以指向其Base2 subobject。编译器会产生以下代码:
Derived *temp = new Derived; Base2 *pbase2 = temp ? temp + sizeof(Base1) : 0;而当程序删除pbase2所指的对象时,如
delete pbase2;此时指针将会再次被调整,以指向Derived对象的起始处。上述的offset的加法在编译时期不能直接设定,因此pbase2所指的真正对象只有在执行期才会确定。
在多重继承下,一个derived class内含n-1个额外的virtual table,n表示其上一层base classes的个数。派生类的虚函数表是与基类共享的,所以有几个基类,就会有几个虚函数表,其中第一个虚函数表是派生类最最左的基类共享的。
问题:符号名称的链接很慢,如何解决?
--> 为了调节链接器的效率,就把多个virtual table连锁成一个。指向次要表格的指针由主要表格的指针加上一个offset-->因此最终每个class只有一个具名的vtable。
2. 虚拟继承下的virtual Functions
假如:class Point3d : public virtual Point2d则面对于Point3d的virtual function的布局而言。
Virtual Table Point3d的第一个值是一个offset to virtual base,指向Point2d subobject。 而在Point2d subobject中则有另一个vptr指向Virtual table Point2d subobject of Point3d。
参考:
深度探索C++对象模型,侯捷, 第四章 Function语意学
相关文章推荐
- 【1】Windows游戏安全之路——学习规划
- C++内容总结--自定义数据类型
- C++内容总结--面向对象语言特点
- C++模板元编程(二)
- 【0】Windows游戏安全之路——个人学习背景
- Mac下好用的的C/C++IDE——Clion使用技巧
- C++ 的名字查找(name lookup)
- 二项队列--C语言实现
- C++多重继承下的指针类型转换 图解
- C语言指针-----指针与变量
- 递归与尾递归 (C语言)
- C++笔记——纯虚函数和抽象类
- C语言文件操作函数大全
- 4、C语言与画面显示的练习
- C或C++文件操作大全
- c++ 块内存管理
- C++小题(七)
- c++之析构函数
- [ 1011] <<C语言深度剖析>> 测试 TEST
- 一张图总结Google C++编程规范(Google C++ Style Guide)