每一个具有虚函数的类叫做多态类(抽象类?)。C++为每一个多态类至少创建一个虚函数表(vtable),它其实是一个函数指针数组,其中存放着这个类所有的虚函数的地址,包括继承下来但未改写的虚函数及该类的类型信息。
每一个抽象类的对象都有一个隐含的指针成员(即vptr),它指向所属类型的vtable,,程序中通过基类指针或引用对虚函数的调用语句都会被编译器改写成如下的形式:(即:利用基类的指针指向派生类对象,调用派生类对象的函数--->实现多态性。)
(*(p -> _vptr[slotNum]))(p, arg-list);
其中:p是基类型指针,_vptr是p指向的对象的隐含指针,而slotNum就是你调用的虚函数在vtable中的编号,这个数组元素的索引号在编译时就确定了下来。
C++对象的内存映像:
1.非静态数据成员被放在每一个对象体内作为对象专有的数据成员。
2.静态数据成员被提取出来放在程序的静态数据区内为该类所有对象共享,一次仅存一份。
3.静态和非静态成员函数最终都被提取出来放在程序的代码段中并为该类的所有对象共享,因此每一个成员函数也只存在一份代码实体。
因此,构成对象本身的只有数据,任何成员函数都不隶属于任何一个对象,非静态成员函数与对象的关系就是绑定,绑定的中介就是this指针。
增加了继承和虚函数的类的对象模型变得更加复杂,规则如下:
1.派生类继承基类的非静态数据成员,并作为自己对象的专用数据成员。
2.派生类基础基类的非静态成员函数,并像自己的成员函数一样访问。
3.为每一个多态类创建一个虚函数指针数组vtable,该类的所有虚函数的地址都保存在这张表中。
4.多态类的每一个对象中安插一个指针成员vptr,其类型为指向函数指针的指针,它指向所属类的vtable;vptr是C++对象的隐含数据成员之一。
5.为了支持RTTI,为每一个多态类创建一个type_info对象,并把其地址保存在vtable中的固定位置。
C++处理函数的方式:
在编译器里,同一个函数只存在一个实现,不管是全局函数还是成员函数。对于两个源文件中分别定义的两个完全相同的static全局函数,由于编译器认为它们是不同的函数,会分别为它们产生可执行代码。
C++通过Name-Mangling技术把每一个非静态成员函数都转换成了名字唯一的全局函数,并把通过对象、指针或引用对每一个成员函数的调用语句改写为相应的全局函数调用语句。例如Rectangle类的函数SetLength被编译器改写后的样子可能是:
void _Rectangle_SetLength_2F_pf(Rectangle *this, float length)
{
this->m_length = length;
}
因此调用语句rect.SetLength(100.5);被编译器改写成:
::_Rectangle_SetLength_2F_pf(&rect1, 100.5);
处理静态成员:
类的静态成员都不依赖于对象的存在而存在,也就不需要通过对象来访问,因此在本质上就是一种全局变量或函数。
类的静态数据成员可以在class的定义中直接初始化,但这只是声明并给它提供一个初值而已,还必须在某一个源文件中把它定义一次(即分配内存)。
静态成员函数不需要this指针。