《C++面向?qū)ο蟪绦蛟O計》譚浩強-第五章_第1頁
《C++面向?qū)ο蟪绦蛟O計》譚浩強-第五章_第2頁
《C++面向?qū)ο蟪绦蛟O計》譚浩強-第五章_第3頁
《C++面向?qū)ο蟪绦蛟O計》譚浩強-第五章_第4頁
《C++面向?qū)ο蟪绦蛟O計》譚浩強-第五章_第5頁
已閱讀5頁,還剩51頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

C++語言設計

第五章繼承與派生大連海事大學陳余慶第一章C++的初步知識第二章類和對象第三章再論類和對象第四章運算符重載第五章繼承與派生第六章多態(tài)性與虛函數(shù)第七章輸入輸出流第八章C++工具5.1繼承與派生的概念5.2派生類的聲明方式5.3派生類的構(gòu)成5.4派生類成員的訪問屬性5.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)5.6多重繼承5.7基類與派生類的轉(zhuǎn)換5.8繼承與組合5.1繼承與派生的概念 我們知道,面向?qū)ο蟪绦蛟O計的4個特性有抽象、封裝、繼承和多態(tài)。我們已經(jīng)討論了數(shù)據(jù)的抽象性和封裝性,現(xiàn)在來討論數(shù)據(jù)的繼承。

5.1繼承與派生的概念 所謂繼承,就是根據(jù)一個已存在的類建立一個新的類。已存在的類叫“基類”或“父類”,新建立的類叫“派生類”或“子類”。 新建立的子類從基類那里獲得基類的特性,稱為類的繼承;由基類產(chǎn)生新的子類,叫派生。一個基類可以派生多個子類,一個子類可以由多級基類派生而來。比方:大學學生大學生研究生大專生本科生博士生碩士生一個基類派生多級子類示意圖約定:箭頭方向表示繼承的方向,由派生類指向基類。5.1繼承與派生的概念多個基類派生一個子類示意圖。約定:箭頭方向表示繼承的方向,由派生類指向基類。研究生職工在職研究生基類:派生類:5.2派生類的聲明方式聲明派生類的方法:

class派生類名:[繼承方式]基類名

