C++虚拟函式实现方式 [大三TJB_708]

1 虚拟函式表

       如果能够了解C++编译器对于虚拟函式的实现方式,我们就能够知道为什么虚拟函式可以做到动态连结。为了达到动态连结的目的,C++编译器透过某个表格,在执行时期间接呼叫实际上连结的函式。这样的表格称为虚拟函式表(vtable)。每一个内含虚拟函式的类别,编译器都会为它做出一个虚拟函式表,表中的每一个元素都指向一个虚拟函式的位址。此外,编译器当然也会为类别加上一向成员函数,是一个指向该虚拟函式表的指標(vptr)。

 

2 例子说明

Class1基类
class Class1 {
  public :
         int m_data1;
        int m_data2;

        memfunc();
        virtual void vfun1();
        virtual void vfun2();
        virtual void vfun3();
};

则Class1物件实体在记忆中占据这样的空间:

C++虚拟函式实现方式 [大三TJB_708]_第1张图片

图1.CMyClass1物件实体占据空间图

由图1.可知Class1物件实体内的数据分两个地方存储,第一个是存储vptr + m_data1 + m_data2成员变量之地;第二个数储存虚拟函数Class1::vfunc1、Class1::vfunc2、Class1::vfunc3和成员函数Class1::memfunc()之地,当然就单成员函数来说,衍生类中的成员函数跟基类所的成员函数都是一个地址的,衍生类使用父类的成员函数都是采用调用形式(this指標)。对于成员变量,在内存中会增加一个vptr变量,作用是指向虚拟函式表(vtable)的首地址,同时也是整个类物件实体的首地址。而虚拟函式表(vtable)中存的是指向每个虚拟函式的函数指针,指向存在其它地方的虚拟函式,虚拟函式跟成员函数存储在一起。

 

3 衍生类

衍生类Class2

每一个由基类衍生出来的物件,都有这么一个vptr。当我们透过这个物件呼叫虚拟函式,事实上是透过vptr找到虚拟函式表,再找出虚拟函式的真正地址。奥妙自傲与这个虚拟函式表以及这种间接呼叫方式。虚拟函式表的内容是依据类别中的虚拟函式宣告次序,一一填入函数指標。衍生类别会继承基础类别的虚拟函式表(以及其他可以继承的成员),当我们在衍生类别中改写虚拟函式时,虚拟函式表就收到了影响,表中元素所指的函式位址将不再是基础类别中函数位址,而是衍生类别中中虚拟函式的位址。

class Class2 : public Class1{
  public:
	int m_data3;
	memfunc();
	virtual void vfunc2();
};

则CMyClass2物件实体在记忆中占据这样的空间:

图2.衍生类CMyClass2物件实体记忆空间分配

由图2可知,在成员变量存储区多了m_data3变量,在虚拟函式表中,(*vfunc2)所指向的虚拟函式为Class2,成员函数menmfunc()也属于Class2物体实体的了。这跟指標只跟定义指標的类相同一致。

 

衍生类Class3
class Class3 : public Class2{
  public:
	int m_data1;
	int m_data4;
	memfunc();
	virtual void vfunc2();
};

则Class3物件实体的记忆空间会区分Class1::m_data1和m_data1(Class3::m_data1),所以在数据变量存储区域存储的数据依次为:vptr, Class1::m_data1,m_data2, m_data3, m_data1, m_data4,虚拟函式表中所指向的虚拟函式跟Class2中一样都是指向Class3的虚拟函式,值得一提的是vfunc()实质是指Class1::vfunc1()而不是Class2::vfunc1()。成员函数是Class3型的。

 

 

此次笔记记录完毕。

你可能感兴趣的:(C++虚拟函式实现方式 [大三TJB_708])