关于C++ 多态实现技术的深度解析(vfptr,vftable)

#PS:要转载请注明出处,本人版权所有

#PS:这个只是 《 我自己 》理解,如果和你的

#原则相冲突,请谅解,勿喷

前置说明:
关于C++的多态,我就不多说了,我理解的一句话就是:一个接口,多种实现(简称:一对多)。在C++里面,多态的实现方法就是继承,简单来说:就是基类提供接口,子类可以实现基类的接口,不同的子类就构成了基类接口的不同的实现。以上就是C++课本上或者C++原理上的常见的知识。

而今天这里所说明的是多态是怎么实现的,以及实现的详细方法。我个人认为:只有了解了这些东西后,我们才不会在多态中迷失自己的方向,导致代码稀烂。

多态的实现

多态的实现是通过一个指针数组实现的。此指针数组的一般声明如下:

//虚函数表
void ** vfptr;//此ptr指向一个void * 的数组

说明: vfptr 变量的声明是C++编译器根据:当一个带有虚函数的类定义了一个实例时,编译器会自动在此类的前4bytes(32bit 系统)添加vfptr变量来实现多态。同时编译器会生成一个void *数组来存放实际接口方法地址(其实这里就已经实现了多态,也就是说:这里就已经定义好了这个实例可以调用哪些实际接口方法,例如是来至于基类还是子类等等),也就是虚函数表(vftable),此表是放在生成的可执行文件的数据段。

总结:这里有一些基本的习惯。
vfptr放在一个类的起始相关地址。
vftable中的方法按照继承顺序、接口声明顺序赋值。

以上都是自己瞎扯的原理,看不懂也没有啥问题,毕竟我的语文水平不高,语言组织能力不行,直接跳过查看下面的实例分析。

多态的实现原理解析实例

class Base
{
public:
    Base() {}
    ~Base() {}
    virtual void base1(void) = 0;
    virtual void base2(void) {};
    virtual void base3(void) {};
    int base_a = 0;
private:

};
class Base1
{
public:
    Base1() {}
    ~Base1() {}
    virtual void base2(void) {};
    virtual void base3(void) {};
    int base_a = 0;
private:

};

class Derive:public Base
{
public:
    Derive() {}
    ~Derive() {}
    virtual void base1(void) {}
    virtual void base2(void) {}
    int derive_a = 1;
private:

};
class Derive1 :public Base
{
public:
    Derive1() {}
    ~Derive1() {}
    virtual void base1(void) {}
    virtual void base2(void) {}
    virtual void derive1(void) {}
    virtual void derive2(void) {}
    int derive_a = 2;
private:

};

class Derive2:public Derive1
{
public:
    Derive2() {}
    ~Derive2() {}
    virtual void derive1(void) {}

private:

};

int main()
{
    Base1 b1;
    Derive d1;
    Derive1 dd1;
    Derive2 ddd1;
    return 0;
}

以下是vs2015的调试分析截图:(若只想了解一下vfptr和vftable是个什么样子,只看此图并结合代码分析足以)
这里写图片描述

友情提示:此图中关于ddd1的vfptr和我们想要的结果是不同的,原因和调试器相关,具体关于ddd1哪里有问题,若有需求,请看以下分析。

多态的实现原理解析实例(进阶版)

同上一份代码,我们深入到汇编里面,就可以发现一些我们不知道的细节,关于这些汇编中的其他内容部分,若想了解,参考此文章:https://blog.csdn.net/u011728480/article/details/79092194
这里我们只分析变量ddd1的生成过程。
用ida载入我们生成的exe文件。找到main函数中ddd1初始化入口如下:
这里写图片描述

这里我们跳转到call后面的地址查看Derive2的构造函数,先调用Derive1的构造函数,再给vfptr赋值。下图是构造函数汇编
这里写图片描述
下图是vfptr的赋值内容,也就是vftable是什么样子的
这里写图片描述
这里可以看到,其实此实例调用的是哪些方法(也就实现了多态),早已经在编译时就确定了,只是这里才赋值而已,所以可以说是动态绑定或者静态绑定。
下图为Derive1构造函数以及vftable值:
这里写图片描述
这里写图片描述

下图为Base构造函数以及vftable的值:
这里写图片描述
这里写图片描述

以上总结:
我们可以发现vfptr经过了多次赋值的,不同的时间段,vfptr值不一样,所以在构造实例的时候,不同时段调用同一个接口,会有不同的方法。

同时也可以得出一个结论,每个接口的实现类都有一个虚函数表,包括接口类本身,这些虚函数表在编译时就确定了,只是vfptr赋值的时候,是在程序运行时确定的。
根据以上的分析,可以解决许多我们关于多态的疑问。
#PS:请尊重原创,不喜勿喷

#PS:要转载请注明出处,本人版权所有.

有问题请留言,看到后我会第一时间回复

猜你喜欢

转载自blog.csdn.net/u011728480/article/details/79812780