{派生類新增的成員 };繼承方式包括:Public方式,公用方式;Private方式,私有方式;Protected方式,受保護方式。繼承方式是可選的,不寫此項,默認為私有方式。5.2派生類的聲明方式 例如,由一基類Box〔盒子類〕,派生出一個新的子類colorBox〔彩色盒子〕:#include<iostream.h>classBox{private:intlen,wid,hei;public:voiddisplay(){cout<<“盒子體積=”<<len*wid*hei<<endl;}Box(intl=0,intw=0,inth=0){len=l;wid=w;hei=h;}};classcolorBox:publicBox{private:intcolor;public:voiddisplay(){cout<<“盒子的顏色=”<<color<<endl;}colorBox(intc=0){color=c;}};voidmain(){colorBoxcb1(3);cb1.display();}colorBox類intlen;intwid;inthei;構(gòu)造函數(shù)Box;voiddisplay();intcolor;voiddisplay();Box類intlen;intwid;inthei;構(gòu)造函數(shù)Box;voiddisplay();5.3派生類的構(gòu)成 派生類的成員包括從基類繼承過來的成員和自己增加的成員。從基類繼承過來的成員,表達了基類與子類的共性;而新增加的成員,表達了子類的個性。下面是Box基類和colorBox子類的存儲圖:基類數(shù)據(jù)成員成員函數(shù)派生類新增成員繼承5.3派生類的構(gòu)成 請注意,類的繼承并不是把基類成員和派生類新增成員簡單地放在一起。構(gòu)造一個派生類包括以下四局部工作從基類接收成員。派生類把基類的全部成員〔不包括構(gòu)造函數(shù)和析構(gòu)函數(shù)〕接收過來。缺乏之處在于會造成冗余,浪費存儲空間和執(zhí)行效率,尤其是屢次派生時。所以,不能隨意地找一個類去派生出某一個子類。調(diào)整從基類接收的成員。接收基類成員必須照單全收,但編程人員可以對接收過來的成員作一些調(diào)整。如,通過指定不同的繼承方式,來改變基類成員在派生類中的訪問屬性;在派生類中聲明一個和基類成員同名的成員,來“遮蓋”接收的基類成員。如果想遮蓋成員函數(shù),其函數(shù)名和參數(shù)表必須相同,否那么就成為重載。在聲明派生類時增加新的成員。主要表達子類對基類的擴展。構(gòu)造派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)。5.4派生類成員的訪問屬性 派生類成員中包含基類成員和派生類成員,在討論訪問屬性時,需要考慮以下幾種情況:基類成員函數(shù)訪問基類成員;派生類成員函數(shù)訪問派生類自己新增成員;基類成員函數(shù)訪問派生類成員;派生類成員函數(shù)訪問基類成員;在派生類外訪問派生類的成員;在派生類外訪問基類的成員。 對于①、②,種情況,按照第二章介紹的規(guī)那么處理。即基類成員函數(shù)可訪問基類成員,派生類成員函數(shù)可訪問派生類自己新增成員,基類私有數(shù)據(jù)成員只能被基類成員函數(shù)訪問,派生類新增私有數(shù)據(jù)成員,只能被派生類新增成員函數(shù)訪問。5.4派生類成員的訪問屬性 對于第④~⑥種情況,我們分不同的繼承方式分開討論:公用繼承:基類的公用成員和保護成員在派生類中保持原有的訪問屬性,其私有成員仍為基類的私有,派生類新增成員不可訪問它。私有繼承:基類的公用成員和保護成員在派生類中成為私有成員,其私有成員仍為基類的私有,派生類新增成員不可訪問它。保護繼承:基類的公用成員和保護成員在派生類中成為保護成員,其私有成員仍為基類的私有,派生類新增成員不可訪問它。什么叫保護成員? 保護成員不能被外界引用,但可以被派生類的成員引用。5.4派生類成員的訪問屬性公用繼承公用繼承的定義:在定義一個派生類時,將基類的繼承方式指定為public就是公用繼承。其基類叫公用基類(publicbaseclass),子類叫公用派生類(publicderivedclass)。被公用繼承而來的基類在派生類中的訪問屬性見下表: 我們來看看下面的例如程序:公用基類的成員在公用派生類中的訪問屬性私有成員不可訪問公用成員公用保護成員私有5.4派生類成員的訪問屬性#include<iostream.h>#include<string.h>classperson{public:voidget_value(){cin>>num>>name;}voiddisplay(){cout<<“id:”<<num<<endl;cout<<“name:”<<name<<endl;}private:intnum;stringname;};classstudent:publicperson{public:voiddisplay(){cout<<“id:”<<num<<endl;//錯誤,派生類中不能訪問基類私有成員cout<<“name:”<<name<<endl;//錯誤,派生類中不能訪問基類私有成員cout<<“stu_id:”<<stu_id<<endl;}private:intstu_id;};程序應修改,在main函數(shù)中分別調(diào)用基類和派生類的顯式成員函數(shù):#include<iostream.h>#include<string.h>classperson{public:voidget_value(){cin>>num>>name;}voidperson_display(){cout<<“id:”<<num<<endl;cout<<“name:”<<name<<endl;}private:intid;stringname;};classstudent:publicperson{public:voidstudent_display(){cout<<“stu_id:”<<stu_id<<endl;}private:intstu_id;};voidmain(){studentzhangsan;:::zhangsan.person_display();zhangsan.student_display();} 這里只是對顯示函數(shù)做了調(diào)整,其實還有很多修改.比方,對數(shù)據(jù)輸入,就要調(diào)整.請同學們自己完成.5.4派生類成員的訪問屬性私有繼承私有繼承的定義:在定義一個派生類時,將基類的繼承方式指定為private就是私有繼承。其基類叫私有基類(privatepublicbaseclass),子類叫私有派生類(privatederivedclass)。被私有繼承而來的基類在派生類中的訪問屬性見下表: 我們來看看下面的例如程序:私有基類的成員在私有派生類中的訪問屬性私有成員不可訪問公用成員私有保護成員私有5.4派生類成員的訪問屬性 我們作如下示意圖來說明私有繼承的訪問屬性:子類Bpublic:intm;private:inti,n;不可訪問:intk;基類Apublic:inti;private:intk;子類Bpublic:inti;private:intk;public:intm;private:intn;私有繼承派生類新增成員實際派生類基類A中的公用成員i,私有成員k,被私有派生給子類B子類B自己新增公用成員m,和私有成員n最后,子類B具有的公用成員為m,私有成員為i,n?;惖膋在子類B中不可訪問。私有繼承例如:這里的錯誤該如何改寫?#include<iostream.h>#include<string.h>classperson{public:voidget_value(){cin>>num>>name;}voidperson_display(){cout<<“id:”<<num<<endl;cout<<“name:”<<name<<endl;}private:intnum;stringname;};classstudent:privateperson{public:voidstudent_display(){cout<<“stu_id:”<<stu_id<<endl;}private:intstu_id;};voidmain(){studentzhangsan;:::zhangsan.person_display();//私有繼承基類的公用函數(shù)//變成子類私有函數(shù),類外不能訪問zhangsan.student_display();}

