《inside the cpp object model》 阶段性阅读总结(4)

第四章 函数的语义

章前阅读 c++支持,静态,非静态函数成员,虚函数,不同的函数调用,会产生不同的效果。

第一节 不同种类的成员调用

非静态成员函数。在挑选函数实例的时候,是没有花费的,这个是通过内部的转换,将成员函数变为非成员函数而实现的。包括以下三步:参数表中添加this指针,函数体内成员变量增加this指针,重写函数为外部函数并进行名字分解。

名字分解。成员函数的名字被改为唯一的名字,通过整合类名和成员名(与类名结合,我们就可以在子类里访问父类的同名成员了)。分解的模式未知,是和编译器相关的。

虚成员函数。虚函数的调用,会被转义为形如( * ptr->vptr[ index ])( ptr ); 的调用。如果这个类不支持多态,那么通过实例调用函数也会被转义为普通的非静态成员函数。

静态成员函数。由于静态成员函数是被挂在对象外部的。。于是直接分解为普通成员函数,并且不包含this指针,这是最重要的特性。

第二节 虚成员函数

为了支持虚函数的机制,一些形式的运行时类型识别需要被支持。

在这个策略下,一个指针需要包含两方面的信息:

对象的地址

对象类型的编码或者一个地址指向存储该信息的结构体。(目的是寻找正确的调用)

这个解决方案有两个方面的问题。

不论这个程序是不是需要多态,都增加了很大的空间花费

打破了对c语言的兼容

当然,指针是无法存储这些东西的,所以具体的类型的信息会被存储到对象里面(虚函数表里)。

接下来需要解决的的是,区分出哪些类需要这些信息。首先不能通过class或者struct这种关键字去区分。RTTI解决了这个问题,RTTI的出现,使得编译器只需要孤立的考虑哪些类需要RTTI,判断的方法是,查看类是否包含虚函数。

下一个问题是,储存哪些信息,共需要

一个字符串或者数字来代表类型

一个指向某些表的指针,保存程序虚函数的运行时地址(虚函数表)。

子类的同名虚函数地址,会替换父类的虚函数地址,并且和父类同名虚函数的下标一致。这样的好处是,分解完后的调用,只有指针地址是未知的,虚函数指针和下标都是固定的。这样便可支持多态。

多重继承下的虚函数

Base2 *pbase2 = new Derived; 会转义为derived首地址+偏移量(之前的类对象占用的空间)

delete pbase2的时候,又会重新定位为derived首地址。

this指针也需要偏移。编译器的做法是,将vptr转换为结构体,分别存储函数地址和偏移量。

多重继承下,可能会包含多个虚函数表,此时虚函数表的名字会与类名结合到一起,方便调用。

第三章 函数的效率

第四章 指向成员函数的指针

对普通的成员函数取地址,获得的是在内存中的实际地址。如果对虚函数取地址,获得的是在虚函数表中的下标。编译器通过&~127来判断当前值是否是虚函数的地址(于是编译器最多可存储128个虚函数)。单继承下这个是可行的。但是多重继承的引入,使得编译器不得不考虑一个方法来破除这个限制。多重继承下,函数地址指向一个结构体。

struct __mptr {
   int delta;
   int index;
   union {
      ptrtofunc  faddr;
      int        v_offset;
   };
};

当index小于0的时候,转义为( *pmf.faddr )( ptr ) ,否则  ( * ptr->vptr[ pmf.index ]( ptr ); delta代表this指针的偏移量。

第五节 内联函数

节省了调用和返回值的花费。

posted @ 2011-05-23 16:43  dk647  阅读(1115)  评论(0编辑  收藏  举报