什么是多态?
父类指针即根据指向的不同对象,响应同一消息(函数调用),产生不同行为。
多态三要素?
1,继承
2,虚函数重写
3,父类指针(引用)指向子类对象
多态的实现很简答,让我们来看一段代码
#include
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指针,那么我们能找到它吗?
咱们一起来探究下:首先看下加了虚函数的类的大小有没有变化。
可以看到加了虚函数,类的大小比没有增加虚函数的类,多了四个字节的空间,有的同学可能会说,四个字节的类型不一定是指针。不要着急,让我们继续往下看。
接下来我们定义对象,然后通过调试,看下局部变量窗口
从这里就可以明确看到,子类对象中有一个vptr指针,而且它是对象的第一个成员,它的类型是void**,指向的是一个顺序表,下标为0的元素装的是我们声明的虚函数。
那么,知道了这些,咱们能利用对象找到虚函数表,然后自己手动调用虚函数吗?
你们:肯定可以啊,废话
我:。。。那就废话不多说,欧力给!搞起
我:首先画一张内存模型图,瞅瞅(画工太丑,见谅)
1,首先,要拿到vptr指针,怎么拿呢?因为它在对象的第一个元素,所以我们先对对象取地址&ch,这样就拿到了对象的地址。对象的元素的内存是连续的,但是现在指针的步长是Child类的大小,我们需要把它当成一个整型数组(因为vptr是四个字节),所以需要强转成int*,即(int*)&ch,这样之后数组第一个元素就是vptr指针了,取值即可得到
(int )&ch
2,然后,前面通过调试我们知道了,vptr指针是void**类型的,所以我们也要讲它转为int*,然后取值. (int )( (int )&ch),这样就拿到了虚函数表的第一个元素。
3,但是,现在拿到的元素是int*型,不是函数指针,无法调用,所以我们需要强转为函数指针,才能进行调用。
你学废了没?嘿嘿
全部0条评论
快来发表一下你的评论吧 !