可以將子類的顯示函數(shù)這樣改寫:#include<iostream.h>#include<string.h>classperson{public:voidget_value(){cin>>num>>name;}voidperson_display(){cout<<“id:”<<num<<endl;cout<<“name:”<<name<<endl;}private:intid;stringname;};classstudent:privateperson{public:voidstudent_display(){person_display();

cout<<“stu_id:”<<stu_id<<endl;}private:intstu_id;};voidmain(){studentzhangsan;:zhangsan.student_display();}5.4派生類成員的訪問屬性保護成員和保護繼承 “保護,protected”既可以用來限定類成員的性質(zhì),也可以用來限定繼承方式。保護成員:被protected聲明的成員叫保護成員,保護成員不能被類外訪問,這和私有成員的特性一樣;但保護成員如果被派生給子類,可以被子類的成員函數(shù)引用。 我們將友元比喻成朋友,可以進入自己的臥室,訪問類的私有成員;我們將保護成員比喻成保險箱,任何外人不得窺視,包括朋友。只有自己和子女〔即派生類〕可以翻開。保護繼承:派生類繼承時被指定為protected。用保護方式繼承的派生類叫保護派生類(protecteddetivedclass),其基類叫受保護的基類(protectedbassclass)。 保護繼承的特點是,保護基類的公用成員和和保護成員在保護派生類中成為保護成員,私有成員仍為基類私有,派生類不得引用。5.4派生類成員的訪問屬性 綜合所述,三種繼承方式下派生類成員的訪問屬性列表如下:基類中的成員屬性在公用繼承得來的派生類中在私有繼承得來的派生類中在保護繼承得來的派生類中私有成員不可訪問不可訪問不可訪問公用成員公用私有保護保護成員保護私有保護由上表可以得出以下結(jié)論:公用成員,派生類內(nèi)外都可以訪問;保護成員,派生類內(nèi)可以訪問,派生類外不能訪問,其下一級的派生也可以訪問;私有成員,派生類內(nèi)可以訪問,派生類外不能訪問,其下一級的派生不可訪問;不可訪問的成員,派生類內(nèi)外都不可訪問。5.4派生類成員的訪問屬性 下面是一個怎樣使用保護成員的例如:#include<iostream.h>#include<string.h>classperson{protected:intid;stringname;};classstudent:protectedperson{public:voidstudent_display(){cout<<“id:”<<id<<endl;cout<<“name:”<<name<<endl;cout<<“stu_id:”<<stu_id<<endl;}private:intstu_id;};voidmain(){studentzhangsan;student.id=“12345678;//錯誤,外界不能訪問保護成員zhangsan.student_display();//合法,此函數(shù)是公用函數(shù)}5.4派生類成員的訪問屬性多級派生時的訪問屬性 實際應用中,常常有多級派生的情況。比方:A是基類,派生出B類,B類又派生出C類。那么B是A的直接派生,C是A的間接派生。A是B的直接基類,A是C的間接基類。請看:A是B的公用基類,B是C的保護基類;各成員在不同類的屬性為:classA{public:inti;protected:intj;voidf2();private:intk;};classB:publicA{public:voidf3();protected:voidf4();private:intm;};classC:protectedB{public:voidf5();private:intn;};if2jkf3f4mf5n基類A公用保護保護私有公用派生類B公用保護保護不可訪問公用保護私有保護派生類C保護保護保護不可訪問保護保護不可訪問公用私有5.4派生類成員的訪問屬性上表說明:無論哪一種繼承方式,在派生類中不能訪問基類的私有成員,私有成員只能被本類的成員函數(shù)訪問;多級派生假設都采用公用繼承,直到最后一級派生類都能訪問基類的公用成員和保護成員;多級派生假設都采用私有繼承,經(jīng)過假設干次派生后,基類的所有成員都變成不可訪問;多級派生假設都采用保護繼承,派生類外無法訪問派生類中的任何成員,人們很難記住哪些成員還能訪問,哪些成員不能被訪問。 因此,實際應用中,通常采用公用繼承方式。5.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù) 構(gòu)造函數(shù)的主要作用是對數(shù)據(jù)成員初始化。在第三章我們介紹過,用戶在聲明類時可以不定義構(gòu)造函數(shù),系統(tǒng)會自動設置一個默認的構(gòu)造函數(shù),在定義類對象時會自動調(diào)用這個默認的構(gòu)造函數(shù)。這個默認的構(gòu)造函數(shù)實際上是個空函數(shù),不執(zhí)行任何操作,如果需要初始化對象的數(shù)據(jù)成員,應該自己定義構(gòu)造函數(shù)。 我們已經(jīng)知道,基類的構(gòu)造函數(shù)是不能繼承的。因此,對繼承來的基類成員初始化的工作,要由派生類的構(gòu)造函數(shù)承擔。所以,在設計派生類的構(gòu)造函數(shù)時,不僅要考慮初始化派生類自己新增的數(shù)據(jù)成員,還要考慮初始化基類的數(shù)據(jù)成員。 解決問題的思路是:在執(zhí)行派生類的構(gòu)造函數(shù)時,調(diào)用基類的構(gòu)造函數(shù)。5.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)簡單派生類的構(gòu)造函數(shù) 我們說的“簡單”是指,一個基類直接派生出一個子類,派生類的數(shù)據(jù)成員中不包含基類的對象〔即子類〕。簡單派生類的構(gòu)造函數(shù)一般形式 派生類構(gòu)造函數(shù)名(總參數(shù)表列):基類構(gòu)造函數(shù)名(參數(shù)表列) {派生類中新增數(shù)據(jù)成員初始化語句} 下面以一個派生類的實例來說明有些什么東西。5.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)#include<iostream.h>#include<string.h>classperson//基類聲明{public:person(inti,stringnam)//基類構(gòu)造函數(shù){id=i;name=nam;}protected:intid;stringname;};classstudent:publicperson//聲明公用派生類{public:

