版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領
文檔簡介
《計算基礎——C++語言實現(xiàn)》繼承與多態(tài)是面向?qū)ο蟪绦蛟O計的兩個重要特性。通過繼承,可以基于已有類定義新類,實現(xiàn)軟件復用。新類繼承了已有類的屬性和方法,還可以根據(jù)需要添加新的屬性和方法?!袄^承”這種復用方式縮短了軟件開發(fā)時間,使得開發(fā)人員可以復用已經(jīng)測試和調(diào)試好的高質(zhì)量的類,減少了系統(tǒng)投入使用后可能出現(xiàn)的問題。通過多態(tài),可以使得在執(zhí)行同一條語句時,能夠根據(jù)運行情況執(zhí)行不同的操作?!岸鄳B(tài)”使得設計和實現(xiàn)易于擴展的系統(tǒng)成為可能。第8章繼承與多態(tài)28.1.1概述繼承是類的一個重要特性,它允許程序員基于已有的類創(chuàng)建自己的新類,而不必從頭編寫代碼。在C++中,如果一個類C1通過繼承已有類C而創(chuàng)建,則將C1稱作派生類(也稱做子類),將C稱做基類(也稱做父類)。派生類會繼承基類中定義的所有屬性和方法,另外也能夠在派生類中定義派生類所特有的屬性和方法。為了更好的理解繼承的概念,我們舉例說明,如圖8-1所示。8.1繼承38.1繼承4以Person類作為基類,創(chuàng)建了Student類、Teacher類等派生類。Student類和Teacher類從Person類繼承了Name(姓名)、Sex(性別)等屬性及SetName()、GetName()等方法,另外還添加了新的屬性和方法。例如,在Student類中新添加了SNO(學號)、Major(專業(yè))等屬性及SetSNO()、GetSNO()等方法;在Teacher類中新添加了TNO(教師號)、Depart(系)等屬性及SetTNO()、GetTNO()等方法。再同時以Student類和Teacher類作為基類,派生出了TA類。從圖8-1可以看出,既可以以一個已有類作為基類創(chuàng)建新類,也可以以多個已有類作為基類創(chuàng)建新類。如果一個派生類是基于一個基類創(chuàng)建的,則該繼承關系稱為單繼承;如果一個派生類是基于多個基類創(chuàng)建的,則該繼承關系稱為多重繼承。8.1繼承5提示: 一個類是基類還是派生類是針對具體的繼承關系而言的。一個類可能在一個繼承關系中是基類,而在另一個繼承關系中是派生類。例如,在圖8-1中,Student類和Teacher類都是基于Person類創(chuàng)建的,因此,在這兩個繼承關系中,Student類和Teacher類是派生類,Person類是基類。而在另一個繼承關系中,TA類是基于Student類和Teacher類創(chuàng)建的,因此,TA類是派生類,而Student類和Teacher類都是基類。 一般來說,派生類所表示的事物是基類所表示事物的子集(即派生類所表示的事物比基類更具體)。因此,在一個繼承關系中,“派生類事物是基類事物”這句話肯定成立,但反過來卻不行。例如,對于Teacher類和Person類的繼承關系來說,Teacher類是派生類,Person類是基類,顯然“Teacher是Person”這句話是正確的,而反過來說“Person是Teacher”則不行。我們在定義繼承關系時可以參照上述方法來檢驗所定義的繼承關系是否合理。8.1繼承68.1.2派生類定義(1)定義派生類的語法在一個繼承關系中,定義派生類的語法為:
class派生類名:繼承方式基類名
{
派生類成員聲明; };其中,繼承方式包括public(公有繼承)、protected(保護繼承)和private(私有繼承)三種,其含義將在后面章節(jié)中介紹。這里我們先使用public作為繼承方式。例如,對于圖8-1所示的Person類和Student類的單繼承關系,我們可以先定義Person類,再以Person類作為基類派生出Student類。8.1繼承7【例8-1】定義Person類及其派生類Student類。//derivedClassDef.cpp#include<iostream>usingnamespacestd;classPerson{public: voidSetName(char*name){strcpy(m_name,name);} char*GetName(){returnm_name;} voidSetSex(boolsex){m_sex=sex;} boolGetSex(){returnm_sex;}private: charm_name[20]; //姓名
boolm_sex; //性別(true:男,false:女)};8.1繼承8classStudent:publicPerson{public: voidSetSNO(char*sno){strcpy(m_sno,sno);} char*GetSNO(){returnm_sno;} voidSetMajor(char*major){strcpy(m_major,major);} char*GetMajor(){returnm_major;} voidDisplayInfo()
{ cout<<"學生信息:"<<endl <<"學號:"<<m_sno<<endl <<"姓名:"<<GetName()<<endl <<"性別:"; if(GetSex()==true)cout<<"男"<<endl; elsecout<<"女"<<endl; cout<<"專業(yè):"<<m_major<<endl; }private: charm_sno[8]; //學號 charm_major[20]; //專業(yè)};8.1繼承9intmain(){ Studentstudent; student.SetSNO("1210101"); student.SetName("張三"); student.SetSex(true); student.SetMajor("計算機應用"); student.DisplayInfo(); return0;}上面的程序中,Student類和Teacher類繼承了Person類的所有數(shù)據(jù)成員和方法成員。程序運行后,會在屏幕上輸出: 學生信息: 學號:1210101
姓名:張三 性別:男
專業(yè):計算機應用8.1繼承10基類中的私有成員在派生類的成員函數(shù)中無法直接訪問。例如,如果將DisplayInfo()函數(shù)中的GetName()和GetSex()函數(shù)調(diào)用分別改為m_name和m_sex,則編譯程序時會報錯。如果希望在派生類中直接訪問基類的成員,則可以將基類的成員聲明為public或protected。表8-1給出了public、private和protected三種類成員訪問控制方式的含義。8.1繼承訪問控制方式含義public類的公有成員,在任何地方都可以直接訪問private類的私有成員,只在該類的成員函數(shù)中可以直接訪問,在其他地方均不能直接訪問protected類的保護成員,在該類及其派生類的成員函數(shù)中可以直接訪問,在其他地方不能直接訪問11例如,只要將例8-1中的Person類定義改為: classPerson { public: voidSetName(char*name){strcpy(m_name,name);} char*GetName(){returnm_name;} voidSetSex(boolsex){m_sex=sex;} boolGetSex(){returnm_sex;} protected: charm_name[20]; //姓名 boolm_sex; //性別(true:男,false:女) };就可以在Student類的成員函數(shù)中直接訪問Person的私有數(shù)據(jù)成員m_name和m_sex.8.1繼承12提示: 用一個類作為基類來派生新類之前,必須要先定義,例如下面的程序就是錯誤的: classPerson; //聲明,未定義 classStudent:publicPerson //錯誤:在繼承前沒有給出Person類的定義 { …… };8.1繼承13(2)函數(shù)重定義對于基類中的成員函數(shù),可以在派生類中對其重新定義、實現(xiàn)新的功能。例如,我們可以在例8-1的Person類中也定義一個DisplayInfo()函數(shù):
voidDisplayInfo() { cout<<"個人信息:"<<endl <<"姓名:"<<m_name<<endl <<"性別:"; if(m_sex==true)cout<<"男"<<endl; elsecout<<"女"<<endl; }8.1繼承14這樣,Student類對從Person類繼承的DisplayInfo()函數(shù)進行了重定義。當使用Person類對象調(diào)用時,就會調(diào)用Person類中定義的DisplayInfo()函數(shù);而當使用Student類對象調(diào)用時,就會調(diào)用Student類中定義的DisplayInfo()函數(shù)。例如,在主函數(shù)中使用如下語句: Personperson; person.SetName("李四"); person.SetSex(false); person.DisplayInfo();可以在屏幕上輸出: 個人信息: 姓名:李四 性別:女8.1繼承15提示:區(qū)分函數(shù)重定義和函數(shù)重載:函數(shù)重載要求函數(shù)形參不同(或者形參個數(shù)不同,或者形參類型不同),在實際調(diào)用時根據(jù)傳入的實參來決定調(diào)用哪個函數(shù);函數(shù)重定義則要求派生類中的函數(shù)原型與基類中的函數(shù)原型完全一樣,在實際調(diào)用時根據(jù)對象類型來決定調(diào)用基類中定義的函數(shù)還是派生類中重定義的函數(shù)。8.1繼承168.1.3派生類的構造函數(shù)和析構函數(shù)派生類構造函數(shù)的作用主要是對派生類中新添加的數(shù)據(jù)成員做初始化工作;在創(chuàng)建派生類對象、執(zhí)行派生類構造函數(shù)時,系統(tǒng)會自動調(diào)用基類的構造函數(shù)來對基類中定義的數(shù)據(jù)成員做初始化。同樣,派生類析構函數(shù)的作用主要是清除派生類中新添加的數(shù)據(jù)成員、釋放它們所占據(jù)的系統(tǒng)資源;在銷毀派生類對象、執(zhí)行派生類析構函數(shù)時,系統(tǒng)會自動調(diào)用基類的析構函數(shù)來釋放基類中數(shù)據(jù)成員所占據(jù)的系統(tǒng)資源。8.1繼承17(1)派生類構造函數(shù)的定義派生類中構造函數(shù)的定義有以下兩種形式:■形式1
派生類名(形參列表):基類名(實參列表) { //派生類中數(shù)據(jù)成員的初始化
…… }通過“基類名(實參列表)”可以調(diào)用基類構造函數(shù)、對派生類從基類繼承的數(shù)據(jù)成員進行初始化??梢詫⑴缮悩嬙旌瘮?shù)形參列表中的形參作為實參傳遞給基類構造函數(shù),也可以將常量、全局變量等作為實參傳遞給基類構造函數(shù)。8.1繼承18例如,我們可以為Person類定義構造函數(shù):
Person(char*name,boolsex) { …… }那么Student類的構造函數(shù)定義為:
Student(char*sno,char*name,boolsex,char*major):Person(name,sex) { …… }當以下面語句創(chuàng)建Student類對象student時,就會將"1210101"和true作為實參傳遞給基類Person的構造函數(shù)。
Studentstudent("1210101","張三",true,"計算機應用");8.1繼承19■形式2
派生類名(形參列表) { //派生類中數(shù)據(jù)成員的初始化
…… }形式2會自動調(diào)用基類的無參構造函數(shù)、對派生類從基類繼承的數(shù)據(jù)成員進行初始化。它等價于:
派生類名(形參列表):基類名() { //派生類中數(shù)據(jù)成員的初始化
…… }8.1繼承20(2)派生類析構函數(shù)的定義派生類中析構函數(shù)的定義形式與基類完全相同,其語法格式為:
~派生類名() { //釋放派生類數(shù)據(jù)成員所占的系統(tǒng)資源
…… }【例8-2】為基類Person和派生類Student定義構造函數(shù)和析構函數(shù)。8.1繼承21//derivedClassDef.cpp#include<iostream>usingnamespacestd;classPerson{public: Person(char*name,boolsex) { strcpy(m_name,name); m_sex=sex; cout<<"Person類構造函數(shù)被調(diào)用!"<<endl; } ~Person(){cout<<"Person類析構函數(shù)被調(diào)用!"<<endl;}private: charm_name[20]; //姓名
boolm_sex; //性別(true:男,false:女)};8.1繼承22classStudent:publicPerson{public: Student(char*sno,char*name,boolsex,char*major):Person(name,sex) { strcpy(m_sno,sno); strcpy(m_major,major); cout<<"Student類構造函數(shù)被調(diào)用!"<<endl; } ~Student(){cout<<"Student類析構函數(shù)被調(diào)用!"<<endl;}private: charm_sno[8]; //學號
charm_major[20]; //專業(yè)};8.1繼承23intmain(){ Studentstudent("1210101","張三",true,"計算機應用"); return0;}當創(chuàng)建派生類對象時,先調(diào)用基類的構造函數(shù),再調(diào)用派生類的構造函數(shù);析構函數(shù)調(diào)用順序總是與構造函數(shù)調(diào)用順序相反。因此,上面的程序運行后,會在屏幕上輸出:
Person類構造函數(shù)被調(diào)用!
Student類構造函數(shù)被調(diào)用!
Student類析構函數(shù)被調(diào)用!
Person類析構函數(shù)被調(diào)用!8.1繼承248.1.4派生類的繼承方式在定義派生類時,可以指定的繼承方式包括public(公有繼承)、protected(保護繼承)和private(私有繼承,缺省方式)3種。通過設置繼承方式,可以使基類成員的訪問控制方式在派生類中發(fā)生變化。表8-2是上述3種繼承方式的總結。可見,派生類從基類繼承過來的成員的訪問控制方式由兩點決定:該成員在基類中的訪問控制方式;定義派生類所采用的繼承方式。8.1繼承訪問控制方式繼承方式publicprivateprotectedpublicpublic不可訪問protectedprivateprivate不可訪問privateprotectedprotected不可訪問protected25以public(公有方式)繼承時,基類的公有成員和保護成員的訪問控制方式在派生類中保持不變,仍作為派生類的公有成員和保護成員,基類的私有成員在派生類中不能直接訪問。以private(私有方式)繼承時,基類的公有成員和保護成員在派生類中都作為私有成員,基類的私有成員在派生類中不能直接訪問。以protected(保護方式)繼承時,基類的公有成員和保護成員在派生類中都作為保護成員,基類的私有成員在派生類中無法直接訪問。8.1繼承268.1.5多重繼承(1)多重繼承方式下的派生類定義在圖8-1中,通過對Student類和Teacher類進行多重繼承得到TA類。在一個多重繼承關系中,定義派生類的語法為:
class派生類名:繼承方式基類名1,繼承方式基類名2,…,繼承方式基類名n {
派生類成員聲明; };其中,不同的基類可以使用不同的繼承方式?!纠?-3】實現(xiàn)圖8-1的程序代碼。8.1繼承27classStudent:publicPerson{public: Student(char*sno,char*name,boolsex,char*major):Person(name,sex) { strcpy(m_sno,sno); strcpy(m_major,major); cout<<"Student類構造函數(shù)被調(diào)用!"<<endl; } ~Student(){cout<<"Student類析構函數(shù)被調(diào)用!"<<endl;} voidSetSNO(char*sno){strcpy(m_sno,sno);} char*GetSNO(){returnm_sno;} voidSetMajor(char*major){strcpy(m_major,major);} char*GetMajor(){returnm_major;}private: charm_sno[8]; //學號
charm_major[20]; //專業(yè)};8.1繼承28classTeacher:publicPerson{public: Teacher(char*tno,char*name,boolsex,char*depart):Person(name,sex) { strcpy(m_tno,tno); strcpy(m_depart,depart); cout<<"Teacher類構造函數(shù)被調(diào)用!"<<endl; } ~Teacher(){cout<<"Teacher類析構函數(shù)被調(diào)用!"<<endl;} voidSetTNO(char*tno){strcpy(m_tno,tno);} char*GetTNO(){returnm_tno;} voidSetDepart(char*depart){strcpy(m_depart,depart);} char*GetDepart(){returnm_depart;}private: charm_tno[6]; //教師號
charm_depart[20]; //系};8.1繼承29classTA:publicStudent,publicTeacher{public: TA(char*sno,char*name,boolsex,char*major,char*tno,char*depart) :Teacher(tno,name,sex,depart),Student(sno,name,sex,major) { cout<<"TA類構造函數(shù)被調(diào)用!"<<endl; } ~TA(){cout<<"TA類析構函數(shù)被調(diào)用!"<<endl;}};intmain(){ TAta("1210102","王五",true,"計算機應用","09110","計算機科學與技術系"); return0;}8.1繼承30在創(chuàng)建通過多重繼承定義的派生類對象時,也會先調(diào)用基類的構造函數(shù),再調(diào)用派生類的構造函數(shù)。各基類構造函數(shù)的調(diào)用順序與多重繼承時的繼承順序一致。在創(chuàng)建TA類時,先繼承Student類、再繼承Teacher類,因此,會先調(diào)用Student類的構造函數(shù)、再調(diào)用Teacher類的構造函數(shù)。Student類和Teacher類又是Person類的派生類,因此,在執(zhí)行Student類構造函數(shù)前會先調(diào)用Person類構造函數(shù)、在執(zhí)行Teacher類構造函數(shù)前也會先調(diào)用Person類構造函數(shù)。在創(chuàng)建TA類對象時,程序會在屏幕上輸出:
Person類構造函數(shù)被調(diào)用!
Student類構造函數(shù)被調(diào)用!
Person類構造函數(shù)被調(diào)用!
Teacher類構造函數(shù)被調(diào)用!
TA類構造函數(shù)被調(diào)用!析構函數(shù)調(diào)用順序總是與構造函數(shù)調(diào)用順序相反。8.1繼承31(2)多重繼承中的二義性問題在例8-3中,Student類和Teacher類都繼承了Person類的成員,TA類繼承了Student類和Teacher類的成員,因此,TA中包含了兩份Person類的成員(分別從Student類和Teacher類繼承)。如果我們通過TA類的對象調(diào)用Person類的成員,則在編譯程序時會報錯。如:cout<<ta.GetName()<<endl; //輸出姓名這是由于TA類中有兩個分別從Student類和Teacher類繼承過來的GetName()函數(shù),直接使用ta對象調(diào)用GetName()函數(shù)時會有二義性問題,即編譯程序不知道應該調(diào)用哪個GetName()函數(shù)。8.1繼承32為了解決這個問題,我們可以在調(diào)用GetName()函數(shù)時通過作用域運算符指定要調(diào)用從哪個類繼承過來的函數(shù)。例如:
cout<<ta.Student::GetName()<<endl;或
cout<<ta.Teacher::GetName()<<endl;但這樣調(diào)用函數(shù)需要知道類的繼承關系,不方便類的使用。下面介紹另一種使用虛擬繼承解決多重繼承中二義性問題的方法。8.1繼承33(3)虛擬繼承和虛基類在定義派生類時,可以通過虛擬繼承方式將基類聲明為虛基類。虛基類中的成員在類的繼承關系中只會被繼承一次,從而解決上述二義性問題。虛擬繼承的語法為:
class派生類名:virtual繼承方式虛基類名
{ …… }其中,關鍵字virtual和繼承方式的順序可以調(diào)換。例如,在例8-3中定義Student類和Teacher類時,如果采用虛擬繼承方式:
classStudent:virtualpublicPerson { …… } classTeacher:virtualpublicPerson { …… }8.1繼承34則Person類成為虛基類。此時,TA類從虛基類Person類中繼承Person類的成員,從Student類和Teacher類中則只繼承了Student類和Teacher類新定義的成員,從而保證TA類中只包含一份Person類的成員、解決了二義性問題。由于虛基類后繼類層次中的類都是直接從虛基類繼承其成員,而對這部分成員的初始化需要調(diào)用虛基類的構造函數(shù)來完成。因此,如果一個派生類同時從基類和虛基類繼承了成員,那么在定義該類的構造函數(shù)時,除了要調(diào)用基類的構造函數(shù)、還要調(diào)用虛基類的構造函數(shù);析構函數(shù)也是如此,當銷毀該派生類的對象時,除了會直接調(diào)用基類的析構函數(shù)、還會直接調(diào)用虛基類的析構函數(shù)。8.1繼承35【例8-4】采用虛擬繼承方式改寫例8-3,解決二義性問題。//VirtualInherit.cpp#include<iostream>usingnamespacestd;classPerson{public: Person(char*name,boolsex) { strcpy(m_name,name); m_sex=sex; cout<<"Person類構造函數(shù)被調(diào)用!"<<endl;} ~Person(){cout<<"Person類析構函數(shù)被調(diào)用!"<<endl;} voidSetName(char*name){strcpy(m_name,name);} char*GetName(){returnm_name;} voidSetSex(boolsex){m_sex=sex;} boolGetSex(){returnm_sex;}private: charm_name[20]; //姓名
boolm_sex; //性別(true:男,false:女)};8.1繼承36classStudent:virtualpublicPerson{public: Student(char*sno,char*name,boolsex,char*major):Person(name,sex) { strcpy(m_sno,sno); strcpy(m_major,major); cout<<"Student類構造函數(shù)被調(diào)用!"<<endl; } ~Student(){cout<<"Student類析構函數(shù)被調(diào)用!"<<endl;} voidSetSNO(char*sno){strcpy(m_sno,sno);} char*GetSNO(){returnm_sno;} voidSetMajor(char*major){strcpy(m_major,major);} char*GetMajor(){returnm_major;}private: charm_sno[8]; //學號
charm_major[20]; //專業(yè)};8.1繼承37classTeacher:virtualpublicPerson{public: Teacher(char*tno,char*name,boolsex,char*depart):Person(name,sex) { strcpy(m_tno,tno); strcpy(m_depart,depart); cout<<"Teacher類構造函數(shù)被調(diào)用!"<<endl; } ~Teacher(){cout<<"Teacher類析構函數(shù)被調(diào)用!"<<endl;} voidSetTNO(char*tno){strcpy(m_tno,tno);} char*GetTNO(){returnm_tno;} voidSetDepart(char*depart){strcpy(m_depart,depart);} char*GetDepart(){returnm_depart;}private: charm_tno[6]; //教師號
charm_depart[20]; //系};8.1繼承38classTA:publicStudent,publicTeacher{public: TA(char*sno,char*name,boolsex,char*major,char*tno,char*depart) :Teacher(tno,name,sex,depart),Student(sno,name,sex,major), Person(name,sex) //需要調(diào)用虛基類的構造函數(shù)
{ cout<<"TA類構造函數(shù)被調(diào)用!"<<endl; } ~TA(){cout<<"TA類析構函數(shù)被調(diào)用!"<<endl;}};intmain(){ TAta("1210102","王五",true,"計算機應用","09110","計算機科學與技術系"); cout<<ta.GetName()<<endl; //輸出姓名
return0;}8.1繼承39當創(chuàng)建TA類對象ta時,會先調(diào)用虛基類的構造函數(shù),再按繼承順序調(diào)用基類的構造函數(shù),最后調(diào)用TA類的構造函數(shù),因此,程序會在屏幕上輸出:
Person類構造函數(shù)被調(diào)用!
Student類構造函數(shù)被調(diào)用!
Teacher類構造函數(shù)被調(diào)用!
TA類構造函數(shù)被調(diào)用!析構函數(shù)的調(diào)用順序與構造函數(shù)相反,因此,在程序結束、銷毀ta對象前會在屏幕上輸出:
TA類析構函數(shù)被調(diào)用!
Teacher類析構函數(shù)被調(diào)用!
Student類析構函數(shù)被調(diào)用!
Person類析構函數(shù)被調(diào)用!TA類只是直接從虛基類Person類中繼承了一份Person類的成員,因此在調(diào)用“ta.GetName()”時不會有二義性問題。提示:虛基類Person類的構造函數(shù)只會在TA類的構造函數(shù)中調(diào)用一次,而在執(zhí)行Student類和Teacher類的構造函數(shù)時不會再重復調(diào)用虛基類Person類的構造函數(shù)。8.1繼承408.2.1類型兼容和多態(tài)性的概念類型兼容是多態(tài)性的前提,指在基類對象可以出現(xiàn)的任何地方,都可以用公有派生類的對象來替代。類型兼容所指的是如下三種情況:(1)可以用派生類對象為基類對象賦值;(2)可以用派生類對象初始化基類引用;(3)可以用派生類對象地址為基類指針賦值。例如,對于圖8-1所示的類的繼承關系,以下程序能正常編譯和運行:
Studentstudent; Personperson,*pPerson; person=student; //用派生類對象為基類對象賦值
pPerson=&student;//用派生類對象地址為基類指針賦值
Person&rPerson=student;//用派生類對象初始化基類引用8.2多態(tài)41提示:需要派生類對象的地方,不能以基類對象來替代。就像“猴子是動物”是正確的,但“動物是猴子”就錯了。提示:用派生類對象替代基類對象進行賦值操作后,通過基類對象、基類對象引用和基類指針只能訪問派生類從基類繼承的成員。例如:
cout<<person.GetName()<<endl; //正確:使用基類對象能夠訪問派生類從基
類繼承的的成員
cout<<person.GetMajor()<<endl; //錯誤:使用基類對象不能訪問派生類中新定義的成員8.2多態(tài)42通過類型兼容,對于基類及其公有派生類的對象,可以使用相同的函數(shù)統(tǒng)一進行處理。比如,函數(shù)參數(shù)是基類類型,而實際調(diào)用該函數(shù)時既可以傳入基類對象,也可以傳入派生類對象?!纠?-5】類型兼容示例。//CompatibleType.cpp#include<iostream>usingnamespacestd;classPerson{public: Person(char*name,boolsex) { strcpy(m_name,name); m_sex=sex; }8.2多態(tài)43
voidDisplayInfo() { cout<<"個人信息:"<<endl <<"姓名:"<<m_name<<endl <<"性別:"; if(m_sex==true)cout<<"男"<<endl; elsecout<<"女"<<endl; }protected: charm_name[20]; //姓名
boolm_sex; //性別(true:男,false:女)};classStudent:publicPerson{public: Student(char*sno,char*name,boolsex,char*major):Person(name,sex) { strcpy(m_sno,sno); strcpy(m_major,major); }8.2多態(tài)44voidDisplayInfo() { cout<<"學生信息:"<<endl <<"學號:"<<m_sno<<endl <<"姓名:"<<m_name<<endl <<"性別:"; if(m_sex==true)cout<<"男"<<endl; elsecout<<"女"<<endl; cout<<"專業(yè):"<<m_major<<endl; }private: charm_sno[8]; //學號
charm_major[20]; //專業(yè)};voidPrint(Person&rp){ rp.DisplayInfo();}8.2多態(tài)45intmain(){ Studentstudent("1210101","張三",true,"計算機應用"); Personperson("李四",false); Print(student); //以Student類對象作為實參
Print(person); //以Person類對象作為實參
return0;}上面的程序運行后,會在屏幕上輸出: 個人信息: 姓名:張三 性別:男 個人信息: 姓名:李四 性別:女8.2多態(tài)46在主函數(shù)中兩次調(diào)用Print()函數(shù),分別將基類對象person和派生類對象student作為實參傳遞給基類引用rp。但是,在使用基類引用rp調(diào)用DisplayInfo()函數(shù)時都是調(diào)用基類定義的函數(shù)。顯然,我們并不希望看到上面的運行結果。我們希望當以派生類對象作為實參傳遞給基類引用rp時,使用rp調(diào)用DisplayInfo()函數(shù)能夠調(diào)用派生類定義的函數(shù)。這種能夠根據(jù)指針或引用所表示的對象的實際類型來調(diào)用該對象所屬類的函數(shù)、而不是每次都調(diào)用基類中函數(shù)的特性,就是本節(jié)所要介紹的多態(tài)性。下面介紹如何來實現(xiàn)多態(tài)性。8.2多態(tài)478.2.2多態(tài)性的實現(xiàn)(1)動態(tài)綁定與虛函數(shù)前面例子中的函數(shù)調(diào)用,都是采用“先期綁定”的方式。所謂“綁定”就是建立函數(shù)調(diào)用和函數(shù)本體的關聯(lián)。如果綁定發(fā)生于程序運行之前(由編譯器和鏈接器完成),則稱為“先期綁定”(也稱為“靜態(tài)綁定”)。要實現(xiàn)多態(tài)性,就要進行“后期綁定”(也稱為“動態(tài)綁定”),即綁定發(fā)生于程序運行過程中。C++通過虛函數(shù)實現(xiàn)“動態(tài)綁定”技術。虛函數(shù)的聲明方法是在基類的函數(shù)聲明前或函數(shù)定義的函數(shù)頭前(無函數(shù)聲明時)加上virtual關鍵字。8.2多態(tài)48例如,對例8-5中Person類的DisplayInfo()函數(shù),只要在其函數(shù)頭前加上virtual關鍵字:
virtualvoidDisplayInfo() { …… }該函數(shù)即成為虛函數(shù)。虛函數(shù)具有繼承性,只要基類中的函數(shù)被聲明為虛函數(shù),則在派生類中對虛函數(shù)進行重定義時,無論是否加了virtual關鍵字,這個函數(shù)都是虛函數(shù)。【例8-6】多態(tài)性示例。//Polymorphism.cpp#include<iostream>usingnamespacestd;8.2多態(tài)49classPerson{public: Person(char*name,boolsex) { strcpy(m_name,name); m_sex=sex; } virtualvoidDisplayInfo() { cout<<"個人信息:"<<endl<<"姓名:"<<m_name<<endl<<"性別:"; if(m_sex==true)cout<<"男"<<endl; elsecout<<"女"<<endl; }protected: charm_name[20]; //姓名
boolm_sex; //性別(true:男,false:女)};8.2多態(tài)50classStudent:publicPerson{public: Student(char*sno,char*name,boolsex,char*major):Person(name,sex) { strcpy(m_sno,sno); strcpy(m_major,major); } voidDisplayInfo() //等價于virtualvoidDisplayInfo() { cout<<"學生信息:"<<endl <<"學號:"<<m_sno<<endl <<"姓名:"<<m_name<<endl <<"性別:"; if(m_sex==true)cout<<"男"<<endl; elsecout<<"女"<<endl; cout<<"專業(yè):"<<m_major<<endl; }private: charm_sno[8]; //學號
charm_major[20]; //專業(yè)};8.2多態(tài)51voidPrint(Person&rp){ rp.DisplayInfo();}intmain(){ Studentstudent("1210101","張三",true,"計算機應用"); Personperson("李四",false); Print(student); //以Student類對象作為實參
Print(person); //以Person類對象作為實參
return0;}8.2多態(tài)52上面的程序運行后,會在屏幕上輸出: 學生信息: 學號:1210101
姓名:張三 性別:男 專業(yè):計算機應用 個人信息: 姓名:李四 性別:女與例8-5相同,在主函數(shù)中兩次調(diào)用Print()函數(shù),分別將基類對象person和派生類對象student作為實參傳遞給基類引用rp。但在例8-6中,DisplayInfo()函數(shù)被聲明為虛函數(shù),因此在使用基類引用rp調(diào)用該函數(shù)時就可以根據(jù)rp所引用對象的不同調(diào)用不同類的成員函數(shù),即實現(xiàn)了多態(tài)性。8.2多態(tài)53提示: 只有使用指針或引用調(diào)用虛函數(shù)時才能實現(xiàn)多態(tài)性。如果使用對象調(diào)用虛函數(shù),則不具有多態(tài)性,必然是調(diào)用該對象所屬類的成員函數(shù)。例如,將例8-6中的Print()函數(shù)改為:
voidPrint(Personp) { p.DisplayInfo(); }
則運行結果與例8-5完全一樣,不具有多態(tài)性。8.2多態(tài)54(2)虛析構函數(shù)如果使用基類指針釋放動態(tài)創(chuàng)建的派生類對象,則需要將析構函數(shù)聲明為虛函數(shù)?!纠?-7】虛析構函數(shù)示例。//VirtualDestructor.cpp#include<iostream>usingnamespacestd;classPerson{public: Person(char*name,boolsex) { m_name=newchar[strlen(name)+1]; strcpy(m_name,name); m_sex=sex; cout<<"Person類構造函數(shù)被調(diào)用!"<<endl; }8.2多態(tài)55virtual~Person() //將析構函數(shù)聲明為虛函數(shù)
{ delete[]m_name; cout<<"Person類析構函數(shù)被調(diào)用!"<<endl; }protected: char*m_name; //姓名
boolm_sex; //性別(true:男,false:女)};classStudent:publicPerson{public: Student(char*sno,char*name,boolsex,char*major):Person(name,sex) { m_sno=newchar[strlen(sno)+1]; m_major=newchar[strlen(major)+1]; strcpy(m_sno,sno); strcpy(m_major,major); cout<<"Student類構造函數(shù)被調(diào)用!"<<endl
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 關于小區(qū)養(yǎng)狗問卷調(diào)查
- 清退僑房協(xié)議書
- 班組長崗位職責
- 綠色能源產(chǎn)業(yè)園區(qū)管理策略
- 裝飾裝修室外施工合同
- 體育場所用電合同
- 橄欖球場改造協(xié)議
- 工業(yè)園區(qū)排水溝改造工程合同范文
- 社區(qū)工作者聘用合同模板
- 證券投資顧問聘用合同
- 《水土保持技術》課件-項目八 攔渣措施
- 機動車檢測站違規(guī)檢驗整改報告
- 2024年建筑電工復審考試題庫附答案
- 2024年4月自考04737C++程序設計試題及答案含評分參考
- 睡眠醫(yī)學智慧樹知到期末考試答案章節(jié)答案2024年廣州醫(yī)科大學
- GB/T 17259-2024機動車用液化石油氣鋼瓶
- 國開(河北)2024年《中外政治思想史》形成性考核1-4答案
- 床邊護理帶教體會
- 2024年社區(qū)工作者考試必背1000題題庫及必背答案
- MOOC 微型計算機原理與接口技術-南京郵電大學 中國大學慕課答案
- 1kw太陽能獨立供電系統(tǒng)解決方案
評論
0/150
提交評論