转载请注明出处
blog.yesiare.cn
多态原理
在c++中,多态是通过一张虚函数表(virtual table)来实现的,简称v-table,在这个表中主要是一个类的虚函数地址,通过这张表来实现继承,覆写。
比如,一个父类有一个虚函数,这个虚函数地址放在v-table中,有一个子类继承了这个父类,并且覆写了这个虚函数,是如何复写的呢,由于虚函数表也继承了过来,就用新的虚函数地址将原来表中旧的替换掉,这样一来,通过父类指针指向子类的时候,就可以调用这个子类覆写过的虚函数,如果有不同子类都覆写了这个函数,那么只用父类指针就可以实现调用所有子类覆写过的函数,这就是c++的多态。
虚函数表(v-table)存放在哪
如果一个类包含虚函数,那么在这个类内存空间的开头,就存放着一个指向它虚函数表的指针,这个表类似一个指针数组,存放函数指针。
比如有这样一个类。
class A{
public:
virtual void func1(){cout<<"i am A::func1"<<endl;}
virtual void func2(){cout<<"i am A::func2"<<endl;}
virtual void func3(){cout<<"i am A::func3"<<endl;}
int data;
int data2;
};
那么它在内存中的情况就是这样的
func1 是虚函数表中具体的函数指针 可以看着 * 型
ptovtable 就是指向虚函数表的指针 可以把它看作 ** 型
下面我们如何用代码来证明呢?
#include<iostream>
using namespace std;
class A{
public:
virtual void func1(){cout<<"i am A::func1"<<endl;}
virtual void func2(){cout<<"i am A::func2"<<endl;}
virtual void func3(){cout<<"i am A::func3"<<endl;}
int data;
int data2;
};
int main(){
//首先我们建立一个函数指针类型(无返无参,对应上面类中虚函数)
typedef void (*Pf)(void);
Pf f1,f2,f3;//一会用来放三个虚函数
A a;//生成实例
cout<<"对象的起始地址:"<<&a<<endl; //out 0x69fef4
//接下来要提取出 ptovtable,指针占4个字节(我的编译器是)
//所以如下,先将a的地址转换成int* 这样解引用才能得到4个字节(因为int刚好四字节
//然后将得到的地址转换成int** 看图,ptovtable 可看作**
cout<<"vtable的起始地址: "<<(int **)*(int *)(&a)<<endl;//out 0x4850f8
//现在我们已经得到vtable的起始地址了,下面 要提取第一个函数指针
//将 ** 变成 * 解引用就行,第一个函数指针偏移是0
cout<<"表中第一个位置存放的指针:"<<*((int **)*(int *)(&a)+0)<<endl;//out 0x42d790
//我们得到了一个函数指针,但是别忘了,他现在还是int*型,而函数指针是 void(*)(void)型
//so 再强转一下
f1=(Pf)*((int **)*(int *)(&a)+0);
f1();//out i am A::func1
//其他的不用说了吧,表的结尾存放NULL
cout<<"表中第二个位置存放的指针:"<<*((int **)*(int *)(&a)+1)<<endl;//输出自己看
cout<<"表中第三个位置存放的指针:"<<*((int **)*(int *)(&a)+2)<<endl;//
cout<<"表中第四个位置存放的指针:"<<*((int **)*(int *)(&a)+3)<<endl;//
f2=(Pf)*((int **)*(int *)(&a)+1);
f3=(Pf)*((int **)*(int *)(&a)+2);
f2();
f3();
cout<<"by zsh"<<endl;
return 0;
}
运行情况
呼~ 打完收工
注意平时用不到这个,知道这个东西就好
有什么意义呢,加深理解把
装完b就跑