student(inti,stringnam,intsid):person(i,nam){stuid=sid;}//子類構(gòu)造函數(shù)voidshow(){cout<<“id:”<<id<<endl;cout<<“name:”<<name<<endl;cout<<“stuid:”<<stuid<<endl;}private:intstuid;};voidmain(){students1(1234,”張三”,200701);s1.show();}5.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù) 將派生類的構(gòu)造函數(shù)形式與第三章介紹的“用參數(shù)初始化表對數(shù)據(jù)成員初始化”的介紹,對照看看:

student(inti,stringnam,intsid):person(i,nam){stuid=sid;} box::box(inth,intw,intlen):height(h),width(w),length(len){}

它也有一個冒號,在冒號后面是對數(shù)據(jù)成員的初始化表。很相似吧? 實際上,本章介紹的在派生類構(gòu)造函數(shù)中對基類成員初始化,就是第三章介紹的構(gòu)造函數(shù)初始化表。也就是說:不僅可以用初始化表對數(shù)據(jù)成員初始化,而且可以利用初始化表調(diào)用派生類的基類構(gòu)造函數(shù),實現(xiàn)對基類數(shù)據(jù)成員的初始化。也可以在同一個構(gòu)造函數(shù)中同時實現(xiàn)這兩種功能。5.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù) 例如,我們可以將上一個例如的子類構(gòu)造函數(shù)改寫成這樣: student(inti,stringnam,intsid):person(i,nam),stuid(sid){} 這樣,函數(shù)體為空,顯得很簡捷,方便。在建立一個對象時,執(zhí)行構(gòu)造函數(shù)的順序是:先調(diào)用基類構(gòu)造函數(shù);再執(zhí)行派生類自身的構(gòu)造函數(shù)。 對于剛剛的例如,先初始化id,name,再初始化stuid。5.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)有子對象的派生類的構(gòu)造函數(shù) 如果類的數(shù)據(jù)成員除了標準數(shù)據(jù)類型外,還包含類對象,這種被包含的對象就叫“子對象(subobject),即對象中的對象。 我們?nèi)砸詫W生類為例如。在person的派生類student中,出來增加學號stuid,還增加“家長parent”一項,即學生數(shù)據(jù)中包含他的家長姓名等相關(guān)情況,而家長本身也屬于person類型,這樣,家長parent項既是基類person對象,也派生類student的子對象。 那么,初始化子類對象的數(shù)據(jù)成員時,怎樣對它所包含的子對象初始化呢?這是我們所關(guān)心的問題。請看下面程序:5.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)#include<iostream.h>#include<string.h>classperson{public:person(inti,stringnam){id=i;name=nam;}

