一、 基類與衍生類別的轉換
3種繼承方式(公用、保護、私人繼承)中,公用衍生類別才是基類真正的子類型,它完整地繼承了基類的功能。
不同類型資料之間在一定條件下可以進行類型的轉換。基類與衍生類別對象之間是否也有賦值相容的關係,可否進行類型間的轉換?回答是可以的。基類與衍生類別對象之間有賦值相容關係,由於衍生類別中包含從基類繼承的成員,因此可以將衍生類別的值賦給基類對象,在用到基類對象的時候可以用其子類對象代替
。
注意:有的資料類型是不可轉換的;有的轉換是無法復原的。
1、衍生類別對象可以向基類對象賦值。
可以用子類(即公用衍生類別)對象對其基類對象賦值。
如:
A al;
//定義基類A對象al
B bl;
//定義類A的公用衍生類別B的對象bl
a1=b1;
//用衍生類別B對象bl對基類對象al賦值
實際上,所謂賦值只是對資料成員賦值,對成員函數不存在賦值問題。
注意:
1、賦值後不能企圖通過對象a1去訪問衍生類別對象bl的成員,因為bl的成員與al的成員是不同的。假設ase是衍生類別B中增加的公用資料成員,分析下面的用法:
a1.age=23;//錯誤,al中不包含衍生類別中增加的成員
b1.age=21;//正確,b1中包含衍生類別中增加的成員
2、子類型關係是單向的、無法復原的。B是A的子類型,不能說A是B的子類型。只能用子類對象對其基類對象賦值,而不能用基類對象對其子類對象賦值,理由是顯然的,因為基類對象不包含衍生類別的成員,無法對衍生類別的成員賦值。同理,同一基類的不同衍生類別對象之間也不能賦值。
2、衍生類別對象可以替代基類對象向基類對象的引用進行賦值或初始化
。
如已定義了基類A對象a1,可以定義a1的引用:
A al; //定義基類A對象al
B bl; //定義公用衍生類別B對象bl
A&r=al;//定義基類A對象的引用變數r,並用a1對其初始化
引用r是al的別名,r和a1共用同一段儲存單元。可以用子類對象初始化引用r,將上面最後一行改為:
A&r=bl;//定義基類A對象的引用變數r,並用衍生類別B對象b1對其初始化或者保留上面第3行“A&r=al;”,而對r重新賦值:
r=bl; //用衍生類別B對象bl對a1的引用變數r賦值
注意
:此時r並不是bl的別名,也不與bl共用同一段儲存單元。它只是b1中基類部分的別名,r與bl中基類部分共用同一段儲存單元,r與b1具有相同的起始地址。
3、如果函數的參數是基類對象或基類對象的引用,相應的實參可以用子類對象。
如有一函數fun:
void fun(A&r)
//形參是類A的對象的引用
{
cout<<r.num<<endl;}//輸出該引用所代表的對象的資料成員num
函數的形參是類A的對象的引用變數,本來實參應該為A類的對象。由於子類對象與衍生類別對象賦值相容,衍生類別對象能自動轉換類型,在調用fun函數時可以用衍生類別B的對象bl作實參: fun(b1);
輸出類B的對象bl的基類資料成員num的值。
在fun函數中只能輸出衍生類別中基類成員的值
。
4、衍生類別對象的地址可以賦給指向基類對象的指標變數,也就是說,指向基類對象的指標變數也可以指向衍生類別對象。
例10
定義一個基類Student(學生),再定義Student類的公用衍生類別Graduate(研究生),用指向基類對象的指標輸出資料。
本例主要是說明用指向基類對象的指標指向衍生類別對象,為了減少程式長度,在每個類中只設很少成員。學生類只設num(學號),name(名字)和score(成績)3個資料成員,Graduate類只增加一個資料成員pay(工資)
:
#include <string>
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 //聲明公用衍生類別Graduale
{
public:
Graduate(int,string,float,float);//聲明建構函式
void display();
//聲明輸出函數
private:
float pay; };
//工資
void Graduate::display()
//定義輸山函數
{
Student::display();
//調用Student類的display函數
cout<<"pay="<<pay<<endl; }
Graduate::Graduate(int n,string nam,float s,float p):Student(n,nam,s),pay(p){}
//定義建構函式
int main()
{
Student stud1(1001,"Li",87.5);
//定義Student類對象stud1
Graduate grad1(2001,"Wang",98.5,563.5);//定義Graduate類對象grad1
Student *pt=&stud1;
//定義指向Student類對象的指標並指向studl
pt->display();
//調用studl.display函數
pt=&grad1;
//指標指向gradl
pt->display();
//調用gradl.display函數
return 0; }
程式的輸出結果:
num:1001
name:Li
score:87.5
num:2001
nume:wang
score:98.5
為什麼沒有輸出pay的值?
通過本例可以看到:
用
指向基類對象的指標變數指向子類對象是合法的、安全的,不會出現編譯上的錯誤。但在應用上卻不能完全滿足人們的希望,人們有時希望通過使用基類指標能夠調
用基類和子類對象的成員。如果能做到這點,程式人員會感到方便。在下一章就要解決這個問題。辦法是使用虛函數和多態性。