您的位置:首页 > 编程语言 > C语言/C++

C++ 基类指针指向派生对象/派生类指针指向基类对象

2014-10-19 20:26 316 查看
源程序:
#include <iostream.h>

class A
{
public:
virtual void F1()
{
cout<<"A1"<<endl;
}
void F2()
{
cout<<"A2"<<endl;
}

};
class B :public A
{
public:
void F1()
{
cout<<"B1"<<endl;
}
void F2()
{
cout<<"B2"<<endl;
}

};
void main(){

A *pa;
B *pb;
B TB;
A TA;

pa = &TB;//基类指针指向派生类对象
pa->F1();
pa->F2();

pb =(B *) &TA;//派生类指针指向基类对象

pb->F1();
pb->F2();

}
以上程序输出结果:
B1
A2
A1
B2
解释一下:
一个类所有的函数都是再code代码区中唯一的存放一份。而数据成员则是每个对象存储一份,并按照声明顺序依次存放。
类A中有了虚函数就会在类的数据成员的最前面添加一个vfptr指针(void** vfptr),这个指针用来指向一个vtable表(一个函数指针数组)(一个类只有一个该表),该表存储着当前类的所有虚函数的地址。这样vfptr就成为了一个类似成员变量的存在。访问虚函数的时候通过vfptr间址找到vtable表,再间址进而找到要调用的函数。这样就在一定程度上摆脱了类型制约。
派生类的虚表:
         基类在定义并实现了自己的虚函数后,在全局内存区域中(可以这么理解),会有一个虚函数表,里面都是已实现的函数的指针。程序运行过程中实例化的所有基类对象,都有一个指向虚函数表的指针,而且这个指针的值是一样的,都指向那个全局内存。因为对于基类的每个实例他们使用的函数代码都是一样的嘛。当你定义了一个派生类,并重新实现了部分虚函数后,由于你实现的虚函数和基类不同,所以必须有另一块全局内存保存派生类的虚函数指针列表。最简单的派生情况下,你的派生类就是在基类的内存块之后又加了些派生类使用的内存,此种最简单的情况下,你的派生类其实可以直接使用基类的那个指向虚函数列表的指针,只是指针的值是不同的而已。它指向不是基类的虚函数列表,而是派生类的虚函数列表。如果你没有定义新的虚函数,那么派生类的虚函数列表和基类布局一样,只是函数地址值不同,否则会在后面增加你新定义的函数的地址。通过以上分析,我感觉如果你的派生类不对基类的虚函数做任何重新实现或补充,理论上可以使用同一个虚函数表,否则不行。
只要vptr的值不同,那么访问函数成员的时候使用的vtable表就不同,就可能访问到不同类的函数成员。B类对象中的vptr指向B类自己的vtable。当B类继承A类的时候,因为A中有虚函数,编译器就自动的给B类添加vfprt指针和vtable表。也可以理解为B类继承来了A类中的那个vptr指针成员。当A类指针指向B类对象时,发生假切割。要知道这个过程只是切掉A类中没有的那些成员,由于vptr是从A类中继承来的,所以这个量仍将保留。而对于vptr的值则不会改变,仍然指向B类的vtable表。所以访问F1函数的时候是通过B类的vtable表去寻址的,自然就是使用子类的函数。当B类的指针指向A类的对象时(当B类存在新增数据成员时可能出错),同理。 而对于普通函数则受类型的制约,(因为没有vptr指针)使用哪个类的指针调用函数,那么所调用的就是那个累的函数。总而言之,普通函数通过对象或指针的类型来找所调用的函数,而虚函数是通过一个指针来找到所要调用的函数的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: