虚表
上一章了解了多态,那么我们来了解一下多态在C++中是如何实现的。
了解本质,那就通过反汇编代码去看就行了,首先我们看下非多态的情况下的反汇编代码:
然后再来看下多态情况下的反汇编代码:
很明显这里多态的情况下会根据edx间接调用,而非多态则会直接调用。
那么我们来看下间接调用的流程是什么:
-
ebp+8地址对应的值给到eax(ebp+8 也就是函数的参数 → 当前参数指针【父类指针】)
-
eax地址对应的值给到edx(eax相当于当前对象的第一个成员)
-
调用edx地址对应的值,也就是子类对象的Print函数
但是这里很奇怪,第一个成员为什么就能是Print函数呢?跟我们之前理解的4个字节的参数完全不一样。
那么编译器到底是做了什么工作,才能根据我们传入的对象来进行间接调用的呢?这是因为虚表。
只要有虚函数,不论多少个,对象的数据宽度就会比其原来多出4个字节,这四个字节我们称之为虚表。
那么虚表在哪呢?可以通过VC6来寻找虚标,先创建对象然后下断点运行查看,如下图中,可以很清晰的看见对象t除了继承Person父类的Age、Sex以及本身的Level成员外,还有一个__vfptr,上面有一个地址就是0x00422024,那这个地址就是虚表,这个表里面存储的就是函数的地址:
我们可以调出内存窗口查看一下:
这个存储的地址就是0x00401037,这时候切到反汇编代码就然后Ctrl+G输入跟进这个地址:
那这个地址就是Teacher的成员函数Print的地址。
虚表的结构:虚表中存储的都是函数地址,每个地址占用4个字节,有几个虚函数,则就有几个地址。
子类没有重写时,虚表中则只有父类自己的成员函数地址,反之,当子类重写虚函数时候,虚表中则存在父类自己的成员函数地址与子类重写的成员函数地址。
纯虚函数
之前学习过虚函数,也提到了纯虚函数,虽然纯虚函数语法很简单的,但是其比较难理解,所以在有一定的面向对象的基础时候再来学习会比较容易理解一些。
纯虚函数语法:
-
将成员函数声明为 virtual
-
该函数没有函数体,最后跟=0
class
Base {
public
:
virtual
int
Plus() =
0
;
}
语法不过多的阐述,之前也有写过;接下来我们要了解一个新的概念:抽象类。
抽象类有这几种特征:
-
含有纯虚函数的类,称之为抽象类;
-
抽象类也可以包含普通的函数;
-
抽象类不能实例化(创建对象)。
那么问题来了,抽象类有什么意义呢?我们可以把抽象类看作是对子类的一种约束,或者认为其(抽象类)就是定义一种标准。
比如:淘宝,有很多店铺,虽然每个店铺卖的东西都不一样,但是他们同样都可以下单、评论、购物车,也就是说他们都遵守了这种标准规则;也就是说你可以把淘宝当作一个抽象类,其有很多成员:购物车、评论、商品展示区...但是他都没有定义,而是交给开淘宝店的人(子类)去根据标准规则定义。
而如果不按照这种标准呢来,那么假如要统计所有的数据就会非常麻烦,不便于管理。
标签:虚表,函数,子类,多态,C++,地址,抽象类 From: https://www.cnblogs.com/bonelee/p/17299993.html