////验证 虚函数的组织结构 ,虚函数如何实现一般化操作 ,强制转换原理 ;
/// 指针的多种用法: 函数指针, 函数指针实现绝对跳转,函数指针数组, 指向一维数组的指针
////(分析 侯俊杰MFC深入浅出+最近指针学习 = 写出验证代码)
#include<iostream.h>
#include<stdio.h>
typedef unsigned int uint ;
class ClassA
{
public:
int m_data1;
int m_data2;
void func1() { cout<<"classA func1"<<endl ;}
void func2() { cout<<"classA func2"<<endl ;}
virtual void vfunc1() { cout<<"classA vfunc1"<<endl ;}
virtual void vfunc2() { cout<<"classA vfunc2"<<endl ;}
};
class ClassB :public ClassA
{
public:
int m_data3;
virtual void vfunc1() { cout<<"classB vfunc1"<<endl ;}
virtual void vfunc3() { cout<<"classB vfunc3"<<endl ;}
} ;
void main(void)
{
cout<<sizeof(ClassA)<<endl; ///12= (1vtableaddr + 2ClassAdata)*4
cout<<sizeof(ClassB)<<endl; ///16= (1vtableaddr + 2ClassAdata +1ClassBdata)*4
ClassA a;
ClassB b ,c;
a.m_data1 = 1 ;
a.m_data2 = 2 ;
b.m_data1 = 3 ;
b.m_data2 = 4;
b.m_data3 = 5;
uint *pClassData;
void (*pfun)(void); ///C实现绝对跳转
void (*pfun2[3])(void) ; ///函数指针数组
uint *pfunadd ;
uint (*pfunadd2)[3]; ///指向一维数组的指针
pClassData = (uint*)&a; ///获得a对象的数据空间
////数据结构为 vfuntableAddr ,m_data1 ,m_data2
cout<<"A "<<*pClassData<<" "<<*(pClassData+1)<<" "<<*(pClassData+2)<<" "<<endl;
pfunadd = (uint *)(*pClassData) ; ////获得vfuntableAddr
////vfunc1 (A) ,vfunc2 (A),不确定
cout<<"A vfunaddr "<<*(pfunadd)<<" "<<*(pfunadd+1)<<" "<<*(pfunadd+2)<<" "<<endl;
/////-----------------------
pClassData = (uint*)&b;////获得b对象的数据空间
////数据结构为 vfuntableAddr ,m_data1 ,m_data2 ,m_data3
cout<<"B "<<*pClassData<<" "<<*(pClassData+1)<<" "<<*(pClassData+2)<<" "<<*(pClassData+3)<<" "<<endl;
pfunadd2 = (uint (*)[3])(*pClassData); /////地址值直接转换为指向数组的指针
////vfunc1 (B) ,vfunc2 (A),不确定
cout<<"B vfunaddr "<<(*pfunadd2)[0]<<" "<<(*pfunadd2)[1]<<" "<<(*pfunadd2)[2]<<" "<<endl;
/////-----------------------
pClassData = (uint*)&c;
cout<<"C "<<*pClassData<<" "<<endl;
pfunadd = (uint *)(*pClassData) ;
///目的:比较b,c虚函数表是否一样,输出结果表明一样
cout<<"------(*pfun)()------"<<endl;
cout<<"C vfunaddr "<<*pfunadd<<" "<<*(pfunadd+1)<<" "<<*(pfunadd+2)<<" "<<endl;
/////----------------------
pfun = (void(*)())(*pfunadd); ///测试classdata第一个字存储虚函数表入口地址 则取表中值就是第一个虚函数执行地址
(*pfun)(); ///测试结果表明该处运行的是ClassB 的 vfunc1();
pfun = (void(*)())(*(pfunadd+1)); //ClassA 的 vfunc2();
(*pfun)();
pfun = (void(*)())(*(pfunadd+2)); //ClassB 的 vfunc3();
(*pfun)();
cout<<"=====(*pfun2[3])()======="<<endl;
pfun2[0] = (void(*)())(*pfunadd);
pfun2[1] = (void(*)())(*(pfunadd+1));
pfun2[2] = (void(*)())(*(pfunadd+2));
(*pfun2[0])();
(*pfun2[1])();
(*pfun2[2])();
cout<<"--------------------"<<endl;
ClassA *p = &b; ////数据空间没有改变,即虚函数表没有改变&&&&& 这是实现一般化操作的关键所在
p->vfunc1();
cout<<"-----强制转换后-------"<<endl;
((ClassA)b).vfunc1(); ////这里是另一个问题。强制转换实际上先完成一个拷贝,再来按转换类型裁剪/延伸数据空间。
cout<<((ClassA)b).m_data1<<endl; ////对象强制转换时,会覆盖原数据空间的虚函数表入口地址(测试结果得出,个人观点),其他不变
}
/*******总结**********************************************************
派生类继承基类时,首先拷贝一份基类的虚函数表,再来重载,增加自己的虚函数
ClassA
vptr ----(*vfunc1)()--- ClassA::vfunc1()
(*vfunc2)()--- ClassA::vfunc2()
ClassB
vptr ----(*vfunc1)()--- ClassB::vfunc1() 虚函数表中定义的函数首地址改变
(*vfunc2)()--- ClassA::vfunc2()
**********************************************************************/