一个关于基类和派生类指针的问题
直接上程序吧:
#include <iostream>using namespace std;//没有使用虚函数的继承派生关系class Base{public: Base(int i = 0):ival(i){} void getVal() { cout<<ival<<endl; }private: int ival;};class Derived:public Base{public: Derived(int i = 0, int j = 1):Base(i),ival(j){} void getVal() { cout<<ival<<endl; }private: int ival;};//使用了虚函数的继承派生关系class Base1{public: Base1(int i = 0):ival(i){} virtual void getVal() { cout<<ival<<endl; }private: int ival;};class Derived1:public Base1{public: Derived1(int i = 0, int j = 1):Base1(i),ival(j){} void getVal() { cout<<ival<<endl; }private: int ival;};void useBaseObj(Base b){ b.getVal();}void useDerivedObj(Derived d){ d.getVal();}void useBasePtr(Base *pb){ pb->getVal();}void useDerivedPtr(Derived *pd){ pd->getVal();}void useBase1Obj(Base1 b){ b.getVal();}void useDerived1Obj(Derived1 d){ d.getVal();}void useBase1Ptr(Base1 *pb){ pb->getVal();}void useDerived1Ptr(Derived1 *pd){ pd->getVal();}int main(){ Base b; Derived d; Base *pb = &b; Derived *pd = &d; useBaseObj(b);//基类实参,基类形参,调用基类函数 useDerivedObj(d);//派生类实参,基类形参,自动转化,调用基类函数// useDerivedObj((Derived)b);//无法用基类(自动的)构造出一个派生类 useDerivedObj(d);//派生类实参,派生类形参,调用派生类函数 cout<<endl; useBasePtr(pb);//指向基类形参,指向基类实参,调用基类函数 useBasePtr(pd);//指向基类形参,指向派生类实参,调用静态类型-基类函数// useDerivedPtr(pb);//指向基类实参,指向派生类形参,无法自动转化 useDerivedPtr((Derived*)pb);//强制类型转化,打印结果为随机数 //为什么? useDerivedPtr(pd);//指向派生类实参,指向派生类形参,调用派生类 cout<<endl; pb = new Derived;//静态类型为指向基类,动态类型为指向派生类 useBasePtr(pb);//调用基类 useDerivedPtr((Derived*)pb);//调用派生类 cout<<endl; Base1 b1; Derived1 d1; useBase1Obj(b1);//基类实参-基类形参,调用基类函数 useBase1Obj(d1);//派生类实参-基类形参,发生派生类到基类的转化,调用基类// useDerivedObj((Derived)b);//基类实参,派生类形参,无法类型转化,函数报错 useDerived1Obj(d1);//派生类实参-派生类形参,调用派生类函数 cout<<endl; Base1 *pb1 = &b1; Derived1 *pd1 = &d1; useBase1Ptr(pb1);//指向基类的实参,指向基类的形参,调用基类函数 useBase1Ptr(pd1);//指向派生类实参,指向基类的形参,根据多态,调用派生类// useDerivedPtr(pb1);//指向基类的实参,指向派生类形参,不匹配 useDerived1Ptr((Derived1*)pb1);//通过强制类型转换使其匹配,调用基类函数 //为什么? useDerived1Ptr(pd1);//指向派生类的实参,指向派生类的形参,调用派生类 cout<<endl; pb1 = new Derived1;//形参静态类型为基类,动态类型为派生类 useBase1Ptr(pb1);//根据多态,调用派生类 useDerived1Ptr((Derived1*)pb1);//直接调用派生类 return 0;}
Base1* pb1 = &b1;
Derived1* pd1 = &d1;
调用及中间翻译为:
b1.getVal() --> Base1::getVal(&b1);
d1.getVal() --> Derived1::getVal(&d1);
pb1->getVal() --> (*pb1->vtable[0])(pb1); // 这个是使用函数指针进行函数调用的语法
pd1->getVal() --> (*pd1->vtable[0])(pd1);
基于对象的方法调用和前面还是一样的,没有任何区别。而有趣的地方在于使用指针进行方法调用。如果对象拥有虚函数表,且被调方法是虚方法。则编译器会从虚函数表中取出该方法的地址,而不是直接确定。
虽然你的代码不正确,但最有趣的地方也可由它来说明:
Derived1* pd1 = (Derived1*)pb1;
pd1->getVal(); --> (*pd1->vtable[0])(pd1);
这和上面的中间翻译没有区别,正是这样,才使得行为得以保证。
即使指针类型进行了强制转换,但编译器看到的只是一个“整数”,该整数意味着一个内存地址。此外,它的类型是Derived1对象的指针。但pb1所指向的内存没有发生任何改变,也就是说,它的虚函数表没有发生变化,依然指向Base1的虚函数表。所以当执行被转换后的代码时,所执行的依然是Base1的getVal()方法。
[解决办法]
useDerivedPtr((Derived*)pb);//强制类型转化,打印结果为随机数
//为什么?
这里的 pb 是 Base 类的指针, Derived 是 Base 类的子类,由基类强制转换成子类,那么子类的数据会丢失。
应为基类没有包含子类的数据
useDerived1Ptr((Derived1*)pb1);//通过强制类型转换使其匹配,调用基类函数
//为什么?
这里则反之,子类是包含有基类的数据的,所以强制转换后数据不会丢失
[解决办法]
不能不基类的对象或指向基类的指针,因为基类中的成员并不包含派生类的,由基类转换到派生类之后,转换出来的对象访问派生类中特有的成员的时候,但其转换来的对象里面并不包含这些成员函数的值,所以访问会出现问题,当然可以用一种动态转换,但这种转换时把基类类型的指针或引用指向派生类,可以把这种基类型的指针或引用转化为派生类的。
例如:
base b;
base *pb;
derive d;
pb=&d;
derive *pd;
pd=dynamic_cast<derive *>(pb);
像这种转换是可以实现的!