voiddisplay(){cout<<“id:”<<id<<endl;cout<<“name:”<<name<<endl;}protected:intid;stringname;};classstudent:publicperson

{public:

student(inti,stringnam,intpid,stringpnam,intsid):person(i,nam),parent(pid,pnam){stuid=sid;}派生類構(gòu)造函數(shù)任務:1,初始化基類數(shù)據(jù)成員;2,初始化子對象數(shù)據(jù)成員;3,初始化派生類新增數(shù)據(jù)成員。voidshow(){cout<<“id:”<<id<<endl;cout<<“name:”<<name<<endl;cout<<“stuid:”<<stuid<<endl;}voidshowParent(){cout<<“parent_id:”<<endl;parent.display();}private:intstuid;

personparent;};voidmain(){students1(1234,”張三”,2211,”張父”,200701);s1.show();

s1.showParent();}5.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù) 歸納起來,我們有派生類構(gòu)造函數(shù)的一般形式:

派生類構(gòu)造函數(shù)名(總參數(shù)表):

基類構(gòu)造函數(shù)名(參數(shù)表列), 子對象名(參數(shù)表列),

{派生類中新增數(shù)據(jù)數(shù)據(jù)成員初始化語句}執(zhí)行派生類構(gòu)造函數(shù)的順序:調(diào)用基類構(gòu)造函數(shù),初始化基類數(shù)據(jù)成員;調(diào)用子對象構(gòu)造函數(shù),初始化子對象數(shù)據(jù)成員;再執(zhí)行派生類自身的構(gòu)造函數(shù),初始化自己新增數(shù)據(jù)成員。5.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù) 上例中的student派生類構(gòu)造函數(shù)可寫成如下形式:

student(inti,stringnam,intpid,stringpnam,intsid): person(i,nam), parent(pid,pnam), stuid(sid){}

可以看到,函數(shù)體為空。此時,派生類構(gòu)造函數(shù)參數(shù)個數(shù)等于基類構(gòu)造函數(shù)參數(shù)、子對象構(gòu)造函數(shù)參數(shù)、自己新增數(shù)據(jù)對象初始化參數(shù)之和。這種形式十分常見。5.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)多層派生時的構(gòu)造函數(shù) 當出現(xiàn)多層派生時,不能列出每一層的構(gòu)造函數(shù),只須寫出其上一層派生類〔即它的直接基類〕的構(gòu)造函數(shù)就可以了。private:intage;};classstudent2:publicstudent1{public:

student2(intn,stringnam,inta,ints):student1(n,nam,a){score=s;}voidshow_all(){show();cout<<“age:”<<age<<endl;}private:intscore;};voidmain(){student2stud(1001,”Li”,17,90);stud.show_all();}#include<iostreaam.h>#include<string.h>classstudent{public:student(intn,stringnam){num=n;name=nam;}voiddisplay(){cout<<“num:”<<num<<endl;cout<<“name:”<<name<<endl;}private:intnum;stringname;};classstudent1:publicstudent{public:student1(intn,stringnam,inta):student(n,nam){age=a;}voidshow(){display();cout<<“age:”<<age<<endl;}5.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù) 程序中,基類student構(gòu)造函數(shù)首部為:

