原创 Maye426 C语言Plus 2020-09-01

什么是多态?

 父类指针即根据指向的不同对象,响应同一消息(函数调用),产生不同行为。


多态三要素?

1,继承

2,虚函数重写

3,父类指针(引用)指向子类对象


多态的实现很简答,让我们来看一段代码

#include<iostream>using namespace std;class Parent{public:  virtual void show(){    cout << "我是你爹" << endl;  }};class Child:public Parent//1,继承{public:  virtual void show()//2,虚函数重写{    cout << "我是你崽" << endl;  }};
int main(){  Parent *pa = new Child;//3,父类指针指向子类对象  pa->show();
 getchar();  return 0;}//结果输出的是子类的show函数--"我是你崽"


实现很简单,但是这又是什么原理呢?


当我们在类中声明了虚函数之后,编译器会给类添加一个vptr指针,当定义对象的时候,会把所有虚函数放入一个叫虚函数表的顺序表,然后用vptr指针指向虚函数表。当进行pa->show();调用的时候,C++编译器不需要区分子类或者父类对象,只需要pa指针中,找到vptr指针即可。


如果对象类型是子类,就调用子类的函数;如果对象类型是父类,就调用父类的函数,(即指向父类调父类,指向子类调子类)此为多态的表现。


既然类里面有vptr指针,那么我们能找到它吗?


咱们一起来探究下:首先看下加了虚函数的类的大小有没有变化。


C++ 多态的实现及原理,深挖vptr指针,手动调用虚函数_C语言

可以看到加了虚函数,类的大小比没有增加虚函数的类,多了四个字节的空间,有的同学可能会说,四个字节的类型不一定是指针。不要着急,让我们继续往下看。


接下来我们定义对象,然后通过调试,看下局部变量窗口

C++ 多态的实现及原理,深挖vptr指针,手动调用虚函数_C语言_02

从这里就可以明确看到,子类对象中有一个vptr指针,而且它是对象的第一个成员,它的类型是void**,指向的是一个顺序表,下标为0的元素装的是我们声明的虚函数。


那么,知道了这些,咱们能利用对象找到虚函数表,然后自己手动调用虚函数吗?


你们:肯定可以啊,废话


我:。。。那就废话不多说,欧力给!搞起

我:首先画一张内存模型图,瞅瞅(画工太丑,见谅)

C++ 多态的实现及原理,深挖vptr指针,手动调用虚函数_C语言_03


1,首先,要拿到vptr指针,怎么拿呢?因为它在对象的第一个元素,所以我们先对对象取地址&ch,这样就拿到了对象的地址。对象的元素的内存是连续的,但是现在指针的步长是Child类的大小,我们需要把它当成一个整型数组(因为vptr是四个字节),所以需要强转成int*,即(int*)&ch,这样之后数组第一个元素就是vptr指针了,取值即可得到

*(int*)&ch

2,然后,前面通过调试我们知道了,vptr指针是void**类型的,所以我们也要讲它转为int*,然后取值.*(int*)(*(int*)&ch),这样就拿到了虚函数表的第一个元素。

3,但是,现在拿到的元素是int*型,不是函数指针,无法调用,所以我们需要强转为函数指针,才能进行调用。

C++ 多态的实现及原理,深挖vptr指针,手动调用虚函数_C语言_04


你学废了没?嘿嘿