下面我们说多态的实现原理。
这个多态嘛,它的关键在于,啊,通过基类指针或者引用调用一个虚函数的时候,
编译的时候你没有办法一口咬定说这个,嗯, 虚函数到底是哪一个类的。
只有在运行到那条语句的时候才能确定,嗯,这个 韩式到底是属于哪一个类的。那这种情况叫做
动态联编。啊,它跟多态说得上是一回事儿了。拿这个很神奇的动态联编,到底是怎么实现的呢?
嗯,我们先看一个程序。
引发大家一点思考,啊。嗯,在这个程序里面有个基类base,嗯, 它有一个,嗯
他有一个成员变量 i。然後它有一个虚函数输出BasePrint。
然後在Derieved的类里面呢, 也有一个成员变量n。这个Derieved 类是从base派生而来的,它的Print输出
Derieved的Print。然后在main里面我们定义了一个派生类的对象d,
嗯, 然後就没什么用了。然後,输出了size of
base 和size of derieved,啊。
当你说嘛,这size of base
它应该是四个字节,对吧?它只有一个整型成员变量。那size of derieved应该是八个字节,啊。
这是我们前面所学的知识。那实际上这个程序输出了的结果是8和12。
也就是说吧,为什么 这些对象都多了四个字节出来了?看上去是很奇怪吧?
啊,那多出来的4个字节它肯定是有用的。
具体做什么用呢?这就牵涉到多态实现的关键-虚函数表。
就是说,嗯,在C++里面每一个有虚函数的类,
那一个类如果没有虚函数但它的基类有虚函数,那它也算是有虚函数的类啊。
总之吧,每一个有虚函数的类都有一张虚函数表跟它对应。
这个虚函数表,是编译器自动生成的
自动生成的,加到你这个可执行文件里面去的。当你的可执行程序被装入内存的时候
和一个类所对应的虚函数表,也就被装到内存里面去了。
然後 有虚函数的类的任何对象中都会,都会存放着
这个类所对应的内阁虚函数表的地址。
然後虚函数表里面放着什么东西呢?
啊,虚函数表里面放着这张表所对应的类的所有虚函数的内存里面的地址。
那实际上我们前面看到,诶,那些包含虚函数的类,它的事实上
结果比我们想象的要多了四个字节。那多出来的字节是什么东西呢?
多出来的四个字节,实际上就是用来存放这个类所对应的虚函数表的地址了。
那我们看,一个base的对象b 它的前四个字节存放的就是base类所对应的
虚函数表的指针。那接下来才开始存放自己的成员变量i。
那这个虚函,通过虚函数表指针就能够找到base类所对应的虚函数
虚函数表,这个虚函数表是是在内存里面的。这base类的虚函数表里面放的些什么东西呢?
放了的就是base类的所有虚函数在内存里面的地址。
那通过这张表 有了虚函数的名字就能够查到这个虚函数在内存里的地址了。
有了虚函数在内存里面的地址,当然就能够调用这个虚函数了。
调用一个函数本来就是跳到那个函数的地址上去执行嘛! 当然你还是得再跳回来。
那融延制虚函数表是可以通过 虚函数的名字,或者说虚函数的代号,或者是什么东西来进行查找的。
总之,有了这张表,你就能够查到一个虚函数在内存里面的地址。
注意,每一个类 只要是包含虚函数的类它都有一张虚函数表跟它对应。
那假设这里有一个Derieved类的对象,那它这个对象前面的四个字节放着的也是
Derieved的类的虚函数表的指针。接下来才存放 Derieved类的成员变量。
前面是从基类继承下来的i,后面是它自己的n。
好, 那通过前面的四个字节就能够找到Derived类所对应的这个虚函数表。
这个虚函数表里面放的什么东西呢?当然就放着的Derived类的
全部虚函数的内存地址。啊,它存放了 Derieved里面的Print的这个虚函数的内存地址。
那我们查这张表就能够得到Derieved类的所有虚函数的内存地址。
那有了内存地址之就能调用虚函数。嗯,那现在
多态是怎么实现的呢?假设我们用一个pBase的指针指向
指向某个对象。这个对象它有可能是,是,是,是基类的对象,也有可能是这个派生类的对象。
那这样一条多态语句
那么基类指针调用虚函数,这台,这条多态行状的语句
被编辑处理了以后它会生成一大堆的指令。这些指令并不是简简单单的
就跳转到某个函数的地址去执行那个函数。啊,这个一系列指令 首先
它做的事情就是根据基类指针所指向的,嗯,或者说基类引用
所引用的那个对象中存在的虚函,对象中存放的虚函数表的地址。
啊,这对象里面的全四个字节就是虚函数表的地址。
然后有一个指着指着它,指着一个对象,或者引用在引用那个对象。
那总而言之我们通过这个对象的前四个字节,就能够找到这个对象 所属的类对应的那张虚函数表。
然後,我们再在这个虚函数表中去查找虚函数的地址。
找到虚函数的地址以后,就去调用哪个虚函数。
啊,就是对于一条多态语句,编译器编译出来的指令
执行过程会比较复杂的这么一大套。那当然了, 在这个,这一切指令执行的过程中,
这些对象里面放着的是哪一个类的虚函数表的指针?
最后终究会跑到哪一个类的虚函数表里面去查找虚函数的地址?
那最终被调用的又是哪一个类的虚函数?嗯,这也是多态实现的这个 原理。
那,知道这个原理,你对这个 面向对象对多态什么的理解就会更加深入了一层,啊。
多态好难讲啊!