student(intn,stringnam) 一級派生類student1構(gòu)造函數(shù)首部為

student1(intn,stringnam,inta):student(n,nam) 二級派生類student2構(gòu)造函數(shù)首部為

student2(intn,stringnam,inta,ints

):student1(n,nam,a)

注意,二級派生類student2構(gòu)造函數(shù)首部不要寫成:

student2(intn,stringnam,inta,ints

):student(n,nam),student1(n,m,a)//錯誤??!

不要列出每一層構(gòu)造函數(shù),只須列出其上一層的構(gòu)造函數(shù)。5.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)派生類的析構(gòu)函數(shù)析構(gòu)函數(shù)的作用:我們已經(jīng)學過,析構(gòu)函數(shù)的作用是在撤消對象之前,給編程人員一個時機,來進行一些清理工作。當對象被刪除時,系統(tǒng)會自動調(diào)用析構(gòu)函數(shù)。 和構(gòu)造函數(shù)一樣,析構(gòu)函數(shù)也是不能派生給派生類的,在派生類中必須編寫自己的析構(gòu)函數(shù),來作些派生類自己的清理工作。調(diào)用析構(gòu)函數(shù)的順序:與調(diào)用構(gòu)造函數(shù)的順序正好相反:先執(zhí)行派生類自己的析構(gòu)函數(shù),清理派生自己新增數(shù)據(jù)成員;再調(diào)用子對象的析構(gòu)函數(shù),清理子對象;最后調(diào)用基類的析構(gòu)函數(shù),對基類進行清理。5.6多重繼承什么叫多重繼承?一個子類由多個基類派生而來,叫多重繼承。比方所,在職研究生,既有在職職工的屬性,又有研究生的屬性。C++為了適應這種情況,允許一個派生類同時繼承多個基類,這種行為就是多重繼承(multipleinheritance)。聲明多重繼承的方法:設已聲明了類A,B,C,可以從它們派生出子類D,其聲明的一般形式為: classD:publicA,privateB,protectedC {類D新增加的成員} D是按照不同的繼承方式從不同的基類多重繼承而來的派生類。5.6多重繼承多重繼承派生類的構(gòu)造函數(shù):多重繼承派生類的構(gòu)造函數(shù)與單繼承派生類的構(gòu)造函數(shù)根本相同,只是在初始表中包含多個基類構(gòu)造函數(shù)。其一般形式是: 派生類構(gòu)造函數(shù)名(總參數(shù)表): 基類1構(gòu)造函數(shù)(參數(shù)列表1), 基類2構(gòu)造函數(shù)(參數(shù)列表2), 基類3構(gòu)造函數(shù)(參數(shù)列表3) {派生類中新增數(shù)據(jù)成員初始化語句} 例:分別聲明一個職工類(姓名,工資)和一個研究生類(姓名,成績),用多重繼承的方式從這兩個類派生出在職研究生類(新增學號)。在定義派生類時給出初始化數(shù)據(jù),然后輸出這些數(shù)據(jù)。5.6多重繼承#include<iostream>#include<string>usingnamespacestd;classworker{public:worker(stringnam,intsal){name=nam;salary=sal;}voidwdisplay(){cout<<“name:”<<wname<<endl;cout<<“salary:”<<salary<<endl;}protected:stringwname;intsalary;};classstudent{public:student(stringsnam,ints)

