版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第4章繼承本章主要教學(xué)內(nèi)容繼承的基本原理和實(shí)現(xiàn)技術(shù),繼承方式:公有繼承,保護(hù)繼承,私有繼承,多繼承,虛擬繼承。派生類與基類對象之間的關(guān)系:賦值相容和類型轉(zhuǎn)換派生類構(gòu)造函數(shù)如何提供對基類構(gòu)造函數(shù)的調(diào)用。通過繼承與組合進(jìn)行程序設(shè)計(jì)本章教學(xué)重點(diǎn)學(xué)會用繼承解決類之間層次結(jié)構(gòu)關(guān)系,進(jìn)行代碼復(fù)用,提高軟件開發(fā)效率。繼承方式,派生類與基類成員的關(guān)系,及其對基類成員的使用、重載、訪問權(quán)限更改和函數(shù)功能修改繼承體系中靜態(tài)成員的功能和設(shè)計(jì)方法派生類構(gòu)造函數(shù)、復(fù)制構(gòu)造函數(shù)、賦值運(yùn)算符函數(shù)設(shè)計(jì),派生類、對象成員和基類構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用次序教學(xué)難點(diǎn)派生類的構(gòu)造函數(shù)、復(fù)制構(gòu)造函數(shù)、賦值函數(shù)、移動(dòng)賦值函數(shù)設(shè)計(jì)派生類與基數(shù)對象的類型轉(zhuǎn)換與對象復(fù)制、賦值之間的區(qū)別和相關(guān)成員函數(shù)設(shè)計(jì)多繼承、多對象成員的派生類構(gòu)造函數(shù)設(shè)計(jì)和調(diào)用問題虛擬繼承方式下的派生對象和基類對象構(gòu)造函數(shù)的設(shè)計(jì)和執(zhí)行過程分析1.繼承的概念以現(xiàn)有的類為基礎(chǔ)定義新的類,新類即捅有基類的數(shù)據(jù)成員和成員函數(shù)的一份復(fù)制品,這就是繼承。Personchar*name;char*id_card_no;boolgender;Studentcharstudent_no;Dateenroll_date;……FacultyDatehire_date;Degreedegree;……AdministratorDatehire_date;Rankrank;……4.1繼承的概念繼承方式:單一繼承:只有一基類多重繼承:可以有多個(gè)基類基類也叫超類,父類派生類也叫子類2、繼承目的代碼重用coderesue描述能力:類屬關(guān)系廣泛存在IsAvs.HasA3、有關(guān)概念 基類,超類派生類,子類4.1繼承的概念4、派生類可實(shí)施的對基類的改變增加新的數(shù)據(jù)成員和成員函數(shù)。重載基類的成員函數(shù)。重定義(覆蓋)基類已有的成員函數(shù)。改變基類成員在派生類中的訪問屬性。5、派生類不能繼承基類的以下內(nèi)容基類的構(gòu)造函數(shù)C++11之后可以繼承析構(gòu)函數(shù)?;惖挠言瘮?shù)。靜態(tài)數(shù)據(jù)成員和靜態(tài)成員函數(shù)4.1繼承的概念基類中protected的成員類內(nèi)部:可以訪問類的使用者:不能訪問類的派生類成員:可以訪問【例4-1】類B有數(shù)據(jù)成員i,j,k,希望j可被派生類D和自身訪問,但不希望除此之外的其它函數(shù)訪問。分析:protected權(quán)限正好具有這樣的訪問控制能力4.2protected和繼承classB{ private:inti; protected:intj; public:intk;};classD:publicB{public: voidf() { i=1;//cannotaccess j=2; k=3; }};voidmain(){ Bb; b.i=1;//cannotaccess b.j=2;//cannotaccess b.k=3;}Kkiif()接口私有數(shù)據(jù)BDjj4.3.繼承方式1、C++的繼承方式公有繼承、保護(hù)繼承和私有繼承,也稱為公有派生、保護(hù)派生和私有派生。不同繼承方式會不同程度地改變基類成員在派生類中的訪問權(quán)限2、繼承語法形式classB{……};classD:[private|protected|public]B{ ……};基類子對象派生類新定義成員繼承部分派生部分派生類對象1、public繼承最常用的派生方式,派生類復(fù)制了基類數(shù)據(jù)成員和成員函數(shù)的一份復(fù)制品。派生類從基類繼承到的成員,維持基類成員的可訪問性。即基類的public成員在派生類中也是public成員,可被派生類的外部函數(shù)訪問。同樣,一個(gè)成員若在基類是protected或private屬性,它在派生類中仍然是protected或private屬性在任何情況下,類的private成員只能被自身類的成沒訪問。因此,派生類不可直接訪問基類的private成員4.3.繼承方式例題4_2.cpp,派生類Derived繼承了基類Base的數(shù)據(jù)成員x和全部成員函數(shù)classBase{ intx;public: voidsetx(intn){ x=n; } intgetx(){ returnx;} voidshowx() { cout<<x<<endl;}};classDerived:publicBase{ inty;public: voidsety(intn){ y=n; } voidsety(){ y=getx();} voidshowy() { cout<<y<<endl;}};Setx()Getx()Showx()xSetx()Getx()Showx()xSety()Gety()Showy()y接口私有數(shù)據(jù)basederivedvoidmain(){ Derivedobj;
obj.setx(10); cout<<obj.getx();; obj.showy(); obj.sety(); obj.sety(20);obj.showy();obj.showy();}4.3.繼承方式Derived類并未定義setx等成員函數(shù),但卻調(diào)用了它們。原因是它從Base類繼承了這些函數(shù)。2.Private基類的中的public、protected成員在派生類private,private成員在派生類中不可訪問?!纠克接欣^承的例子#include<iostream>usingnamespacestd;classBase{intx;public:voidsetx(intn){x=n;}intgetx(){returnx;}voidshowx(){cout<<x<<endl;}};4.3.繼承方式2、Private繼承classDerived:privateBase{
……}在private派生方式下,派生復(fù)制了基類全部成員,但復(fù)制到的成員在派生類中全部變成了private成員。在private派生方式下,雖然基類的public和protected成員在派生類中都變成了private成員,但它們?nèi)匀挥袇^(qū)別:派生類的成員函數(shù)不能直接訪問基類的private成員,但可以直接訪問基類的public和protected成員,并且通過它們訪問基類本身的private成員。4.3.繼承方式classderived:privatebase{ inty;public: voidsety(intn){y=n; } voidsety(){ y=getx();} voidshowy() { cout<<y<<endl;}};voidmain(){ derivedobj; obj.setx(10);//cannotaccess obj.sety(20); obj.showx();//cannotaccess obj.showy(); }Setx()Getx()Showx()xSetx()Getx()Showx()xSety()Gety()Showy()y接口私有數(shù)據(jù)basederived3、protected繼承classDerived:protectedBase{
……}在protected派生方式下,派生復(fù)制了基類全部成員,但復(fù)制到public成員在派生類中變成了protected成員,其余成員的訪問權(quán)限保持不變。在派生方式下,派生類的繼承到的成員函數(shù)都不能被外部函數(shù)訪問,但在派生類中可以直接訪問基類的public和protected成員,并且通過它們訪問基類本身的private成員。4.3.繼承方式classDerived:protectedBase{inty;public:voidsety(intn){y=n;}voidsety(){y=getx();}//訪問基類的保護(hù)成員
voidshowy(){cout<<y<<endl;}};voidmain(){Derivedobj;obj.setx(10);//錯(cuò)誤obj.showx(); //錯(cuò)誤,
obj.sety(20);obj.showy();}Protected繼承方式已將setx,showx改變成了protected成員,不能在派生類外直接訪問4.3.繼承方式#include<iostream>usingnamespacestd;classBase{intx;protected:intgetx(){returnx;}public:voidsetx(intn){x=n;}voidshowx(){cout<<x<<endl;}};基類成員在派生類中的訪問權(quán)限不能繼承的基類內(nèi)容1.構(gòu)造函數(shù)
C++11標(biāo)準(zhǔn)以前(C++11起可以繼承)2、析構(gòu)函數(shù)3.友員關(guān)系4.針對基類定義的一些特殊運(yùn)算符,如new等。派生類基類public繼承protected繼承private繼承publicprotectedprivatepublicprotectedprivatepublicprotectedprivatepublic√
√
√protected
√
√
√private
√
√
√4.阻止繼承
11C++如果不想讓一個(gè)類作為其它類的基類,可以用final關(guān)鍵字阻止它被繼承。形式如下:classBase{……}//可以被繼承classNoDerifinal{……}//不能被繼承classDfinal:Base{……}//正確,D不能被繼承classD1:NoDeri{……}//錯(cuò)誤,NoDeri不能被繼承classD2:D{……}//錯(cuò)誤,D不能被繼承4.3.繼承方式派生類不可以()。訪問基類的public成員訪問基類的private成員訪問基類的protected成員繼承基類的構(gòu)造函數(shù)ABCD提交單選題1分1、派生類和基類的關(guān)系派生類拷貝了基類數(shù)據(jù)成員和成員函數(shù)的一份副本,不用編程就具備了基類的程序功能。在派生類對象中,具有一個(gè)基類子對象。classBase {basemembers;};classDerived:Base {newmembers;};basemembersnewmembersthis指針Derivedmembers4.4派生類對基類的擴(kuò)展2、派生類和基類成員的修改和擴(kuò)展派生類可以在繼承基類成員的基礎(chǔ)上派生類可以增加新的數(shù)據(jù)成員和成員函數(shù);重載從基類繼承到的成員函數(shù);覆蓋(重定義)從基類繼承到的成員函數(shù);改變基類成員在派生類中的訪問屬性。3、派生類不能繼承基類的以下成員析構(gòu)函數(shù)?;惖挠言瘮?shù)。靜態(tài)數(shù)據(jù)成員和靜態(tài)成員函數(shù)。4.4派生類對基類的擴(kuò)展4.4.1成員函數(shù)的重定義和名字隱藏1、成員函數(shù)的重定義和名字隱藏派生類對基類成員函數(shù)的重定義或重載會影響基類成員函數(shù)在派生類中的可見性,基類的同名成員函數(shù)會被派生類重載的同名函數(shù)所隱藏?!纠?-3】設(shè)計(jì)計(jì)算矩形與立方體面積和體積的類。(1)問題分析矩形具有長和寬,面積=長×寬,沒有體積,可以設(shè)置為0具有高的矩形就是立方體,面積=2×底面積+2×側(cè)面積1+2×側(cè)面積2,體積=底面積×高。其中的底面積就是矩形的面積。立方體是對矩形的擴(kuò)展,矩形完成了長和寬的處理,在此基礎(chǔ)上完成高的處理就能夠?qū)崿F(xiàn)其功能。這一關(guān)系可以通過繼承實(shí)現(xiàn)。4.4.1成員函數(shù)的重定義和名字隱藏(2)數(shù)據(jù)抽象將矩形抽象成類Rectangle,用數(shù)據(jù)成員width、length表寬與長,為了方便其派生類訪問數(shù)據(jù)成員,將它們設(shè)置為protected訪問權(quán)限;并設(shè)置成員函數(shù)setWidth、setLength、getWidth、getLength來設(shè)置和獲取矩形的寬和長,area和volume成員函數(shù)計(jì)算矩形的面積和體積,成員函數(shù)outData輸出矩形的長和寬。將立方體抽象成類Cube,并從Rectangle類派生,由于Rectangle類已經(jīng)完成了矩形長和寬的處理功能,所以只須增加數(shù)據(jù)成員high表示高,并設(shè)置setHigh和getHigh成員函數(shù)完成對高的讀、寫功能。雖然Rectangle類已經(jīng)設(shè)置了area、volume和outData成員函數(shù)計(jì)算面積、體積,或輸出數(shù)據(jù)成員的值,但立方體的面積、體積和數(shù)據(jù)成員是不同的,需要重新定義它們矩形和立方體的抽象結(jié)果注意Cube對基類繼承到的成員函數(shù)的重定義會隱藏繼承于基類的同名函數(shù)4.4.1成員函數(shù)的重定義和名字隱藏//Eg4-3.cpp#include<iostream>usingnamespacestd;classRectangle{public: voidsetLength(doubleh){length=h;} voidsetWidth(doublew){width=w;} doublegetLength(){returnlength;} doublegetWidth(){returnwidth;} doublearea(){returnlength*width;} doublevolume(){return0;} voidoutData(){cout<<"lenggh="<<length<<”\t”<<"width="<<width<<endl;}protected: doublewidth; doublelength;};4.4.1成員函數(shù)的重定義和名字隱藏classCube:publicRectangle{public: voidsetHigh(doubleh){high=h;} doublegetHigh(){returnhigh;} doublearea(){returnwidth*length*2+width*high*2+length*high*2;}//L1 doublevolume(){returnRectangle::area()*high;} voidoutData(){ Rectangle::outData();//L2派生類訪問基類同名成員的方法 cout<<"high="<<high<<endl; }private: doublehigh;};4.4.1成員函數(shù)的重定義和名字隱藏voidmain(){ Cubecub1; cub1.setLength(4); cub1.setWidth(5); cub1.setHigh(3);
cub1.Rectangle::outData();//訪問基類繼承到的同名成員 cub1.outData();//訪問派生類的同名成員 cout<<"立方體面積="<<cub1.area()<<endl; cout<<"立方體底面積="<<cub1.Rectangle::area()<<endl; cout<<"立方體體積="<<cub1.volume()<<endl;}4.4.1成員函數(shù)的重定義和名字隱藏派生類對基類成員的訪問有以下形式通過派生類對象直接訪問基類成員,如cub1.setWidth(5);在派生類成員函數(shù)中直接訪問基類成員,如前面Cube類的L1語句。通過基類名字限定訪問被重載的基類成員名在派生類成員函數(shù)中訪問基類同名成員函數(shù)如Cube的volume()和area()成員函數(shù)在派生對象中訪問基類同名成員函數(shù)cub1.Rectangle::outData();
4.4.2基類成員訪問4.4.3using與隱藏函數(shù)重現(xiàn)
11C++1.派生對基類同名成員的隱藏如果基類某個(gè)成員函數(shù)具有多個(gè)重載的函數(shù)版本,派生類又定義了同名成員,就會隱藏基類同名的全部重載函數(shù)。2.訪問隱藏成員一是使用基類名稱限定要訪問的成員函數(shù)重載基類的所有同名函數(shù),而這些重載函數(shù)的代碼與基類完全相同用using聲明使基類重載函數(shù)在派生類中可見。用法如下:
using基類名稱::被隱藏成員函數(shù)名;【例4-4】基類B的f1成員函數(shù)具有3個(gè)重載函數(shù),派生D新增加了f1函數(shù)的功能,此f1會隱藏基類B中f1函數(shù)在派生類的可見性,用using將基類的f1引入到派生類作用域內(nèi)。#include<iostream>usingnamespacestd;classB{public: voidf1(inta){cout<<a<<endl;} voidf1(inta,intb){cout<<a+b<<endl;} voidf1(){cout<<"B::f1"<<endl;}};classD:publicB{ public:
usingB::f1;//L1,使基類的3個(gè)f1函數(shù)在此區(qū)域可見 voidf1(char*d){cout<<d<<endl;}};voidmain(){ Dd;
d.f1(); //L2,正確,調(diào)用基類成員 d.f1(3); //L3,正確,調(diào)用基類成員 d.f1(3,5); //L4,正確,調(diào)用基類成員 d.f1("Hellowc++!");}4.4.4派生類修改基類成員的訪問權(quán)限1.修改原由不同繼承方式可能會改變基類成員在派生類中的訪問權(quán)限。比如,在private繼承方式下,基類的public和protected成員在派生類中的訪問權(quán)限都會被更改為private訪問權(quán)限。在某些時(shí)候,從類的整體設(shè)計(jì)上考慮,需要調(diào)整個(gè)別基類成員在派生類中的訪問權(quán)限,使用using聲明可以實(shí)現(xiàn)這一目的。2.修改方法在派生類的public、protected或pirvate權(quán)限區(qū)域內(nèi),使用using再次聲明基類的非private成員,就可以重新設(shè)置它們在派生類中的權(quán)限為using語句所在區(qū)域的權(quán)限。即using語句在public區(qū)域內(nèi)即為public權(quán)限,在protected內(nèi)即為protected權(quán)限,在private內(nèi)則為private權(quán)限。3.限定內(nèi)容
在派生類中不允許修改基類的private成員4.4.4派生類修改基類成員的訪問權(quán)限【例4-5】類D私有繼承了類Base,修改基類Base成員在派生類中的訪問權(quán)限,設(shè)置基類成員y在派生類的權(quán)限為private,其余成員在派生類中的權(quán)限保持與其在基類中的相同權(quán)限。#include<iostream>usingnamespacestd;classBase{public: intx=0; voidsetxyz(inta,doubleb,floatc){ x=a;y=b;z=c; }protected: doubley=0; floatgetZ(){returnz;}private: floatz=0; };classD:privateBase{protected:
usingBase::getZ;//指定getz為protected權(quán)限
//usingBase::z;//錯(cuò)誤,不允許修改基類private成員public:
usingBase::x;//指定x為public權(quán)限
usingBase::setxyz;//指定setxyz為public權(quán)限 voiddisplay(){ cout<<"x="<<x<<"\ty="<<y<<"\tz="<<getZ()<<endl; } private:
usingBase::y;//指定y為private權(quán)限};voidmain(){ Dd; d.setxyz(8,9,10); d.display();}4.4.4派生類修改基類成員的訪問權(quán)限Private繼承使基類成員在派生類中都成私有成員4.4.5友元與繼承友元不能被繼承每個(gè)類只能夠負(fù)責(zé)控制自已的成員的訪問權(quán)限。因此,如果一個(gè)類繼承了其它類,則它聲明的友元也只能訪問它自己的全體成員,包括它從基類繼承到的public和protected成員。而它的基類和派生類并不認(rèn)可這種友元關(guān)系,按照規(guī)則只能訪問公有成員?!纠?-6】類Deri是基類Base的友元,函數(shù)f1和f2是類Deri的友元,分析下面程序中L4、L5、L7正確的原因,以及L8和L6錯(cuò)誤的原因。4.4.5友元與繼承#include<iostream>usingnamespacestd;classBase{public: intx=0;protected: doubley=0;private: floatz=0;
friendclassDeri;//L1Deri為Base的友元};//Base的全體成員可訪問x,y,zclassDeri:publicBase{protected:intdx=1;public:
friend voidf1(Derid);
friendvoidf2(Baseb); voidf3(Baseb) {cout<<b.x<<b.y<<b.z<<endl; }//正確};voidf1(Derid){
cout<<d.x<<d.y<<d.dx<<endl;//正確 //cout<<d.z<<endl;//錯(cuò)誤}voidf2(Baseb){
cout<<b.x<<endl;//正確
//cout<<b.y<<endl;//錯(cuò)誤}voidmain(){ Baseb; Derid; f1(d); f2(b);}classBase{public:intx=0;protected:doubley=0;private:floatz=0;
friendclassDeri;};4.4.6靜態(tài)成員與繼承1.基類靜態(tài)成員為繼承層次結(jié)構(gòu)所有類共享在繼承體系中,如果基類定義了靜成成員,則在整個(gè)繼承體系中只有該成員的唯一定義,不論從該基類派生出了多少個(gè)或多少層次的派生類,靜態(tài)成員都只有一個(gè)實(shí)例,為整個(gè)繼承體系中的全體對象所共用。2.基類靜態(tài)成員在繼承結(jié)構(gòu)中的應(yīng)用設(shè)計(jì)全類公用數(shù)據(jù),或統(tǒng)計(jì)繼承體系中的對象個(gè)數(shù):將共享數(shù)據(jù)或計(jì)數(shù)器設(shè)置為基類的靜態(tài)成員,就能夠?qū)崿F(xiàn)這樣的目的。4.4.6靜態(tài)成員與繼承【例4-7】假設(shè)父親生了兒子、女兒,兒子又生有孫子,構(gòu)成了家族繼承體系,統(tǒng)計(jì)家族成員的人數(shù)。問題分析:用Father、Son、Daugther、Frandson分別表示父親類、兒子類、女兒類和孫子類,它們通過繼承形成了層次結(jié)構(gòu)的繼承體系。數(shù)據(jù)抽象在Father類中設(shè)計(jì)靜態(tài)成員personNum統(tǒng)計(jì)家族的人數(shù),每構(gòu)造一個(gè)對象人數(shù)就增加1,每析構(gòu)一個(gè)對象就減少1;由于每個(gè)人都有姓名,因此在基類Father中設(shè)置name數(shù)據(jù)成員代表人名。為了便于派生類訪問personNum和name成員,把它們設(shè)置為protected訪問權(quán)限。4.4.6靜態(tài)成員與繼承#include<iostream>#include<string>usingnamespacestd;classFather{protected: stringname;
staticintpersonNum;public: Father(stringName=""):name(Name){personNum++;} ~Father(){personNum--;} staticintgetPersonNumber(){ returnpersonNum; }};intFather::personNum=0;classSon:publicFather{public: Son(stringname):Father(name){}};classDaugther:Father{public: Daugther(stringname):Father(name){}};classGrandson:publicSon{public: Grandson(stringname):Son(name){}};voidmain(){ Fatherson("tom"); Sonsson("jack"); Daugtherdson("mike"); { Grandsongson("s.jack"); cout<<son.getPersonNumber()<<endl;//L1,輸出4 } cout<<son.getPersonNumber()<<endl;//L2,輸出3}到L1語句時(shí),總共定義了4個(gè)對象,都記在了由基類定義的共享靜態(tài)成員personNum中,因此L1語句輸入44.4.7繼承與類作用域1.基類類域每個(gè)類都建立了屬于自己的作用域,本類的全體成員都位于此作用域內(nèi),而且相互之間可以直接訪問,不受定義先后次序的影響。例如,一個(gè)成員函數(shù)可以調(diào)用在它后面定義的另一個(gè)成員函數(shù)。2.派生類類域派生類的作用域嵌套在基類作用域的內(nèi)層。在解析類成員名稱時(shí),如果在本類的作用域內(nèi)沒有找到,編譯器就會接著在外層的基類作用域內(nèi)繼續(xù)尋找該成員名稱的定義。4.4.7繼承與類作用域3.編譯器解析之后的派生類類域形式例如類A、B、C繼承形式如下classA{ intg();……};classB:publicA{ inth(int);……};classC:publicB{ intc; inth(); intf(int);……};4.4.7繼承與類作用域經(jīng)編譯器處理之后,形成類似于下面的塊作用域:A{intg(){……}……
B{ inth(int){……};
……
C{ intc; inth(){……};intf(inti){……;returnB::h(i);}//L1 …… }
}}Cxa;xa.g();xa.h(3);//L2,錯(cuò)誤xa.B::h(3);//L3,正確思考題:1.分析xa.g()的調(diào)用過程?2.分析L2錯(cuò)誤的原因?4.5構(gòu)造函數(shù)和析構(gòu)函數(shù)1.為什么要設(shè)計(jì)構(gòu)造函數(shù)?在任何時(shí)候,只要定義類的對象,就需要調(diào)用適當(dāng)?shù)臉?gòu)造函數(shù)。因此,設(shè)計(jì)類時(shí)必須要考慮類的構(gòu)造函數(shù)設(shè)計(jì)(包括派生類)。有時(shí),一個(gè)類沒有構(gòu)造函數(shù)也在使用,這種情況只能定義無參對象,而且它調(diào)用了編譯器為它生成的默認(rèn)構(gòu)造函數(shù)(這種情況一定符合編譯器為類自動(dòng)生成默認(rèn)構(gòu)造函數(shù)的情況)。2.如何設(shè)計(jì)構(gòu)造函數(shù)在用類定義對象時(shí),通常會用到默認(rèn)構(gòu)造函數(shù)(定義無參對象或?qū)ο髷?shù)組)、拷貝構(gòu)造函數(shù)(類對象作函數(shù)參數(shù)),賦值運(yùn)算符函數(shù)(對象賦值),移動(dòng)構(gòu)造函數(shù),移動(dòng)拷貝構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)符函數(shù),當(dāng)類沒有定義任何構(gòu)造函數(shù)時(shí),編譯器在需要來會自動(dòng)為類生成這些成員函數(shù)。在通常情況下,由編譯器生成的上述成員函數(shù)已能夠勝任對象的定義或復(fù)制了,即可以不定義這些成員函數(shù)。但是,當(dāng)類存在指針數(shù)據(jù)成員時(shí),就很有可能需要顯示定義這些成員函數(shù),否則很有可能產(chǎn)生指針懸掛問題。4.5.1派生類構(gòu)造函數(shù)的建立規(guī)則1.派生類必須為基類和對象成員提供構(gòu)造函數(shù)初值。派生類只能采用構(gòu)造函數(shù)初始化列表的方式向基類構(gòu)造函數(shù)提供初值。派生類可以通過類內(nèi)初始值或構(gòu)造函數(shù)初始化列表向?qū)ο蟪蓡T提供構(gòu)造函數(shù)初值。形式如下:派生類構(gòu)造函數(shù)名(參數(shù)表):基類構(gòu)造函數(shù)名(參數(shù)表),對象成員名1(參數(shù)表),……{
……}【例4-071-ctor】派生類Derived以構(gòu)造函數(shù)初始化列表的方式向基類構(gòu)造函數(shù)提供參數(shù)。#include<iostream>usingnamespacestd;classBase{private:intx;public:Base(inta){x=a;cout<<"Bctorx="<<x<<endl;}~Base(){cout<<“~Bdctor..."<<x<<endl;}};4.5.1派生類構(gòu)造函數(shù)的建立規(guī)則classDerived:publicBase{private:inty;public:Derived(inta,intb):Base(a){初始化列表
y=b;cout<<"Derctory="<<y<<endl;}~Derived(){cout<<“~Ddctor."<<endl;}};voidmain(){Derivedd(1,2);}2、派生類必須定義構(gòu)造函數(shù)的情況當(dāng)基類或成員對象(未進(jìn)行類內(nèi)初始化)所屬類只含有帶參數(shù)的構(gòu)造函數(shù)時(shí),即使派生類本身沒有數(shù)據(jù)成員要初始化。派生類構(gòu)造函數(shù)以初始化列表的方式向基類和成員對象的構(gòu)造函數(shù)傳遞參數(shù),以實(shí)現(xiàn)基類子對象和成員對象的初始化。4.5.1派生類構(gòu)造函數(shù)的建立規(guī)則【例4.8】派生類構(gòu)造函數(shù)的定義。#include<iostream>usingnamespacestd;classPoint{protected:intx,y;public:
Point(inta,intb=0){x=a;y=b;cout<<"constructingpoint("<<x<<","<<y<<")"<<endl;}};4.5.1派生類構(gòu)造函數(shù)的建立規(guī)則classLine:publicPoint{protected:intlen;public:Line(inta,intb,intl):Point(a,b){ //構(gòu)造函數(shù)初始化列表
len=l;cout<<"ConstructingLine,len..."<<len<<endl;}};voidmain(){LineL1(1,2,3);}Line類必須在構(gòu)造函數(shù)列表中向基類Point類構(gòu)造函數(shù)提供初值!4.5.1派生類構(gòu)造函數(shù)的建立規(guī)則3、派生類可以不定義構(gòu)造函數(shù)的情況當(dāng)具有下述情況之一時(shí),派生類可以不定義構(gòu)造函數(shù)?;悰]有定義任何構(gòu)造函數(shù)。基類具有缺省參數(shù)的構(gòu)造函數(shù)。基類具有無參構(gòu)造函數(shù)?!纠?-9】類A具有默認(rèn)構(gòu)造函數(shù),其派生類B沒有成員要初始化,不必定義構(gòu)造函數(shù)。4.5.1派生類構(gòu)造函數(shù)的建立規(guī)則//Eg4-9.cpp#include<iostream>usingnamespacestd;classA{public:A(){cout<<"ConstructingA"<<endl;}~A(){cout<<"DestructingA"<<endl;}};classB:publicA{public:~B(){cout<<"DestructingB"<<endl;}};voidmain(){Bb;}4.5.1派生類構(gòu)造函數(shù)的建立規(guī)則程序運(yùn)行結(jié)果:ConstructingADestructingBDestructingA此結(jié)果表明,在定義對b時(shí),調(diào)用了編譯器為類B自動(dòng)合成的默認(rèn)構(gòu)造函數(shù),此函數(shù)類似于下面的形式:B::B():A(){}3、派生類的構(gòu)造函數(shù)只負(fù)責(zé)直接基類的初始化C++語言標(biāo)準(zhǔn)有一條規(guī)則:如果派生類的基類同時(shí)也是另外一個(gè)類的派生類,則每個(gè)派生類只負(fù)責(zé)它的直接基類的構(gòu)造函數(shù)調(diào)用?;惖哪J(rèn)構(gòu)造函數(shù)可以被自動(dòng)調(diào)用。這條規(guī)則表明當(dāng)派生類的直接基類只有帶參數(shù)的構(gòu)造函數(shù),但沒有默認(rèn)構(gòu)造函數(shù)時(shí)(包括缺省參數(shù)和無參構(gòu)造函數(shù)),它必須在構(gòu)造函數(shù)的初始化列表中調(diào)用其直接基類的構(gòu)造函數(shù),并向基類的構(gòu)造函數(shù)傳遞參數(shù),以實(shí)現(xiàn)派生類對象中的基類子對象的初始化。這條規(guī)則有一個(gè)例外情況,當(dāng)派生類存在虛基類時(shí),所有虛基類都由最后的派生類負(fù)責(zé)初始化。4.5.1派生類構(gòu)造函數(shù)的建立規(guī)則class
A{public:A(int){}};class
B:A{public:B(int
i):A(i){}};class
C:B{C(int
j):B(j){}};【例4-10】類C具有直接基類B和間接基類A,每個(gè)派生類只負(fù)責(zé)其直接基類的構(gòu)造。//Eg4-10.cpp#include<iostream>usingnamespacestd;classA{intx;public:A(intaa){x=aa;cout<<"ConstructingA"<<endl;}~A(){cout<<"DestructingA"<<endl;}};classB:publicA{public:B(intx):A(x){cout<<"ConstructingB"<<endl;}};classC:publicB{public:C(inty):B(y){cout<<"ConstructingC"<<endl;}};voidmain(){Cc(1);}ConstructingAConstructingBConstructingCDestructingA此運(yùn)行結(jié)果表明,在定義C的對象時(shí),基類A,B的構(gòu)造函數(shù)都被調(diào)用了5.派生類繼承基類的構(gòu)造函數(shù)11C++關(guān)于構(gòu)造函數(shù)繼承C++11新增加標(biāo)準(zhǔn)(以前不允許繼承構(gòu)造函數(shù))解決的問題:當(dāng)基類具有多個(gè)重載構(gòu)造函數(shù),或構(gòu)造函數(shù)具有較多參數(shù),而派生類又沒有數(shù)據(jù)成員需要初始化,但它卻必須提供構(gòu)造函數(shù),其唯一目的是為基類構(gòu)造函數(shù)提供初始化值。在這種情況下,可以派生類可以繼承直接基類的構(gòu)造函數(shù)。4.5.1派生類構(gòu)造函數(shù)的建立規(guī)則構(gòu)造函數(shù)繼承方法用using在派生類中聲明基類構(gòu)造函數(shù)名即可。形式如下:classBase:{……}classDerived:[public]Base{//也可以是private或protected繼承
……
usingBase::Base;//繼承基類構(gòu)造函數(shù)}4.5.1派生類構(gòu)造函數(shù)的建立規(guī)則例4-11】類A具有數(shù)據(jù)成員x,y,并且定義了初始化它們的構(gòu)造函數(shù);類B從A派生,沒有任何成員要初始化;類C從類B派生,具有新定義數(shù)據(jù)成員c。設(shè)計(jì)A、B、C的構(gòu)造函數(shù)。4.5.1派生類構(gòu)造函數(shù)的建立規(guī)則問題分析:按照規(guī)則,類B雖然沒有數(shù)據(jù)成員要初始化,但是它必須為基類A的構(gòu)造函數(shù)提供初值(除非A具有默認(rèn)構(gòu)造函數(shù))現(xiàn)在,可以通過繼承A的構(gòu)造函數(shù)使問題更簡單。類C要定義構(gòu)造函數(shù)以便初始化其成員c,同時(shí)還必須為直接基類B提供構(gòu)造初值。AA(int,int)BB(int,int):A(int,int)CC(int,int):B(int,int)///Eg4-11.cpp#include<iostream>usingnamespacestd;classA{ intx,y;public:
A(intaa):x(aa){cout<<"ConstructingA:x=\t"<<x<<endl;}
A(inta,intb):x(a),y(b){ cout<<"ConstructingA:x=\t"<<x<<endl; }};classB:publicA{public:
usingA::A;//L1/*B(intx):A(x){//L2cout<<"ConstructingB\t"<<endl;}*/};L1聲明類B繼承了A的構(gòu)造函數(shù),編譯會為類B自動(dòng)生成相應(yīng)的程序代碼,類似于:B::B(inta):A(a){}B::B(inta,intb):A(a,b){}不論類A有多少構(gòu)造函數(shù),系統(tǒng)都會自動(dòng)生成。如果沒有L1,就需像L2一樣為每個(gè)基類構(gòu)造函數(shù)提供程序代碼。classC:publicB{
usingB::B; //L3 intc;public: C(intx,inty,intz):B(x,y),c(z){//L4 cout<<"ConstructingC:\t"<<c<<endl; }};voidmain(){ Bb1(1),b2(8,9); //L5 Cc1(1),c2(3,4); //L6}4.5.1派生類構(gòu)造函數(shù)的建立規(guī)則C繼承了B的構(gòu)造函數(shù),省去了為B具有1個(gè)參數(shù)和2個(gè)參數(shù)的兩個(gè)構(gòu)造函數(shù)編寫程序代碼構(gòu)造繼承的幾點(diǎn)說明“Base::Base”即為基類名和基類構(gòu)造函數(shù)的名稱,using語句說明了派生類要繼承基類的構(gòu)造函數(shù)。如果基類有多個(gè)構(gòu)造函數(shù),則using語句會在派生類中為每個(gè)基類構(gòu)造函數(shù)生成一個(gè)與之對應(yīng)的構(gòu)造函數(shù),并具有與基類構(gòu)造函數(shù)相同的訪問權(quán)限。using不受訪問權(quán)限制約,放在public、protected或private區(qū)域中沒有區(qū)別。用using在派生類中聲明基類的構(gòu)造函數(shù)和其它成員有所不同,聲明其它成員只是使該成員在指定的派生類權(quán)限區(qū)域可見,并不生成代碼。而用using繼承基類構(gòu)造函數(shù),則會使編譯器在派生類中生成基類構(gòu)造函數(shù)的一份副本。基類的默認(rèn)構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)和移動(dòng)構(gòu)造函數(shù)不能夠被繼承若派生類在繼承基類構(gòu)造函數(shù)的同時(shí),還需要定義其它構(gòu)造函數(shù),必須在構(gòu)造函數(shù)初始化列表中為基類構(gòu)造函數(shù)提供初始化值(除非基類有默認(rèn)構(gòu)造函數(shù))。4.5.1派生類構(gòu)造函數(shù)的建立規(guī)則4.5.1派生類構(gòu)造函數(shù)的建立規(guī)則如果基類構(gòu)造函數(shù)具參數(shù)默認(rèn)值,繼承將為派生類生成多個(gè)構(gòu)造函數(shù),每個(gè)構(gòu)造函數(shù)的參數(shù)依次少一個(gè)。例如,classA{ intx,y;public: A(inta,intb=2):x(a),y(b){cout<<"a="<<a<<"\tb="<<b<<endl;}};classB:publicA{public:
usingA::A;};繼承將為類B生成構(gòu)造函數(shù):B(inta):A(a,2)和B(inta,intb):A(a,b)4.5.2構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用次序回顧3.11.4(P166):類對象成員的構(gòu)造先構(gòu)造對象成員再構(gòu)造自身(調(diào)用構(gòu)造函數(shù))例題ch.cppclassA{public: A(){cout<<"ConstructingA"<<endl;} ~A(){cout<<"DestructingA"<<endl;}};classB{public: B(){cout<<"ConstructingB"<<endl;} ~B(){cout<<"DestructingB"<<endl;}};classC{public: C(){cout<<"ConstructingC"<<endl;} ~C(){cout<<"DestructingC"<<endl;} Bb; Aa;};voidmain(){ Cc;}ConstructingBConstructingAConstructingCDestructingCDestructingADestructingB如果:classB:publicA{}classC:publicB{}結(jié)果又當(dāng)如何?4.5.2派生類構(gòu)造函數(shù)的定義1、派生類構(gòu)造函數(shù)的設(shè)計(jì)原則派生類可能有多個(gè)基類,也可能包括多個(gè)成員對象,在創(chuàng)建派生類對象時(shí),派生類的構(gòu)造函數(shù)除了要負(fù)責(zé)本類成員的初始化外,還要調(diào)用基類和成員對象的構(gòu)造函數(shù),并向它們傳遞參數(shù),以完成基類子對象和成員對象的建立和初始化。派生類只能采用構(gòu)造函數(shù)初始化列表的方式向基類構(gòu)造函數(shù)傳遞參數(shù)。但派生類可以通過構(gòu)造函數(shù)初始化列表,也可以通過類內(nèi)初始值向?qū)ο蟪蓡T傳遞構(gòu)造參數(shù)。形式如下:派生類構(gòu)造函數(shù)名(參數(shù)表):基類構(gòu)造函數(shù)名(參數(shù)表),成員對象名1(參數(shù)表),…{//……}2、構(gòu)造函數(shù)的調(diào)用原則和次序創(chuàng)建派生類對象時(shí),基類、對象成員及派生構(gòu)造函數(shù)都會被調(diào)用,調(diào)用次序:基類構(gòu)造函數(shù)→對象成員構(gòu)造函數(shù)→派生類構(gòu)造函數(shù)(1)當(dāng)有多個(gè)基類時(shí),按照它們在繼承方式中的聲明次序調(diào)用,與它們在構(gòu)造函數(shù)初始化列表中的次序無關(guān)。(2)當(dāng)有多個(gè)對象成員時(shí),將按它們在派生類中的聲明次序調(diào)用,與它們在構(gòu)造函數(shù)初始化列表中的次序無關(guān)。(3)當(dāng)構(gòu)造函數(shù)初始化列表中的基類和對象成員的構(gòu)造函數(shù)調(diào)用完成之后,才執(zhí)行派生類構(gòu)造函數(shù)體中的程序代碼。4.5.2派生類構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用次序class
A{};class
B{};class
C{
public:C(int){}};class
D:public
A,B{
Cc=C(1);Aa1,a2;public:
B():B(),A(),a2()a1(),{…}};【例4-12】類D從類B派生,并具有用類A和C建立的對象成員。分析創(chuàng)建D的對象時(shí),基類、對象成員和派生類構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用次序。//Eg4-12.cpp#include<iostream>usingnamespacestd;classA{ intx;public: A(inti=0):x(i){ cout<<"ConstructA----"<<x<<endl;} ~A(){cout<<"DesA----"<<x<<endl;}};4.5.2派生類構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用次序classB{ inty;public: B(inti):y(i){ cout<<"ConstructB----"<<y<<endl; } ~B(){cout<<"DesB----"<<y<<endl;}};classC{ intz;public: C(inti):z(i){ cout<<"ConstructC----"<<z<<endl; } ~C(){cout<<"DesC----"<<z<<endl;}};4.5.2派生類構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用次序classD:publicB{public:
Cc1,c2;
Aa0,a4;
D():a4(4),c2(2),c1(1),B(1){ cout<<"ConstructD----5“
<<endl;
}
~D(){cout<<"DesD----5"<<endl;}
};voidmain(){
Dd;}4.5.2派生類構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用次序運(yùn)行結(jié)果如下,分析每個(gè)輸出的來源ConstructB----1ConstructC----1ConstructC----2ConstructA----0ConstructA----4ConstructD----5DesD----5DesA----4DesA----0DesC----2DesC----1DesB----14.5.3派生類的賦值、拷貝和移動(dòng)操作1.派生類賦值、拷貝和移動(dòng)操作對基類的職責(zé)(1)派生類的賦值函數(shù)和拷貝構(gòu)造函數(shù),以及移動(dòng)賦值和移動(dòng)構(gòu)造函數(shù)不但要執(zhí)行派生類成員的拷貝和移動(dòng),而且還要負(fù)責(zé)基類部分?jǐn)?shù)據(jù)成員的拷貝和移動(dòng)。(2)如果一個(gè)類沒有定義賦值運(yùn)算、拷貝構(gòu)造函數(shù)、移動(dòng)賦值和移動(dòng)構(gòu)造函數(shù),編譯器將會為它們自動(dòng)生成對應(yīng)的函數(shù)版本。但以下兩種情況除外:當(dāng)一個(gè)類有虛析構(gòu)函數(shù)時(shí),即使沒有定義這些函數(shù),編譯器也不會合成它們。如果一個(gè)類定義了賦值運(yùn)算符或拷貝構(gòu)造函數(shù),編譯器也不會為它合成移動(dòng)賦值和移動(dòng)構(gòu)造函數(shù)。(3)派生類在定義賦值函數(shù)、拷貝構(gòu)造函數(shù)和它們的移動(dòng)函數(shù)版本時(shí),要負(fù)責(zé)對基類成員進(jìn)行相應(yīng)的處理,即應(yīng)當(dāng)調(diào)用基類與之對應(yīng)的賦值函數(shù)、拷貝構(gòu)造函數(shù)和移動(dòng)函數(shù)來完成基類成員的相應(yīng)處理。【例4-13】類A具有數(shù)據(jù)成員x,并定義了賦值函數(shù),拷貝構(gòu)造函數(shù)和它們的移動(dòng)函數(shù)版本,以實(shí)現(xiàn)對象間的賦值、拷貝或移動(dòng)操作,類B從類A派生,并有數(shù)據(jù)成員y。設(shè)計(jì)類B的賦值、拷貝構(gòu)造函數(shù)和移動(dòng)函數(shù),實(shí)現(xiàn)派生類B的對象間的賦值、拷貝和移動(dòng)操作。設(shè)計(jì)思路:
根據(jù)前面的規(guī)則,當(dāng)一個(gè)類設(shè)計(jì)了賦值運(yùn)算符函數(shù)、拷貝構(gòu)造函數(shù)和移動(dòng)函數(shù)時(shí),就需要在這些函數(shù)中提供對基類對應(yīng)函數(shù)的初始化支持。因此,在類B的相應(yīng)函數(shù)設(shè)計(jì)中,要提供對基類A對應(yīng)函數(shù)的初始化列表。4.5.3派生類的賦值、拷貝和移動(dòng)操作//Eg4-13.cpp#include<iostream>usingnamespacestd;classA{intx;public:A(inta=0,intb=2):x(a){}A&operator=(A&o){x=o.x;cout<<"InA=(A&)"<<endl;return*this;}A&operator=(A&&o)=default;//使用默認(rèn)的合成移動(dòng)賦值函數(shù)A(A&o):x(o.x){cout<<"InA(&)"<<endl;}A(A&&o):x(std::move(o.x)){cout<<"InA(&&)"<<endl;}};4.5.3派生類的賦值、拷貝和移動(dòng)操作classB:publicA{ inty;public: B(inta=0,intb=0):A(a),y(b){} B&operator=(B&o){A::operator=(o);cout<<"InB=(B&)"<<endl; return*this; } B&operator=(B&&o){A::operator=(std::move(0));cout<<"InB=(B&&)"<<endl; return*this;} B(B&o):A(o){cout<<"InB(&)"<<endl;} B(B&&o):A(std::move(o)){cout<<"InB(&&)"<<endl;}};4.5.3派生類的賦值、拷貝和移動(dòng)操作voidmain(){ Bb,b1(1,2); b=b1;//L1 Bb2(b);//L2 Bb3=std::move(B(8,9));//L3 b1=std::move(b3);//L4}程序運(yùn)行結(jié)果如下:InA=(A&)//L1的輸出InB=(B&)//L1的輸出InA(&)//L2的輸出InB(&)//L2的輸出InA(&&)//L3的輸出InB(&&)//L3的輸出InB=(B&&)//L4的輸出4.5.3派生類的賦值、拷貝和移動(dòng)操作4.6基類與派生類對象的關(guān)系1.派生對象與基類對象的賦值相容關(guān)系派生類通過繼承獲得了基類成員的一份拷貝,這份拷貝構(gòu)成了派生類對象內(nèi)部的一個(gè)基類子對象。因此,公有派生方式下,凡是需要基類對象的地方都可以使用派生類對象?;悓ο竽軌蚪鉀Q的問題,用派生類對象也能夠解決。稱為賦值相容。包括下面三種情況:把派生類對象賦值給基類對象;把派生類對象的地址賦值給基類指針;或者用派生類對象初始化基類對象的引用。2.派生類與基類賦值相容的處理方式因?yàn)槿魏我粋€(gè)派生類對象的內(nèi)部都包含有一個(gè)基類子對象,在進(jìn)行派生類對象向基類對象的賦值時(shí),C++采用截取的方法從派生類對象中復(fù)制其基類子對象并將之賦值給基類對象。4.6基類與派生類對象的關(guān)系4.6.1派生類對象對基類對象的賦值和初始化派生類中基類之間的對象復(fù)制關(guān)系以下兩種操作并不存在從派生類向基類的類型轉(zhuǎn)換,本質(zhì)上是執(zhí)行基類對象的復(fù)制構(gòu)造函數(shù)或賦值運(yùn)算符函數(shù),通過它們把派生類對象中從基類繼承到的數(shù)據(jù)成員復(fù)制給基類對象在把派生類對象賦值給基類對象用派生類對象初始化基類對象注意:不存在基類對象向派生類對象的復(fù)制關(guān)系【例4-14】類B從類A派生,設(shè)計(jì)類B的復(fù)制構(gòu)造函數(shù)和賦值運(yùn)算符函數(shù),并驗(yàn)證把派生對象賦值給基類對象或通過它初始化基類對象時(shí),相關(guān)函數(shù)的調(diào)用情況。//Eg4-14.cpp#include<iostream>usingnamespacestd;classA{ inta;public: voidsetA(intx){a=x;} intgetA(){returna;} A():a(0){cout<<"A::A()"<<endl;} A(A&o):a(o.a){cout<<"A::A(&o)"<<endl;} A&operator=(Ao){a=o.a;cout<<"A::operaotor="<<endl;return*this;}};classB:publicA{ intb;public: voidsetB(intx){b=x;} intgetB(){returnb;} B():b(0){cout<<"B::B()"<<endl;} B(B&o):b(o.b){cout<<"B::B(&o)"<<endl;} B&operator=(Bo){b=o.b;cout<<"B::operaotor="<<endl;return*this;}};voidmain(){ Aa1,*pA; Bb1,*pB; b1.setA(2); a1=b1; b1.setA(10); Aa2=b1;a2.setA(1); cout<<a1.getA()<<endl; //L1,輸出2 cout<<b1.getA()<<endl;
//L2,輸出10 cout<<a2.getA()<<endl;//L3,輸出1//a2.setB(5);//L4,錯(cuò)誤 //b1=a1; //L5,錯(cuò)誤}程序運(yùn)行結(jié)果如下:A::A()
A::A() B::B() A::A(&o)A::operaotor=A::A(&o)2101
請據(jù)上面的復(fù)制和賦值原則,分析此程序結(jié)果的函數(shù)調(diào)用情況
4.6.1派生類對象對基類對象的賦值和初始化aaba1b1aa24.6.2派生類對象與基類對象的類型轉(zhuǎn)換1.派生類和基類之間的類型轉(zhuǎn)換關(guān)系
(1)可以把派生類對象轉(zhuǎn)換成基類對象,不能把基類對象轉(zhuǎn)換成派生類對象(無法轉(zhuǎn)換出派生類新增加的成員)(2)派生類對象到基類對象的隱式類型轉(zhuǎn)換用派生類對象賦值或初始化基類對象時(shí),實(shí)際是通過賦值運(yùn)算符函數(shù)或拷貝構(gòu)造函數(shù)完成的,并沒有執(zhí)行類型轉(zhuǎn)換;當(dāng)把基類對象的指針或引用綁定到派生對象時(shí),編譯器會自動(dòng)執(zhí)行從派生類對象到基類對象的隱式類型轉(zhuǎn)換。例如,對于例4-14的基類A和派生類B,下面的語句段會發(fā)生類型轉(zhuǎn)換。Bb,b1,b2;A*pa=&b1;//正確,執(zhí)行派生類向基類的轉(zhuǎn)換A&rA=b2//正確,執(zhí)行派生類向基類的轉(zhuǎn)換
Aa=b;
//正確,沒有類型轉(zhuǎn)換,通過基類拷貝構(gòu)造函數(shù)初如化a注意:不論以哪種方式把派生類對象賦值給基類對象,都只能夠訪問到派生類對象中的基類子對象的成員,不能訪問派生類的自定義成員4.6.2派生類對象與基類對象的類型轉(zhuǎn)換(3)基類對象到派生類對象的類型轉(zhuǎn)換實(shí)際上,不能把基類對象直接轉(zhuǎn)換成派生類對象。但是,當(dāng)基類對象的指針或引用實(shí)際綁定的是一個(gè)派生類對象時(shí),則可以將它再次轉(zhuǎn)換成派生類對象。若要進(jìn)行上面所說的類型轉(zhuǎn)換,只能進(jìn)行強(qiáng)制類型轉(zhuǎn)換,編譯器是不會進(jìn)行這種轉(zhuǎn)換的隱式轉(zhuǎn)換的。例如,對例4-14中的基類A和派生類B,Aa,*pa;Bb,b1,b2,*pb,pa=&b1;//正確,執(zhí)行派生類向基類的轉(zhuǎn)換A&rA=b2//正確,執(zhí)行派生類向基類的轉(zhuǎn)換
b=a;//錯(cuò)誤,不允許從基類向派生類的轉(zhuǎn)換pb=pa;//錯(cuò)誤,不能把基類對象的地址賦值給指向派生類對象的指針B&rB=rA;//錯(cuò)誤,不能把基類對象作為派生類對象的引用pb=static_cast<B*>(pa);//正確,強(qiáng)制轉(zhuǎn)換B&rB=static_cast<B&>(rA);//正確,強(qiáng)制轉(zhuǎn)換4.6.2派生類對象與基類對象的類型轉(zhuǎn)換3.對象、指針和引用的區(qū)別把派生類對象賦值給基類對象或用派生類對象初始化基類對象,完成賦值或初始化操作后,基類對象與派生對象就沒有關(guān)系了把基類對象的指針或引用綁定到派生類對象時(shí),指針或引用從來就沒有生成新對象,它們操作的是派生類對象內(nèi)部的基類子對象。如對例4-14的基類A和派生類B,有下面的代碼段voidmain(){Bb,b1;Aa=b,*pa=&b1,&rA=b1;//L1b.setA(10);//L2a.setA(9);//L3pa->setA(20);//L4rA.setA(1);
溫馨提示
- 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)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 保險(xiǎn)業(yè)副總經(jīng)理聘用合同范例
- 通信公司石材施工合同
- 房地產(chǎn)公司會計(jì)聘用合同范本
- 公共交通設(shè)施租賃合同樣本
- 鐵路運(yùn)營HSE施工安全協(xié)議
- 遠(yuǎn)程培訓(xùn)學(xué)習(xí)總結(jié)
- 國慶節(jié)心得體會2022年經(jīng)典作文8篇
- 銷售述職報(bào)告15篇
- 英語教育實(shí)習(xí)報(bào)告范文合集九篇
- 上海城建職業(yè)學(xué)院《光學(xué)工程數(shù)值計(jì)算方法與CAD設(shè)計(jì)》2023-2024學(xué)年第一學(xué)期期末試卷
- 遼寧省2023年高中學(xué)業(yè)水平合格性考試政治試卷真題(答案詳解)
- 反無人飛機(jī)技術(shù)方案
- 《NBA介紹英文》課件
- 《mc入門教程》課件
- 人際溝通.從心開始(大學(xué)生人際交往)
- KYN28-12A開關(guān)柜技術(shù)規(guī)范書
- 化療藥物滲漏PDCA循環(huán)分析
- 國開《小學(xué)數(shù)學(xué)教學(xué)研究》形考任務(wù)二
- 維修方案模板
- 冶金行業(yè)特殊工種有哪些崗位
- 初中部綜合素質(zhì)評價(jià)實(shí)施方案范本(三篇)
評論
0/150
提交評論