版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1第十章繼承性與派生類
C
語言程序設(shè)計案例教程12/23/20242案例一雇員類設(shè)計1.問題描述設(shè)計一個雇員類,包括基礎(chǔ)員工、銷售員工、經(jīng)理以及銷售經(jīng)理;基礎(chǔ)員工每月有基本工資和等級,等級上升則基本工資提升;銷售員工除了基本工資以及等級和基礎(chǔ)員工一致外,還有銷售業(yè)績工資為銷售額的10%;經(jīng)理除了基本工資以及等級和基礎(chǔ)員工一致外,還有每月固定的管理基本工資;銷售經(jīng)理除了基本工資以及等級和基礎(chǔ)員工一致外,還有銷售業(yè)績工資為銷售額的10%和每月固定的管理基本工資。2.問題分析注意掌握本例中虛基類的聲明方法、虛基類的意義、派生類對虛基類中同名函數(shù)的覆蓋;注意輸出時顯示的多重派生情況下的虛基類和派生類構(gòu)造函數(shù)以及析構(gòu)函數(shù)調(diào)用次序。12/23/202433.?C++?代碼#include<iostream.h>#include<string.h>#include<iomanip.h>classemployee{//虛基類protected:charname[20];intidnum;intgrade;floatsalary;
staticinttotalnum;
public:employee(char*name="");~employee();char*getname(){returnname;}inlineintgetgrade(){returngrade;}floatinlinegetsalary(){returnsalary;}inlinevoidpromote(intg){grade=g;}voidgetpay(char*name="");voiddisplay();};12/23/20244inlineemployee::employee(char*name){totalnum++;strcpy(this->name,name);idnum=totalnum;grade=0;salary=3000;cout<<this->name<<"基礎(chǔ)員工已經(jīng)聘任"<<endl;}inlineemployee::~employee(){cout<<name<<"基礎(chǔ)員工已經(jīng)離職"<<endl;totalnum--;}voidemployee::getpay(char*name){strcpy(this->name,name);salary=salary+grade*1000;cout<<"員工信息已經(jīng)重新設(shè)置"<<endl;}voidemployee::display(){cout.setf(ios::left);cout<<setw(10)<<"姓名"<<setw(10)<<"編號"<<setw(10)<<"級別"<<setw(10)<<"工資"<<endl;cout<<setw(10)<<name<<setw(10)<<idnum<<setw(10)<<grade<<setw(10)<<salary<<endl;}intemployee::totalnum=1000; //靜態(tài)成員初始化classsalesperson:virtualpublicemployee{ //派生類salesperson,單一繼承,虛基類protected:floatsales; //當月銷售額public:salesperson();~salesperson();voidgetpay(char*name="",floatsales=0);};12/23/20245inlinesalesperson::salesperson(){this->sales=0;cout<<name<<"銷售員工已經(jīng)聘任"<<endl;}inlinesalesperson::~salesperson(){cout<<name<<"銷售員工已經(jīng)離職"<<endl;}voidsalesperson::getpay(char*name,floatsales) //同名函數(shù)getpay覆蓋基類employee::getpay{strcpy(this->name,name);this->sales=sales;salary=salary+grade*1000+sales*0.1;cout<<"銷售員工信息已經(jīng)重新設(shè)置"<<endl;}classmanager:virtualpublicemployee{ //派生類manager,單一繼承employee,虛基類protected:floatmonsalary;//固定月薪public:manager();~manager();voidgetpay(char*name="");};12/23/20246inlinemanager::manager(){monsalary=5000;cout<<name<<"經(jīng)理已經(jīng)聘任"<<endl;}inlinemanager::~manager(){cout<<name<<"經(jīng)理已經(jīng)離職"<<endl;}voidmanager::getpay(char*name) //manager::getpay覆蓋基類employee中的同名函數(shù)getpay{strcpy(this->name,name);salary=salary+monsalary+grade*1000;cout<<"經(jīng)理信息已經(jīng)重新設(shè)置"<<endl;}classsalesmanager:publicsalesperson,publicmanager{ //多重繼承,虛基類public:salesmanager();~salesmanager();voidgetpay(char*name="",floatsales=0);};inlinesalesmanager::salesmanager(){salary=8000;cout<<name<<"銷售經(jīng)理已經(jīng)聘任"<<endl;}inlinesalesmanager::~salesmanager(){cout<<name<<"銷售經(jīng)理已經(jīng)離職"<<endl;}voidsalesmanager::getpay(char*name,floatsales) //同名函數(shù)覆蓋{strcpy(this->name,name);salary=salary+monsalary+grade*1000+sales*0.1;cout<<"銷售經(jīng)理信息已經(jīng)重新設(shè)置"<<endl;}12/23/20247voidmain(){charname[20];intgrade;floatsales;employeeA;salespersonB;managerC;salesmanagerD;//注意輸出時顯示的多重派生情況下的虛基類和派生類構(gòu)造函數(shù)和析構(gòu)函數(shù)調(diào)用次序
cout<<"請輸入員工的姓名:";cin>>name;cout<<"請輸入"<<name<<"的提升級別:";cin>>grade;A.promote(grade);A.getpay(name);A.display();cout<<"請輸入銷售員工的姓名:";cin>>name;cout<<"請輸入"<<name<<"的提升級別:";cin>>grade;cout<<"請輸入"<<name<<"的銷售業(yè)績:";cin>>sales;B.promote(grade);B.getpay(name,sales);B.display();cout<<"請輸入經(jīng)理的姓名:";cin>>name;cout<<"請輸入"<<name<<"的提升級別:";cin>>grade;C.promote(grade);C.getpay(name);C.display();cout<<"請輸入銷售經(jīng)理的姓名:";cin>>name;cout<<"請輸入"<<name<<"的提升級別:";cin>>grade;cout<<"請輸入"<<name<<"的銷售業(yè)績:";cin>>sales;D.promote(grade);D.getpay(name,sales);D.display();}12/23/202484.運行結(jié)果基礎(chǔ)員工已經(jīng)聘任基礎(chǔ)員工已經(jīng)聘任銷售員工已經(jīng)聘任基礎(chǔ)員工已經(jīng)聘任經(jīng)理已經(jīng)聘任基礎(chǔ)員工已經(jīng)聘任銷售員工已經(jīng)聘任經(jīng)理已經(jīng)聘任銷售經(jīng)理已經(jīng)聘任請輸入員工的姓名:zhang請輸入zhang的提升級別:2員工信息已經(jīng)重新設(shè)置姓名編號級別工資zhang100125000請輸入銷售員工的姓名:wang請輸入wang的提升級別:3請輸入wang的銷售業(yè)績:80000銷售員工信息已經(jīng)重新設(shè)置姓名編號級別工資wang1002314000請輸入經(jīng)理的姓名:li請輸入li的提升級別:4經(jīng)理信息已經(jīng)重新設(shè)置姓名編號級別工資li1003412000請輸入銷售經(jīng)理的姓名:zhao請輸入zhao的提升級別:4請輸入zhao的銷售業(yè)績:96000銷售經(jīng)理信息已經(jīng)重新設(shè)置姓名編號級別工資zhao1004426600zhao銷售經(jīng)理已經(jīng)離職zhao經(jīng)理已經(jīng)離職zhao銷售員工已經(jīng)離職zhao基礎(chǔ)員工已經(jīng)離職li經(jīng)理已經(jīng)離職li基礎(chǔ)員工已經(jīng)離職wang銷售員工已經(jīng)離職wang基礎(chǔ)員工已經(jīng)離職zhang基礎(chǔ)員工已經(jīng)離職12/23/20249上一章我們學習了類,類是進行面向?qū)ο蟪绦蛟O(shè)計的基礎(chǔ)。它能夠定義數(shù)據(jù)和對數(shù)據(jù)的操作,并通過不同的訪問權(quán)限,將類的接口和內(nèi)部實現(xiàn)分開,支持信息的封裝和隱藏。本章中,我們將介紹繼承的用法。代碼復(fù)用是C++?最重要的性能之一,它是通過類繼承機制來實現(xiàn)的。通過類繼承,我們可以復(fù)用基類的代碼,并可以在繼承類中增加新代碼或者覆蓋基類的成員函數(shù),為基類成員函數(shù)賦予新的意義,實現(xiàn)最大限度的代碼復(fù)用。12/23/20241010.1類的繼承與派生
10.1.1繼承與派生的概念現(xiàn)實世界中的許多事物是具有繼承性的。人們一般用層次分類的方法來描述它們的關(guān)系。如圖10.1是一個簡單的交通工具分類圖。12/23/202411圖10.1簡單的交通工具分類圖12/23/202412在這個圖中建立了一個層次結(jié)構(gòu),最高層是最普遍的,具有所有層次最一般的特征;下面每一層都比它的前一層更具體,低層含有高層的所有特性,同時也增加了一些內(nèi)容,與高層有細微的不同。它們之間是基類和派生類的關(guān)系,可以說交通工具類派生了汽車類,也可以說汽車類繼承了交通工具類。繼承與派生是一種現(xiàn)象的不同說法,例如,確定某一交通工具是客車后,沒有必要指出它是汽車,因為客車本身就是從汽車派生出來的,它繼承了這一特性。同樣也不必指出它是交通工具,因為客車都是交通工具。繼承是C++?的一種重要機制,這一機制使得程序員可以在已有類的基礎(chǔ)上建立新類。從而擴展程序功能、體現(xiàn)類的多態(tài)性特征。12/23/202413面向?qū)ο蟪绦蛟O(shè)計允許聲明一個新類作為某一個類的派生。派生類(也稱子類)可以聲明新的屬性(成員)和新的操作(成員函數(shù))。繼承可以重用父類代碼而專注于為子類編寫新代碼。我們稱最初的類為基類,根據(jù)基類生成的新類稱為派生類(子類),這種派生可以是多層次的。下面我們通過例子進一步說明為什么要使用繼承?,F(xiàn)有一個person類,它包含name(姓名)、age(年齡)、sex(性別)等數(shù)據(jù)成員與成員函數(shù)display(),如下所示:classperson{private:charname[10];intage;charsex;public:voiddisplay();};12/23/202414假如現(xiàn)在要聲明一個employee類,它包含name(姓名)、age(年齡)、sex(性別)、department(部門)、salary(工資)等數(shù)據(jù)成員與成員函數(shù)display(),如下所示:classemployee{private:charname[10];intage;charsex;chardepartment[20];floatsalary;public:voiddisplay();};12/23/202415從以上兩個類的聲明中可以看出,這兩個類中的數(shù)據(jù)成員和成員函數(shù)有許多相同的地方。只要在person類的基礎(chǔ)上再增加成員department和salary,再對display()成員函數(shù)稍加修改就可以定義出employee類?,F(xiàn)在這樣定義兩個類,代碼重復(fù)太嚴重。為了提高代碼的可重用性,就必須引入繼承機機制,將employee類說明成person類的派生類,那些相同的成員在employee類中就不需要再定義了,簡化了程序設(shè)計。//下面定義一個派生類(employee類)classemployee:publicperson{private:chardepartment[20];floatsalary;public://需要增加的其他數(shù)據(jù)成員或函數(shù)成員};12/23/20241610.1.2派生類的聲明
聲明一個派生類的一般格式為class派生類名:派生方式基類名{
…//派生類新增的數(shù)據(jù)成員和成員函數(shù)}這里,“派生類名”就是要生成的新的類名,新類名可由用戶任意給出,只要符合標識符的命名規(guī)則即可?!盎惷笔且粋€已經(jīng)定義過的類?!芭缮绞健笨梢允顷P(guān)鍵字public、private或protected。如果使用了private,則稱派生類從基類私有派生;如果使用了public,則稱派生類從基類公有派生;如果使用了protected,則稱派生類從基類保護派生。公有派生、私有派生和保護派生方式有以下特點:(1)公有派生:基類的公有成員和保護成員作為派生類的成員時,它們都保持原有的狀態(tài),而基類的私有成員仍然是私有的(這種私有性,不同于派生類自身的私有成員,派生類的新增成員函數(shù)不能訪問它,派生類可以調(diào)用繼承自基類的非私有成員函數(shù),也就是可調(diào)用基類原來的公有或派生函數(shù)來訪問它,見表10.1)。(2)私有派生:基類的公有成員和保護成員都作為派生類的私有成員,并且不能被這個派生類的子類所訪問。基類的私有成員仍然是私有的。缺省繼承方式為private。(3)保護派生:基類的所有公有成員和保護成員都成為派生類的保護成員,并且只能被它的派生類成員函數(shù)或友元訪問,基類的私有成員仍然是私有的。12/23/202417表10.1給出了這幾種派生方式的訪問特性。派生方式基類中的訪問屬性派生類中的訪問屬性公有派生(public)publicprotectedprivatepublicprotected不可訪問私有派生(private)publicprotectedprivateprivateprivate不可訪問保護派生(protected)publicprotectedprivateprotectedprotected不可訪問表10.1公有派生、私有派生和保護派生的訪問屬性12/23/202418下面,我們給出一個實例來說明派生類對基類的訪問屬性。例10.1派生類對基類的訪問屬性。例程代碼如下:#include<iostream.h>classA{public:voidfa();protected:inta1;private:inta2;};classB:publicA{public:voidfb();protected:intb1;private:intb2;};classC:publicB{public:voidfc();};12/23/202419針對該程序,我們提出如下問題:(1)?B中成員函數(shù)fb()能否訪問基類A中的成員:fa()、a1、a2?(2)?B的對象B1能否訪問A的成員?(3)?C的成員函數(shù)fc()能否訪問直接基類B中的成員:fb()、b1、b2?(4)派生類C的對象C1是否可以訪問直接基類B的成員?能否訪問間接基類A的成員:fa()、a1、a2?根據(jù)表10.1,對以上問題的回答如下:(1)?B中成員函數(shù)fb()可以訪問fa()、a1,不可訪問a2;(2)?B的對象B1可以直接訪問A的成員fa(),不可訪問b1、b2;(3)?C的成員函數(shù)fc()可以訪問fb()、b1、fa()、a1,不可訪問a2、a2;(4)派生類C的對象C1可以直接訪問fb()、fa(),其他的都不可以直接訪問。12/23/202420下面我們分別討論私有派生、公有派生和保護派生的一些特性。1.私有派生1)私有派生類對基類成員的訪問由私有派生得到的派生類,對它的基類的公有成員只能是私有繼承。也就是說基類的所有公有成員和保護性成員都只能成為私有派生類的私有成員,這些私有成員能夠被派生類的成員函數(shù)訪問,但是基類私有成員不能被派生類成員函數(shù)訪問。下面是一個私有派生類對基類成員的訪問的例子。12/23/202421例10.2私有派生類對基類成員的訪問。#include<iostream.h>classBase{ //聲明一個基類
intx;public:voidsetx(intn){x=n;}voiddisplayx(){cout<<x<<endl;}};classDerived:privateBase{ //聲明一個私有派生類
inty;public:voidsety(intn){y=n;}voiddisplayxy(){cout<<x<<y<<endl;} //非法,派生類不能訪問基類的私有成員};12/23/202422例中首先定義了一個類Base,它有一個私有數(shù)據(jù)x和兩個公有成員函數(shù)setx()和displayx()。將Base類作為基類,派生出一個類Derived。派生類Derived私有繼承了基類的成員,Base類的私有成員x在Derived類中不可訪問,Base類的公有成員函數(shù)在Derived類中是私有的屬性,可訪問。如果將例中函數(shù)displayxy()改成如下形式:voiddisplayxy(){displayx();cout<<y<<endl;}則正確??梢娀愔械乃接谐蓡T既不能被外部函數(shù)訪問,也不能被派生類成員函數(shù)訪問,只能被基類自己的成員函數(shù)訪問。因此,我們在設(shè)計基類時,總要為它的私有數(shù)據(jù)成員提供公有成員函數(shù)作為接口,以使派生類和外部函數(shù)可以間接使用這些數(shù)據(jù)成員。12/23/2024232)外部函數(shù)對私有派生類繼承來的成員的訪問私有派生時,基類的所有成員在派生類中都成為私有成員,外部函數(shù)不能訪問。通過下面的例子來說明外部函數(shù)對私有派生類繼承來的成員的訪問特性。12/23/202424例10.3外部函數(shù)對私有派生類繼承來的成員的訪問特性。#include<iostream.h>classBase{
intx;public:voidsetx(intn)
{x=n;}voiddisplayx()
{cout<<x<<endl;}};classDerived:privateBase{inty;public:voidsety(intn)
{y=n;}voiddisplayy()
{cout<<y<<endl;}};main(){Derivedobj;obj.setx(10);//非法
obj.sety(20);//合法
obj.displayx();//非法
obj.displayy();//合法}12/23/202425例中派生類Derived繼承了基類Base的成員。但由于是私有派生,所以基類Base的公有成員setx()和displayx()被Derived私有繼承后,成為Derived的私有成員,只能被Derived的成員函數(shù)訪問,不能被外界函數(shù)訪問。在main()函數(shù)中,定義了派生類Derived的對象obj,由于sety()和displayy()在類Derived中是公有函數(shù),所以對obj.sety()和obj.displayy()的調(diào)用是沒有問題的,但是對obj.setx()和obj.displayx()的調(diào)用是非法的,因為這兩個函數(shù)在類Derived中已成為私有成員,不能通過Derived的對象來調(diào)用,可以通過Derived的對象中設(shè)計的公有成員函數(shù)來訪問基類的保護性或公有性的成員。12/23/2024262.公有派生在公有派生時,基類成員的可訪問性在派生類中維持不變,基類中的私有成員在派生類中仍是私有成員,不允許外部函數(shù)和派生類中的成員函數(shù)直接訪問,派生類的公有成員函數(shù)內(nèi)部可以通過調(diào)用基類的公有性或基類的保護性函數(shù)來訪問基類的私有成員,外部函數(shù)可以調(diào)用基類對象的公有性函數(shù)來訪問基類的私有成員?;愔械墓谐蓡T和保護成員在派生類中仍是公有成員和保護成員,派生類的成員函數(shù)可以直接訪問,外部函數(shù)僅可訪問基類中的公有成員和派生類的公有成員。下面我們看一個有關(guān)公有派生的例子。12/23/202427例10.4聲明公有派生。#include<iostream.h>classBase{intx;public:voidsetx(intn){x=n;}voiddisplayx(){cout<<x<<endl;}};classDerived:publicBase{inty;public:voidsety(intn){y=n;}voiddisplayy(){cout<<y<<endl;}};main(){Derivedobj;obj.setx(10); //合法
obj.sety(20); //合法
obj.displayx(); //合法
obj.displayy(); //合法}12/23/202428在派生類中聲明的名字可以屏蔽基類中聲明的同名的名字,即如果在派生類的成員函數(shù)中直接使用該名字的話,則表示使用派生類中聲明的名字,例如:classX{public:intf();};classY:publicX{public:intf();intg();};voidY::g(){f();//表示被調(diào)用的函數(shù)是Y::f(),而不是X::f()}對于派生類的對象的引用,也有相同的結(jié)論,例如:Yobj;obj.f();//被調(diào)用的函數(shù)是Y::f()如果要使用基類中聲明的名字,則應(yīng)使用作用域運算符限定,例如:Obj.X::f();//被調(diào)用的函數(shù)是X::f()12/23/2024293.保護派生前面講過,無論私有派生還是公有派生,派生類無權(quán)訪問它的基類的私有成員,派生類要想使用基類的私有成員,只能通過調(diào)用基類的成員函數(shù)的方式來實現(xiàn),也就是使用基類所提供的接口來實現(xiàn)。這種方式對于需要頻繁訪問基類私有成員的派生類而言,使用起來非常不便,每次訪問都需要進行函數(shù)調(diào)用。C++?提供了具有另外一種訪問屬性的成員—protected成員,該成員可以讓派生類訪問基類的保護成員。保護成員可以被派生類的成員函數(shù)訪問,但是對于外界是隱藏的,外部函數(shù)不能訪問它。保護派生時基類的所有公有成員和保護成員都成為派生類的保護成員,并且只能被它的派生類成員函數(shù)或友元訪問,基類的私有成員仍然是私有的。下面給出保護派生的例子。12/23/202430例10.5聲明保護派生。#include<iostream.h>classBase{intx;public:voidsetx(intn){x=n;}voiddisplayx(){cout<<x<<endl;}};classDerived:protectedBase{inty;public:voidsety(intn){y=n;}voiddisplayy(){cout<<y<<endl;}};main(){Derivedobj;obj.setx(10); //非法,setx(intn)在派生類對象obj中是保護性函數(shù),外界無法訪問
obj.sety(20); //合法
obj.displayx(); //非法,原因同obj.setx(10);obj.displayy(); //合法}12/23/202431本例中派生類Derived繼承了基類Base的成員。但由于是保護派生,所以基類Base的公有成員setx()和displayx()被Derived保護繼承后,成為Derived的保護成員,只能被Derived的成員函數(shù)訪問,不能被外界函數(shù)訪問。在main()函數(shù)中,定義了派生類Derived的對象obj,由于sety()和displayy()在類Derived中是公有函數(shù),所以對obj.sety()和obj.displayy()的調(diào)用是沒有問題的,但是對obj.setx()和obj.displayx()的調(diào)用是非法的,因為這兩個函數(shù)在類Derived中已成為保護成員。12/23/20243210.2派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)
構(gòu)造函數(shù)不能被繼承。若派生類無構(gòu)造函數(shù),則執(zhí)行其基類的默認構(gòu)造函數(shù)。否則,派生類的構(gòu)造函數(shù)除了對自己新的成員初始化外,還必須調(diào)用基類的構(gòu)造函數(shù)來對基類的數(shù)據(jù)成員初始化。由于構(gòu)造函數(shù)可以帶參數(shù),所以派生類必須根據(jù)基類的情況來決定是否需要定義構(gòu)造函數(shù)。12/23/20243310.2.1構(gòu)造和析構(gòu)的次序
通常情況下,當創(chuàng)建派生類對象時,首先執(zhí)行基類的構(gòu)造函數(shù),隨后再執(zhí)行派生類的構(gòu)造函數(shù);當撤銷派生類的對象時,則先執(zhí)先派生類的析構(gòu)函數(shù),隨后再執(zhí)行基類的析構(gòu)函數(shù)。下列程序的運行結(jié)果,反映了基類和派生類的構(gòu)造函數(shù)與析構(gòu)函數(shù)的執(zhí)行順序。12/23/202434例10.6基類和派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)的執(zhí)行順序。#include<iostream.h>classBase{public:Base(){cout<<"基類的構(gòu)造函數(shù)"<<endl;}~Base(){cout<<"基類的析構(gòu)函數(shù)"<<endl;}};classDerived:publicBase{public:Derived(){cout<<"派生類的構(gòu)造函數(shù)"<<endl;}~Derived(){cout<<"派生類的析構(gòu)函數(shù)"<<endl;}};main(){Derivedobj;}程序運行結(jié)果如下:基類的構(gòu)造函數(shù)派生類的構(gòu)造函數(shù)派生類的析構(gòu)函數(shù)基類的析構(gòu)函數(shù)12/23/20243510.2.2派生類構(gòu)造函數(shù)和析構(gòu)函數(shù)的構(gòu)造規(guī)則
當基類的構(gòu)造函數(shù)沒有參數(shù),或沒有顯示定義構(gòu)造函數(shù)時,派生類可以不向基類傳遞參數(shù),如果派生類自身不需要初始化自身成員,甚至可以不定義構(gòu)造函數(shù)。但由于派生類不能繼承基類中的構(gòu)造函數(shù)和析構(gòu)函數(shù),當基類含有帶參數(shù)的構(gòu)造函數(shù)時,派生類必須定義構(gòu)造函數(shù),以提供把參數(shù)傳遞給基類構(gòu)造函數(shù)的途徑。在C++?中,派生類構(gòu)造函數(shù)的一般格式為派生類構(gòu)造函數(shù)名(參數(shù)表):基類構(gòu)造函數(shù)名(參數(shù)表){//…}其中基類構(gòu)造函數(shù)的參數(shù),通常來源于派生類構(gòu)造函數(shù)的參數(shù)表,也可以用常數(shù)值。下面給出一個派生類構(gòu)造函數(shù)給基類構(gòu)造函數(shù)傳遞參數(shù)的例子。12/23/202436例10.7派生類構(gòu)造函數(shù)給基類構(gòu)造函數(shù)傳遞參數(shù)。#include<iostream.h>classBase{intx;public:Base(inta){cout<<"基類的構(gòu)造函數(shù)"<<endl;x=a;}~Base(){cout<<"基類的析構(gòu)函數(shù)"<<endl;}voiddisplayx(){cout<<x<<endl;}};classDerived:publicBase{inty;public:Derived(inta,intb):Base(a)//派生類的構(gòu)造函數(shù),要綴上基類的構(gòu)造函數(shù)
{cout<<"派生類的構(gòu)造函數(shù)"<<endl;y=b;}~Derived(){cout<<"派生類的析構(gòu)函數(shù)"<<endl;}voiddisplayy(){cout<<y<<endl;}};main(){Derivedobj(10,20);obj.displayx();obj.displayy();}12/23/202437程序運行結(jié)果如下:基類的構(gòu)造函數(shù)派生類的構(gòu)造函數(shù)1020派生類的析構(gòu)函數(shù)基類的析構(gòu)函數(shù)12/23/202438當派生類中含有對象成員時,派生類必須負責該對象成員的構(gòu)造,其構(gòu)造函數(shù)的一般形式為派生類構(gòu)造函數(shù)名(參數(shù)表):基類構(gòu)造函數(shù)名(參數(shù)表),對象成員名1(參數(shù)表),…,對象成員名n(參數(shù)表){//…}其中基類構(gòu)造函數(shù),對象成員的參數(shù),通常來源于派生類構(gòu)造函數(shù)的參數(shù)表,也可以用常數(shù)值。在定義含有對象成員的派生類對象時,構(gòu)造函數(shù)執(zhí)行順序如下:(1)基類的構(gòu)造函數(shù);(2)對象成員的構(gòu)造函數(shù);(3)派生類的構(gòu)造函數(shù)。12/23/202439撤銷這個對象時,析構(gòu)函數(shù)的執(zhí)行順序與構(gòu)造函數(shù)正好相反。下面給出一個例子進一步說明含有對象成員的派生類構(gòu)造函數(shù)的執(zhí)行情況。例10.8含有對象成員的派生類構(gòu)造函數(shù)的執(zhí)行情況。#include<iostream.h>classBase{intx;public:Base(inta){cout<<"基類的構(gòu)造函數(shù)"<<endl;x=a;}~Base(){cout<<"基類的析構(gòu)函數(shù)"<<endl;}voiddisplayx(){cout<<x<<endl;}};classDerived:publicBase{public:Based; //d為基類對象,作為派生類的對象成員
Derived(inta,intb):Base(a),d(b) //派生類的構(gòu)造函數(shù),綴上基類構(gòu)造函數(shù)和對象成員的構(gòu)造函數(shù)
{cout<<"派生類的構(gòu)造函數(shù)"<<endl;}~Derived(){cout<<"派生類的析構(gòu)函數(shù)"<<endl;}};main(){Derivedobj(10,20);obj.displayx();obj.d.displayx();}12/23/202440程序運行結(jié)果如下:基類的構(gòu)造函數(shù)基類的構(gòu)造函數(shù)派生類的構(gòu)造函數(shù)1020派生類的析構(gòu)函數(shù)基類的析構(gòu)函數(shù)基類的析構(gòu)函數(shù)說明:(1)當基類構(gòu)造函數(shù)不帶參數(shù)時,派生類不一定要定義構(gòu)造函數(shù),然而當基類的構(gòu)造函數(shù)哪怕是只帶有一個參數(shù),它所有的派生類都必須定義構(gòu)造函數(shù),甚至構(gòu)造函數(shù)的函數(shù)體可能為空,僅起參數(shù)傳遞的作用。(2)若基類使用缺省構(gòu)造函數(shù)或不帶參數(shù)的構(gòu)造函數(shù),則在派生類中定義構(gòu)造函數(shù)時可略去“:基類構(gòu)造函數(shù)名(參數(shù)表)”,若派生類也不需要構(gòu)造函數(shù),則可以不定義構(gòu)造函數(shù)。(3)每個派生類只需負責其直接基類的構(gòu)造。12/23/20244110.3多重繼承前面介紹的派生類只有一個基類,這種派生稱為單一繼承;當一個派生類具有多個基類時,這種派生方式稱為多重繼承。如圖10.2所示就是一個多重繼承的例子。該例中玩具槍有玩具和槍兩個基類,因此同時具備玩具和槍的特性。圖10.2多重繼承的例子12/23/20244210.3.1多重繼承的聲明
在C++?中,聲明具有兩個以上基類的派生類與聲明單一繼承的形式相似,其聲明的一般形式如下:class派生類名:派生方式1基類名1,…,派生方式n基類名n{//派生類新定義成員};冒號后面的部分稱基類表,各基類之間用逗號分割,其中派生方式缺省為private。在多重繼承中,各種派生方式對于基類成員在派生類中的可訪問性與單一繼承的規(guī)則相同。12/23/202443例10.9聲明多重派生。#include<iostream.h>classBase1{inta;public:voidsetBase1(intx){a=x;}voiddisplayBase1(){cout<<"a="<<a<<endl;}};classBase2{
intb;public:voidsetBase2(inty){b=y;}voiddisplayBase2(){cout<<"b="<<b<<endl;}};12/23/202444classDerive:publicBase1,privateBase2{intc;public:voidsetDerive(intx,inty){c=x;setBase2(y);}voiddisplayDerive(){displayBase2();cout<<"c="<<c<<endl;}};voidmain(){Deriveobj;obj.setBase1(3);obj.displayBase1();obj.setBase2(4); //非法
obj.displayBase2(); //非法
obj.setDerive(6,8);obj.displayDerive();}12/23/202445上面的程序中,類Base1和類Base2是兩個基類,Derive從Base1和Base2多重派生。從派生方式看,類Derive從Base1公有派生,而從Base2私有派生。根據(jù)派生的規(guī)則,類Base1的公有成員在類Derive中仍是公有成員,類Base2的公有成員在類Derive中是私有成員,所以在main()函數(shù)中不能訪問。對基類的訪問必須是無二義性的。多重繼承時,若基類具有同名數(shù)據(jù)成員或函數(shù),則要防止出現(xiàn)二義性。例如下例就具有二義性。12/23/202446例10.10多重繼承時存在二義性的情況。#include<iostream.h>classBase1{public:voiddisplay(){}};classBase2{public:voiddisplay(){}};classDerive:publicBase1,privateBase2{public:voiddisplayDerive(){}};voidmain(){Deriveobj;obj.display();//二義性錯誤,不知調(diào)用的是Base1的display()還是Base2的display()}12/23/202447編譯時就會提示下面的錯誤:'Derive::display'isambiguous。那么如何避免多重繼承時存在的二義性呢?使用成員名限定可以消除二義性,例如:obj.Base1::display();obj.Base2::display();12/23/20244810.3.2多重繼承的構(gòu)造函數(shù)多重繼承構(gòu)造函數(shù)的定義形式與單繼承構(gòu)造函數(shù)的定義形式相似,只是n個基類的構(gòu)造函數(shù)之間用逗號分隔,在多個基類之間,則嚴格按照派生類聲明時從左到右的順序來排列先后。多重繼承構(gòu)造函數(shù)定義的一般形式如下:派生類構(gòu)造函數(shù)名(參數(shù)表):基類構(gòu)造函數(shù)名1(參數(shù)表),基類構(gòu)造函數(shù)名2(參數(shù)表),…,基類構(gòu)造函數(shù)名n(參數(shù)表){//派生類中其他數(shù)據(jù)成員初始化}12/23/202449例10.11多重繼承構(gòu)造函數(shù)。#include<iostream.h>classBase1{inta;public:Base1(intx){a=x;}voiddisplayBase1(){cout<<"a="<<a<<endl;}};classBase2{
intb;public:Base2(inty){b=y;}voiddisplayBase2(){cout<<"b="<<b<<endl;}};classDerive:publicBase1,publicBase2{intc;public:Derive(intx,inty,intz):Base1(x),Base2(y) //派生類Derive的構(gòu)造函數(shù),綴上基類//Base1和Base2的構(gòu)造函數(shù)
{c=z;}voiddisplayDerive(){cout<<"c="<<c<<endl;}};12/23/202450voidmain(){Deriveobj(1,2,3);obj.displayBase1();obj.displayBase2();obj.displayDerive();}12/23/20245110.4虛基類
10.4.1虛基類及其使用的原因當引用派生類的成員時,首先在派生類自身的作用域中尋找這個成員,若沒有找到,則在它的基類中尋找。若一個派生類是從多個基類派生出來的,而這些基類又有一個共同的基類,則在這個派生類中訪問這個共同的基類中的成員時,可能會產(chǎn)生二義性。12/23/202452例10.12多重派生產(chǎn)生二義性的情況。#include<iostream.h>classBase{protected:inta;public:Base(){a=5;}};classBase1:publicBase{public:Base1(){cout<<"Base1a="<<a<<endl;}};classBase2:publicBase{public:Base2(){cout<<"Base2a="<<a<<endl;}};classDerived:publicBase1,publicBase2{public:Derived(){cout<<"Deriveda="<<a<<endl;}圖10.3例10.12中類的層次關(guān)系圖};main(){Derivedobj;}12/23/202453上述程序中,類Base是一個基類,從Base派生出類Base1和類Base2,這是兩個單一繼承;從類Base1和類Base2共同派生出類Derived,這是一個多重繼承。類的層次關(guān)系如圖10.3所示。圖10.3例10.12中類的層次關(guān)系圖12/23/202454上述程序是有錯誤的,問題出在派生類Derived的構(gòu)造函數(shù)定義中,它試圖輸出一個它有權(quán)訪問的變量a,表面上看來這是合理的,但實際上它對a的訪問存在二義性,即函數(shù)中的變量a的值可能是從類Base1的派生路徑上來的,也有可能是從類Base2的派生路徑上來的,這里沒有明確的說明。二義性檢查在訪問控制權(quán)限或類型檢查之前進行,訪問控制權(quán)限不同或類型不同不能解決二義性問題。為了解決這種二義性問題,C++?引入了虛基類的概念。12/23/20245510.4.2虛基類的定義
在例10.12中,如果類Base只存在一個拷貝,那么對a的引用就不會產(chǎn)生二義性。在C++?中,如果想使這個公共的基類只產(chǎn)生一個拷貝,則可以將這個基類說明為虛基類。這就要求從類Base派生新類時,使用關(guān)鍵字virtual引出。我們在下面的例子中用虛基類重新定義例10.12中的類。12/23/202456例10.13定義虛基類。#include<iostream.h>classBase{protected:inta;public:Base(){a=5;}};classBase1:virtualpublicBase{//聲明Base為虛基類public:Base1(){cout<<"Base1a="<<a<<endl;}};classBase2:virtualpublic
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 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. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 《收入分配的決定》課件
- 2.1《改造我們的學習》課件 2024-2025學年統(tǒng)編版高中語文選擇性必修中冊
- 內(nèi)環(huán)境穩(wěn)態(tài)-課件
- 安徽省亳州市2025屆高考數(shù)學四模試卷含解析
- 13.3《 自己之歌(節(jié)選)》課件 2023-2024學年統(tǒng)編版高中語文選擇性必修中冊
- 2025屆廣東省佛山市四校高三沖刺模擬英語試卷含解析
- 2025屆德陽市重點中學高三最后一模英語試題含解析
- 八年級英語FamilylivesVocabulary課件
- 2025屆甘肅省宕昌縣第一中高考英語倒計時模擬卷含解析
- 天津市武清區(qū)等五區(qū)縣2025屆高考英語一模試卷含解析
- 技術(shù)總監(jiān)年度述職報告
- 幼兒園大班科學課件:《植物的生長》
- DB3212T 2002-2019 鵝性能測定操作技術(shù)規(guī)程
- 中國老年糖尿病診療指南(2024)解讀
- 湖南高速鐵路職業(yè)技術(shù)學院單招職業(yè)技能測試參考試題庫(含答案)
- 小品碰瓷的所有臺詞
- 山西省忻州地區(qū)2023-2024學年八年級上學期期末數(shù)學試題
- 河南省南陽市鄧州市2023-2024學年七年級上學期期末數(shù)學試題(含答案)
- 《測繪管理法律與法規(guī)》課件-測繪標準化
- 《沃森克里克》課件
- 譯林版小學六年級Unit7單元測試卷
評論
0/150
提交評論