{sname=snam;score=s;}voidsdisplay(){cout<<“name:”<<sname<<endl;cout<<“score:”<<score<<endl;}protected:stringsname;intscore;};classgraduate:publicworker,publicstudent{public:graduate(stringnam,intsal,ints,inti):worker(nam,sal),student(nam,s),id(i){}voidgdisplay(){cout<<“id:”<<id<<endl;cout<<“wname:”<<name<<endl;cout<<“salary:”<<salary<<endl;cout<<“score:”<<score<<endl;}private:intid;};voidmain(){graduates1(“Li”,2000,90,1001);s1.gdisplay();}

5.6多重繼承 此例程中,請注意:兩個基類的數(shù)據(jù)成員都聲明成protected類型,因此可以通過派生類的成員函數(shù)引用這兩個基類的數(shù)據(jù)成員。同學們可能注意到,兩個基類中,分別用wname和sname來表示姓名,其實是同一個人的姓名,能否用同一名稱呢?不行。因為,這樣一來,同一個派生類中就有兩個同名的數(shù)據(jù)成員,引起二義性。多重繼承時,從不同的基類中會繼承一些重復的數(shù)據(jù),例如本例中姓名就是重復的。在實際編程應用中,會有更多的重復數(shù)據(jù),問題會更突出。5.6多重繼承多重繼承的二義性 多重繼承給編寫程序帶來靈活性,但也引出了“二義性(ambiguous)”問題。我們來看看。 假設類A和類B中都有成員函數(shù)display和數(shù)據(jù)成員a,類C是A、B的多重繼承的直接派生類。我們以下三種情況討論:兩個基類有同名成員, 如右圖示。假設C 類有一對象c1, 我們寫: c1.a=3; c1.display();A類inta;voiddisplay()B類inta;voiddisplay()C類inta;voiddisplay()inta;voiddisplay()intb;voidshow()基類A的成員基類B的成員派生類C新增成員5.6多重繼承 由于基類A、B都有數(shù)據(jù)成員a,和成員函數(shù)display(),系統(tǒng)沒法判斷要訪問哪一個基類成員。解決的方法是,在成員名前加上對象名來限制: c1.A::a=3; c1.A::display(); 如果在派生類C中,通過成員函 數(shù)show()來訪問基類的display()和 a,可以直接寫成: A::a=3; A::display();C類intA::a;voidA::display()intB::a;voidB::display()intb;voidshow()基類A的成員基類B的成員派生類C新增成員5.6多重繼承兩個基類和派生類都有同名成員,那么C類的成員函數(shù)變?yōu)椋?classC:publicA,publicB {inta; voiddisplay(); }在main函數(shù)中,定義C對象為c1,并調(diào)用a和dispaly():Cc1;c1.a=3;c1.display(); 這訪問的是C的成員,因為有規(guī)那么:基類的同名成員在派生類中被屏蔽。C類intA::a;voidA::display()intB::a;voidB::display()inta;voiddisplay()基類A的成員基類B的成員派生類C新增成員5.6多重繼承如果類A、B是從同一個基類派生的,如以下圖示: 基類N的成員a和display()在一級派生類A、B中都存在。假設要訪問A類中的a和display(),必須加上類名限定。類N類A類B類CclassC:publciA,publicB{public:inta3;voidshow(){cout<<“a3:“<<a3<<endl;}};voidmain(){Cc1;c1.A::a=3;c1.A::display();}classN{public:inta;voiddisplay(){cout<<“N::a:”<<a<<endl;}};classA:publicN{public:inta1;};classB:publicN{public:inta2;};5.6多重繼承虛基類 如果一個派生類有多個直接基類,而這些直接基類又有一個共同的基類,那么在最終的派生類中會保存該間接共同基類數(shù)據(jù)成員的多份同名成員。前一個例如就是這種情況。C類intA::a;intA::a1;intB::a;intB::a2;inta;voidA:;display();voidB::display();voidshow();派生類C的數(shù)據(jù)成員派生類C的成員函數(shù)基類N的成員A新增成員基類N的成員B新增成員C新增成語NNABC5.6多重繼承 在一個類中保存間接共同基類的多份同名成員,雖然有時是必要的,可以在不同的數(shù)據(jù)成員中存放不同的數(shù)據(jù)。但大多數(shù)情況下是不必要的,浪費較多內(nèi)存空間,容易出錯。實際上并不需要這么多份拷貝。 C++提供虛基類的技術(shù)來解決這儀問題。 其解決思路是,在繼承間接共同基類時,內(nèi)存里只存儲一份成員。虛基類定義的一般形式 class派生類名:virtual繼承方式基類名 即在聲明派生類時,將關(guān)鍵字virtual加在繼承方式前面。經(jīng)過這樣的聲明后,當基類通過多條派生路徑被一個派生類中只繼承一次,即基類成員只保存一次。5.6多重繼承 需要注意的是,為了保證虛 基類在派生類中只繼承一次,應該 在該基類的所有直接派生類中聲明 為虛基類。否那么仍然會出現(xiàn)對虛基 類的屢次繼承。 如果象右圖那樣,只在A,B 中將N聲明為虛基類,而C中沒有 將N聲明為虛基類,那么派生類D 中仍然有冗余的基類拷貝。類N類A類C類D類Bvirtualvirtual5.6多重繼承虛基類的初始化:在最后的派生類中,不僅要初始化直接基類,而且還要初始化虛基類。 如果虛基類中定義了帶參數(shù)的構(gòu)造函數(shù),而且沒有定義默認構(gòu)造函數(shù),那么在所有直接派生和間接派生類中通過構(gòu)造函數(shù)的初始化表對虛基類進行初始化。如: 注意,在定義多重繼承的 派生類D時,由于虛基類在派生 類中只有一份數(shù)據(jù)成員,所以 這份數(shù)據(jù)成員的初始化必須由 最后的派生類直接給出,以防止 出現(xiàn)B、C的構(gòu)造函數(shù)給出不同 的初始化參數(shù)而產(chǎn)生矛盾。classA{A(inti){}};classB:virtualpublicA{B(intn):A(n){}};classC:virtualpublicA{C(intn):A(n){}};classD:publicB,publicC{D(intn):A(n),B(n),C(n){}};5.6多重繼承#include<iostream>#include<string>Usingnamespacestd;classperson{public:person(stringnam,chars,inta){name=nam;sex=s;age=a;}protected:stringname;charsex;intage;};classteacher:virtualpublicperson{public:teacher(stringnam,chars,inta,stringt):person(nam,s,a){title=t;}protected:stringtitle;};classstudent:virtualpublicperson{public:student(stringnam,chars,inta,floatsco):person(nam,s,a),score(sco){}protected:floatsco;};classgraduate:publicteacher,publicstudent{public:graduate(stringnam,chars,inta,stringt,floatsco,floatw):

