先上一段代码:
#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);//基类实参,基类形参,调用基类函数
useBaseObj(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);//形参类型为指向派生类指针,实参静态类型为指向基类的指针
//但是由于内存中的确有这个数(因为我们实际new的是一个派生类)
//所以结果为派生类函数
cout<<endl;
Base1 b1;
Derived1 d1;
useBase1Obj(b1);//基类实参-基类形参,调用基类函数
useBase1Obj(d1);//派生类实参-基类形参,发生派生类到基类的转化,调用基类
// useDerivedObj((Derived)b);//基类实参,派生类形参,无法类型转化,函数报错
useDerived1Obj(d1);//派生类实参-派生类形参,调用派生类函数
cout<<endl;
Base1 *pb1 = &b1; //b1的实际类型为基类,它的虚函数表指明了如果通过指针或者引用调用自己,使用基类的方法
Derived1 *pd1 = &d1;//d1的实际类型为派生类,它的虚函数表指明了如果通过指针或者引用调用自己,使用派生类方法
useBase1Ptr(pb1); //通过虚函数表查得,对于pb1应该调用基类的方法
useBase1Ptr(pd1); //通过虚函数表查得,对于pd1应该调用基类的方法
// useDerivedPtr(pb1); //指向基类的实参,指向派生类形参,不匹配
useDerived1Ptr((Derived1*)pb1); //通过强制类型转换使其匹配,调用基类函数
//强制类型转化并没有修改虚函数表的内容,所以还是调用基类的方法
useDerived1Ptr(pd1);//通过虚函数表查得,调用派生类的方法
cout<<endl;
pb1 = new Derived1; //形参静态类型为基类,动态类型为派生类,虚函数表中指明了遇见它调用派生类函数
useBase1Ptr(pb1); //使用派生类方法
useDerived1Ptr((Derived1*)pb1);//使用派生类方法
return 0;
}
大部分内容其中注释都说清楚了,下面总结如下:
1.对于基类与派生类对象之间:派生类对象可以用来当做基类对象使用,完成基类的功能,不需要类型转化。反之则不成立,需要一个派生类对象的地方,不能填入一个基类对象。
2.对于指针和引用,分为两种情况:
(a)没有虚函数:此时,调用基类还是派生类完全取决于函数形参的静态类型。如果形参与实参类型不符,需要类型转化:派生类指针转化为基类指针,由编译器自动完成;而基类指针转化为派生类,则必须强制完成。其中后者可能是不安全的。比如我们程序中的那个打印结果为随机数。因为这里试图访问一个基类中并不存在的内容。通常,如果使用C++的类型转化操作:dynamic_cast可以解决这个问题,如果从基类向派生类转化,则会转化失败,dynamic_cast会返回一个空指针;而对应的引用操作则会抛出异常。
(2)有虚函数:此时,函数的调用是通过虚函数表来完成的。每次通过指针或者引用调用它时,都调用的是它实际类型的函数。如果形参与实参类型不符,可以通过强制类型转化来完成匹配,但即使强制类型转化后,并没有改变虚函数表里面的内容,所以不管你如何转化,都调用的它实际指向对象的那个函数。
来源:http://blog.csdn.net/thefutureisour/article/details/8123203