c++中虚多态的实现机制
参考博客:http://blog.csdn.net/neiloid/article/details/6934135
- 序言
- 证明vptr指针存在
- 无继承
- 单继承无覆盖
- 单继承有覆盖
- 多继承有覆盖
- 总结
1.序言
这篇博文探讨c++内部多态的实现机制,参考书主要是《深入探索c++对象模型》,由于本人水平有限,望读者指正。
实现多态有3点前提,即继承,虚化,基类指针指向派生类对象。
本质而言,总体来说,当类的内部通过virtual关键字声明虚函数时,编译器生成vptr指针,指向虚函数表(虚表),虚表中依次存放着各虚函数(非虚函数不在这里)。这样,就可以通过指针来调用这些函数。
PS:运行环境为VC++6.0,由于编译器的差异而产生的不同本文不讨论。
2.证明vptr指针的存在
#include<iostream>
using namespace std;
class Base
{
public:
virtual void f(){cout<<"Base::f()"<<endl;}
virtual void g(){cout<<"Base::g()"<<endl;}
virtual void h(){cout<<"Base::h()"<<endl;}
};
int main (void)
{
Base b;
cout<<"证明vptr的存在"<<endl;
cout<<"sizeof b = " <<sizeof b<<endl;
return 0;
}
ps:linux下 sizeof b = 8
3无继承
#include<iostream>
using namespace std;
class Base
{
public:
virtual void f(){cout<<"Base::f()"<<endl;}
virtual void g(){cout<<"Base::g()"<<endl;}
virtual void h(){cout<<"Base::h()"<<endl;}
};
int main (void)
{
typedef unsigned long P_FUN;//指针类型为无符号长整型
typedef void(*FUN)(void);//声明函数指针
FUN f,g,h;
Base b;
f=(FUN)(*(P_FUN*)*(P_FUN*)&b);
g=(FUN)(*((P_FUN*)*(P_FUN*)&b+1));
h=(FUN)(*((P_FUN*)*(P_FUN*)&b+2));
f();
g();
h();
cout<<(FUN)(*((P_FUN*)*(P_FUN*)&b+3))<<endl;
return 0;
}
这样就通过指针的方式调用了续表中的方法函数。
通过现象,可以大概估计出内存的布局,如下图。
结束位置以后可能为操作系统内部的只读空间,访问会发生段错误。
ps:linux下结束位置可能为0,可能为1,有兴趣的读着可以研究一下。
4.单继承无覆盖
#include<iostream>
using namespace std;
class Base
{
public:
virtual void f(){cout<<"Base::f()"<<endl;}
virtual void g(){cout<<"Base::g()"<<endl;}
virtual void h(){cout<<"Base::h()"<<endl;}
};
class Derive:public Base
{
public:
virtual void f1(){cout<<"Derive::f1()"<<endl;}
virtual void g1(){cout<<"Derive::g1()"<<endl;}
virtual void h1(){cout<<"Derive::h1()"<<endl;}
};
int main (void)
{
typedef unsigned long P_FUN;
typedef void(*FUN)(void);
FUN f,g,h,f1,g1,h1;
Derive b;
f=(FUN)(*(P_FUN*)*(P_FUN*)&b);
g=(FUN)(*((P_FUN*)*(P_FUN*)&b+1));
h=(FUN)(*((P_FUN*)*(P_FUN*)&b+2));
f1=(FUN)(*((P_FUN*)*(P_FUN*)&b+3));
g1=(FUN)(*((P_FUN*)*(P_FUN*)&b+4));
h1=(FUN)(*((P_FUN*)*(P_FUN*)&b+5));
f();
g();
h();
f1();
g1();
h1();
cout<<(FUN)(*((P_FUN*)*(P_FUN*)&b+6))<<endl;
return 0;
}
根据结果可以看出,无覆盖的情况下,派生类的虚函数直接放在第一张虚表后边。
5.单继承有覆盖
#include<iostream>
using namespace std;
class Base
{
public:
virtual void f(){cout<<"Base::f()"<<endl;}
virtual void g(){cout<<"Base::g()"<<endl;}
virtual void h(){cout<<"Base::h()"<<endl;}
};
class Derive:public Base
{
public:
virtual void f(){cout<<"Derive::f1()"<<endl;}
virtual void g1(){cout<<"Derive::g1()"<<endl;}
virtual void h1(){cout<<"Derive::h1()"<<endl;}
};
int main (void)
{
typedef unsigned long P_FUN;
typedef void(*FUN)(void);
FUN f,g,h,f1,g1,h1;
Derive b;
f1=(FUN)(*(P_FUN*)*(P_FUN*)&b);
g=(FUN)(*((P_FUN*)*(P_FUN*)&b+1));
h=(FUN)(*((P_FUN*)*(P_FUN*)&b+2));
g1=(FUN)(*((P_FUN*)*(P_FUN*)&b+3));
h1=(FUN)(*((P_FUN*)*(P_FUN*)&b+4));
f1();
g();
h();
g1();
h1();
cout<<(FUN)(*((P_FUN*)*(P_FUN*)&b+5))<<endl;
return 0;
}
根据运行结果可以看出,有覆盖的情况下,派生类虚函数覆盖到基类的虚函数,不覆盖的虚函数依次放在第一张表的后面。
6多继承有覆盖
#include<iostream>
using namespace std;
class Base1
{
private:
virtual void f(){cout<<"Base1::f()"<<endl;}
virtual void g(){cout<<"Base1::g()"<<endl;}
virtual void h(){cout<<"Base1::h()"<<endl;}
};
class Base2
{
public:
virtual void f(){cout<<"Base2::f()"<<endl;}
virtual void g(){cout<<"Base2::g()"<<endl;}
virtual void h(){cout<<"Base2::h()"<<endl;}
};
class Derive:public Base1,public Base2
{
public:
virtual void f(){cout<<"Derive::f1()"<<endl;}
virtual void g1(){cout<<"Derive::g1()"<<endl;}
virtual void h1(){cout<<"Derive::h1()"<<endl;}
};
int main (void)
{
typedef unsigned long P_FUN;
typedef void(*FUN)(void);
FUN B1f,B1g,B1h,B2f,B2g,B2h,Dg,Dh;
Derive b;
B1f=(FUN)(*(P_FUN*)*((P_FUN*)&b));
B1g=(FUN)(*((P_FUN*)*((P_FUN*)&b)+1));
B1h=(FUN)(*((P_FUN*)*((P_FUN*)&b)+2));
B2f=(FUN)(*(P_FUN*)*((P_FUN*)&b+1));
B2g=(FUN)(*((P_FUN*)*((P_FUN*)&b+1)+1));
B2h=(FUN)(*((P_FUN*)*((P_FUN*)&b+1)+2));
Dh=(FUN)(*((P_FUN*)*((P_FUN*)&b)+3));
Dg=(FUN)(*((P_FUN*)*((P_FUN*)&b)+4));
B1f();
B1g();
B1h();
cout<<endl;
Dh();
Dg();
cout<<endl;
B2f();
B2g();
B2h();
cout<<endl;
cout<<(FUN)(*((P_FUN*)*((P_FUN*)&b)+5))<<endl;
cout<<(FUN)(*((P_FUN*)*((P_FUN*)&b+1)+3))<<endl;
return 0;
}
根据结果可知,多继承下,覆盖的派生类函数会把所有的虚表中相对应的基类虚函数覆盖掉,不覆盖的派生类虚函数只放在第一张虚表的后面,其他虚表后没有。
7.总结
1.当类的内部通过virtual关键字声明虚函数时,编译器生成vptr指针,指向虚函数表(虚表),虚表中依次存放着各虚函数(非虚函数不在其中)。
2.无覆盖的情况下,派生类的虚函数直接放在第一张虚表后边。
3.有覆盖的情况下,派生类虚函数覆盖到基类的虚函数,不覆盖的虚函数依次放在第一张表的后面。
4.多继承下,覆盖的派生类函数会把所有的虚表中相对应的基类虚函数覆盖掉,不覆盖的派生类虚函数只放在第一张虚表的后面,其他虚表后没有。
PS:c++是一门威力十分强大的语言,内部构造复杂,也许有人说这些内容没什么卵用,但本人觉得深入理解一下绝不是坏事,本文阅读的难点在于指针的应用,其实整个c/c++的强大也是得益于指针,希望大家通过本文对c/c++有更深入的理解。
另外说明一下,指针确实是不安全的,例如本文中所有的public关键字删掉之后,通过指针仍然能够调用出类内的私有成员方法。
本人是一个即将大三的本科生,能力有限,博文如有不妥支出望各位读者指正。