person(nam,s,a),teacher(nam,s,a,t),student(nam,s,a,sco),wage(w){}voidshow(){cout<<“name:”<<name<<endl;cout<<“age:”<<age<<endl;cout<<“sex:”<<sex<<endl;cout<<“score:”<<score<<endl;cout<<“wages:”<<wage<<endl;}private:floatwage;};voidmain(){graduates1(“wan”,”m”,24,”assistant”,90.5,2000.50);s1.show();}5.7基類與派生類的轉(zhuǎn)換 不同類型數(shù)據(jù)之間在一定條件下可以進行類型轉(zhuǎn)換,如,整數(shù)賦給雙精度數(shù),但不能賦給指針變量。這種不同類型數(shù)據(jù)之間的自動轉(zhuǎn)換和賦值,稱為“賦值兼容”。 我們現(xiàn)在關(guān)心的是,基類和派生類之間是否也有賦值兼容的關(guān)系,能否進行類型間的轉(zhuǎn)換? 答復是肯定的?;惻c派生類之間有賦值兼容關(guān)系,由于派生類中包含從基類繼承的成員,因此可以將派生類的值賦給基類對象,在用到基類對象的時候可以用其子類對象代替。我們分以下幾種情況來討論。5.7基類與派生類的轉(zhuǎn)換派生類對象可以向基類對象賦值 可以用公用派生類對象對其基類對象賦值。如 Aa1;//定義基類A對象a1 Bb1;//定義A的公用派生類B的對象b1 a1=b1;//將派生類對象b1的值賦給基類A對象a1 這種情

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論