C++基类与派生类的转换

具体表现在以下几个方面: 派生类对象可以向基类对象赋值。 可以用子类(即公用派生类)对象对其基类对象赋值。如    A a1; //定义基类A对象a1    B b1; //定义类A的公用派生类B的对象b1    a1=b1; //用派生类B对象b1对基类对象a1赋值 在赋值时舍弃派生类自己的成员。

实际上,所谓赋值只是对数据成员赋值,对成员函数不存在赋值问题。请注意: 赋值后不能企图通过对象a1去访问派生类对象b1的成员,因为b1的成员与a1的成员是不同的。

假设age是派生类B中增加的公用数据成员,分析下面的用法:   a1.age=23;//错误,a1中不包含派生类中增加的成员    b1.age=21; //正确,b1中包含派生类中增加的成员 应当注意,子类型关系是单向的、不可逆的。B是A的子类型,不能说A是B的子类型。

只能用子类对象对其基类对象赋值,而不能用基类对象对其子类对象赋值,理由是显然的,因为基类对象不包含派生类的成员,无法对派生类的成员赋值。同理,同一基类的不同派生类对象之间也不能赋值。 派生类对象可以替代基类对象向基类对象的引用进行赋值或初始化。 如已定义了基类A对象a1,可以定义a1的引用变量:    A a1; //定义基类A对象a1    B b1; //定义公用派生类B对象b1    A& r=a1; //定义基类A对象的引用变量r,并用a1对其初始化 这时,引用变量r是a1的别名,r和a1共享同一段存储单元。也可以用子类对象初始化引用变量r,将上面最后一行改为    A& r=b1;//定义基类A对象的引用变量r,并用派生类B对象b1//对其初始化 或者保留上面第3行“A& r=a1;”,而对r重新赋值:    r=b1;//用派生类B对象b1对a1的引用变量r赋值

注意: 此时r并不是b1的别名,也不与b1共享同一段存储单元。它只是b1中基类部分的别名,r与b1中基类部分共享同一段存储单元,r与b1具有相同的起始地址。 如果函数的参数是基类对象或基类对象的引用,相应的实参可以用子类对象。如有一函数 fun: void fun(A& r)//形参是类A的对象的引用变量 {    cout<<r.num<<endl; } //输出该引用变量的数据成员num

函数的形参是类A的对象的引用变量,本来实参应该为A类的对象。由于子类对象与派生类对象赋值兼容,派生类对象能自动转换类型,在调用fun函数时可以用派生类B的对象b1作实参: fun(b1); 输出类B的对象b1的基类数据成员num的值。与前相同,在fun函数中只能输出派生类中基类成员的值。 派生类对象的地址可以赋给指向基类对象的指针变量,也就是说,指向基类对象的指针变量也可以指向派生类对象。 例11.10 定义一个基类Student(学生),再定义Student类的公用派生类Graduate(研究生), 用指向基类对象的指针输出数据。本例主要是说明用指向基类对象的指针指向派生类对象,为了减少程序长度,在每个类中只设很少成员。学生类只设num(学号),name(名字)和score(成绩)3个数据成员,Graduate类只增加一个数据成员pay(工资)。 程序如下: #include <iostream> #include <string> Graduate::Graduate(int n, string nam,float s,float p):Student(n,nam,s),pay(p){ } using namespace std; class Student//声明Student类 {    public :    Student(int, string,float );//声明构造函数    void display( );//声明输出函数    private :    int num;    string name;    float score; }; Student::Student(int n, string nam,float s)  //定义构造函数 {    num=n;    name=nam;    score=s; } void Student::display( )//定义输出函数 {    cout<<endl<<″num:″<<num<<endl;    cout<<″name:″<<name<<endl;    cout<<″score:″<<score<<endl; } class Graduate:public Student//声明公用派生类Graduate {    public :    Graduate(int, string ,float ,float );//声明构造函数    void display( );//声明输出函数    private :    float pay;//工资 }; //定义构造函数 void Graduate::display() //定义输出函数 {    Student::display(); //调用Student类的display函数    cout<<″pay=″<<pay<<endl; } int main() {    Student stud1(1001,″Li″,87.5); //定义Student类对象stud1    Graduate grad1(2001,″Wang″,98.5,563.5); //定义Graduate类对象grad1    Student *pt=&stud1;//定义指向Student类对象的指针并指向stud1    pt->display( ); //调用stud1.display函数    pt=&grad1; //指针指向grad1    pt->display( ); //调用grad1.display函数 }

很多读者会认为: 在派生类中有两个同名的display成员函数,根据同名覆盖的规则,被调用的应当是派生类Graduate对象的display函数,在执行Graduate::display函数过程中调用Student::display函数,输出num,name,score,然后再输出pay的值。

事实上这种推论是错误的,先看看程序的输出结果: num:1001 name:Li score:87.5 num:2001 name:wang score:98.5 并没有输出pay的值。

问题在于pt是指向Student类对象的指针变量,即使让它指向了grad1,但实际上pt指向的是grad1中从基类继承的部分。转载请注明圣安娜娱乐http://www.leruntoys.com

 

posted @ 2013-07-10 10:54  飞天人人  阅读(434)  评论(0编辑  收藏  举报