C++对象模型之虚函数实现原理

来源:互联网 发布:软件流程图画图工具 编辑:程序博客网 时间:2024/06/08 08:40

在C++中,多态(polymorphism)的意思是,用基类的指针或者引用,寻址出一个派生类对象。而虚函数(virtual member function)是多态的基础,这也是面向对象编程迷人之处。现在刚好有时间,就写一下自己对C++在单一继承情况下如何实现虚函数的肤浅认识。

一旦一个类有一个虚函数,那么一定会建立一个虚表(virtual table),虚表里面有所有虚函数的地址。虚表是该类所有对象共享的,每个对象都有一个虚指针(vptr),指向虚表。虚指针的设定与重置,是由类的构造函数与析构函数以及复制运算符(copy assignment)自动完成的。

虚表是虚函数的基础。表格有很多槽(slot),通常第一个槽位存放的是每一个类所关联的类型信息(type_info object),用来支持运行时类型识别(RTTI)。

class Demo{public:Demo();virtual ~Demo();virtual void f1();virtual void f2();void f3();static void f4();private:static int ival;long lval;};

图1 Demo类的对象内存布局

每个对象内部都会存储非静态成员变量以及虚指针,其它成员都会在对象外部存储,这些不是这次要讨论的内容。

在C++中,虚函数在编译器间获知,而且这些虚函数的地址是固定不变的,执行期不可能新增或者被替换。为了找到函数的地址,每一个虚函数都会有一个在虚表里面的索引号。

如果继承基类的过程中,派生类决定不改写某个虚函数,那么它就会继承基类这个函数的实体(定义),此时,在这个派生类的虚表中存放的就是基类的该函数。否则,如果派生类重写了基类的某个虚函数,那么派生类的虚表中存放的就会是对此虚函数重新定义的实体。如下图所示。

 

class Base{public:virtual void x();virtual void y();int ival;};//单一继承class Derived: public Base{public:void x();virtual void z();long lval;};

图2 单一继承情况下的虚表

特别要注意的是:

1)如果是继承基类声明的虚函数实体,那么该函数实体就会被拷贝到派生类虚表相对应的槽位上。

2)如果是使用自己的函数实体,也就是改写基类的虚函数,那么也必须把这个函数实体地址放到相对应的槽位上。

3)如果是新增加一个虚函数,那么虚表就会增加一个slot,并把新的虚函数实体放进这里。

所以,假设对于上面的例子,我们有这样的用法:

ptr->x();

我们不知道ptr是Base还是Dervied指针,也就无法由ptr取得该对象的虚表,但是,我知道每一个x()函数的地址都会放在#1槽位上,所以,编译器会将该调用转换成

(*ptr->vptr[1])(ptr);

上面的表达式中,唯一需要在执行期才能知道的东西是,槽位1是哪个类虚表的,一旦知道了,那么便可调用相应的函数体了。

 

就写到这。

多重继承与虚拟继承下,虚函数的实现就没那么容易了。下次有空写一下。

原创粉丝点击