C++語言程序設計七_第1頁
C++語言程序設計七_第2頁
C++語言程序設計七_第3頁
C++語言程序設計七_第4頁
C++語言程序設計七_第5頁
已閱讀5頁,還剩70頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、(最新整理)C+語言程序設計(清華大學鄭莉)七2021/7/261第七章 繼承與派生清華大學 鄭 莉C+語言程序設計2021/7/262本章主要內容類的繼承類成員的訪問控制單繼承與多繼承派生類的構造、析構函數類成員的標識與訪問深度探索2021/7/263類的繼承與派生保持已有類的特性而構造新類的過程稱為繼承。在已有類的基礎上新增自己的特性而產生新類的過程稱為派生。被繼承的已有類稱為基類(或父類)。派生出的新類稱為派生類。2021/7/264繼承與派生問題舉例類的繼承與派生2021/7/265繼承與派生問題舉例類的繼承與派生貓科2021/7/266繼承與派生問題舉例類的繼承與派生2021/7/2

2、67繼承與派生問題舉例類的繼承與派生2021/7/268繼承與派生的目的繼承的目的:實現代碼重用。派生的目的:當新的問題出現,原有程序無法解決(或不能完全解決)時,需要對原有程序進行改造。類的繼承與派生2021/7/269派生類的聲明class 派生類名:繼承方式 基類名 成員聲明;類的繼承與派生2021/7/2610繼承方式不同繼承方式的影響主要體現在:派生類成員對基類成員的訪問權限通過派生類對象對基類成員的訪問權限三種繼承方式公有繼承私有繼承保護繼承類成員的訪問控制2021/7/2611公有繼承(public)基類的public和protected成員的訪問屬性在派生類中保持不變,但基類的

3、private成員不可直接訪問。派生類中的成員函數可以直接訪問基類中的public和protected成員,但不能直接訪問基類的private成員。通過派生類的對象只能訪問基類的public成員。類成員的訪問控制2021/7/2612例7-1 公有繼承舉例class Point /基類Point類的定義public:/公有函數成員void initPoint(float x = 0, float y = 0) this-x = x; this-y = y;void move(float offX, float offY) x += offX; y += offY; float getX() c

4、onst return x; float getY() const return y; private:/私有數據成員float x, y;類成員的訪問控制2021/7/2613class Rectangle: public Point /派生類定義部分public:/新增公有函數成員void initRectangle(float x, float y, float w, float h) initPoint(x, y); /調用基類公有成員函數this-w = w;this-h = h;float getH() const return h; float getW() const retu

5、rn w; private:/新增私有數據成員float w, h;142021/7/2614#include #include using namespace std;int main() Rectangle rect;/定義Rectangle類的對象/設置矩形的數據rect.initRectangle(2, 3, 20, 10);rect.move(3,2);/移動矩形位置cout The data of rect(x,y,w,h): endl;/輸出矩形的特征參數cout rect.getX() , rect.getY() , rect.getW() , rect.getH() w =

6、w;this-h = h;void move(float offX, float offY) Point:move(offX, offY);float getX() const return Point:getX(); float getY() const return Point:getY(); float getH() const return h; float getW() const return w; private:/新增私有數據成員float w, h;類成員的訪問控制2021/7/2617#include #include using namespace std;int mai

7、n() Rectangle rect;/定義Rectangle類的對象rect.initRectangle(2, 3, 20, 10);/設置矩形的數據rect.move(3,2);/移動矩形位置cout The data of rect(x,y,w,h): endl;cout rect.getX() , /輸出矩形的特征參數 rect.getY() , rect.getW() , rect.getH() endl;return 0;182021/7/2618保護繼承(protected)基類的public和protected成員都以protected身份出現在派生類中,但基類的private

8、成員不可直接訪問。派生類中的成員函數可以直接訪問基類中的public和protected成員,但不能直接訪問基類的private成員。通過派生類的對象不能直接訪問基類中的任何成員類成員的訪問控制2021/7/2619protected 成員的特點與作用對建立其所在類對象的模塊來說,它與 private 成員的性質相同。對于其派生類來說,它與 public 成員的性質相同。既實現了數據隱藏,又方便繼承,實現代碼重用。類成員的訪問控制2021/7/2620例7-3 protected 成員舉例class A protected:int x;int main() A a;a.x = 5; /錯誤類成

9、員的訪問控制2021/7/2621class A protected:int x;class B: public Apublic:void function();void B:function() x = 5; /正確222021/7/2622類型兼容規則一個公有派生類的對象在使用上可以被當作基類的對象,反之則禁止。具體表現在:派生類的對象可以隱含轉換為基類對象。派生類的對象可以初始化基類的引用。派生類的指針可以隱含轉換為基類的指針。通過基類對象名、指針只能使用從基類繼承的成員類型兼容2021/7/2623例7-4 類型兼容規則舉例#include using namespace std;cl

10、ass Base1 /基類Base1定義public:void display() const cout Base1:display() endl;類型兼容2021/7/2624class Base2: public Base1 /公有派生類Base2定義public:void display() const cout Base2:display() endl;class Derived: public Base2 /公有派生類Derived定義public:void display() const cout Derived:display() display();/對象指針-成員名25202

11、1/7/2625int main() /主函數Base1 base1;/聲明Base1類對象Base2 base2;/聲明Base2類對象Derived derived;/聲明Derived類對象 /用Base1對象的指針調用fun函數fun(&base1);/用Base2對象的指針調用fun函數fun(&base2);/用Derived對象的指針調用fun函數fun(&derived);return 0;運行結果:B0:display()B0:display()B0:display()262021/7/2626基類與派生類的對應關系單繼承派生類只從一個基類派生。多繼承派生類從多個基類派生。多

12、重派生由一個基類派生出多個不同的派生類。多層派生派生類又作為基類,繼續派生新的類。單繼承與多繼承2021/7/2627多繼承時派生類的聲明class 派生類名:繼承方式1 基類名1,繼承方式2 基類名2,. 成員聲明;注意:每一個“繼承方式”,只用于限制對緊隨其后之基類的繼承。單繼承與多繼承2021/7/2628多繼承舉例class A public:void setA(int);void showA() const;private:int a;class B public:void setB(int);void showB() const;private:int b;class C : pu

13、blic A, private B public:void setC(int, int, int);void showC() const;private const:int c;單繼承與多繼承2021/7/2629void A:setA(int x) a=x; void B:setB(int x) b=x; void C:setC(int x, int y, int z) /派生類成員直接訪問基類的/公有成員setA(x); setB(y); c = z;/其他函數實現略int main() C obj;obj.setA(5);obj.showA();obj.setC(6,7,9);obj.s

14、howC();/ obj.setB(6); 錯誤/ obj.showB(); 錯誤return 0;302021/7/2630繼承時的構造函數基類的構造函數不被繼承,派生類中需要聲明自己的構造函數。定義構造函數時,只需要對本類中新增成員進行初始化,對繼承來的基類成員的初始化,自動調用基類構造函數完成。派生類的構造函數需要給基類的構造函數傳遞參數派生類的構造、析構函數2021/7/2631單一繼承時的構造函數派生類名:派生類名(基類所需的形參,本類成員所需的形參):基類名(參數表)本類成員初始化賦值語句;派生類的構造、析構函數2021/7/2632單一繼承時的構造函數舉例#includeusin

15、g namecpace std;class B public:B();B(int i);B();void print() const;private:int b;派生類的構造、析構函數2021/7/2633B:B() b=0;cout Bs default constructor called. endl;B:B(int i) b=i;cout Bs constructor called. endl;B:B() cout Bs destructor called. endl;void B:print() const cout b endl;342021/7/2634class C: publi

16、c B public:C();C(int i, int j);C();void print() const;private:int c;352021/7/2635C:C() c = 0;cout Cs default constructor called. endl;C:C(int i,int j): B(i) c = j;cout Cs constructor called. endl;C:C() cout Cs destructor called. endl;void C:print() const B:print();cout c endl;int main() C obj(5, 6);

17、obj.print();return 0;362021/7/2636多繼承時的構造函數派生類名:派生類名(參數表):基類名1(基類1初始化參數表), 基類名2(基類2初始化參數表), .基類名n(基類n初始化參數表) 本類成員初始化賦值語句;派生類的構造、析構函數2021/7/2637派生類與基類的構造函數當基類中聲明有缺省構造函數或未聲明構造函數時,派生類構造函數可以不向基類構造函數傳遞參數,也可以不聲明,構造派生類的對象時,基類的缺省構造函數將被調用。當需要執行基類中帶形參的構造函數來初始化基類數據時,派生類構造函數應在初始化列表中為基類構造函數提供參數。派生類的構造、析構函數2021/7

18、/2638多繼承且有內嵌對象時的構造函數派生類名:派生類名(形參表):基類名1(參數), 基類名2(參數), .基類名n(參數),新增成員對象的初始化 本類成員初始化賦值語句;派生類的構造、析構函數2021/7/2639構造函數的執行順序1 調用基類構造函數,調用順序按照它們被繼承時聲明的順序(從左向右)。2 對成員對象進行初始化,初始化順序按照它們在類中聲明的順序。3執行派生類的構造函數體中的內容。派生類的構造、析構函數2021/7/2640拷貝構造函數若建立派生類對象時沒有編寫拷貝構造函數,編譯器會生成一個隱含的拷貝構造函數,該函數先調用基類的拷貝構造函數,再為派生類新增的成員對象執行拷貝

19、。若編寫派生類的拷貝構造函數,則需要為基類相應的拷貝構造函數傳遞參數。例如:C:C(C &c1): B(c1) 派生類的構造、析構函數2021/7/2641例7-4 派生類構造函數舉例#include using namespace std;class Base1 /基類Base1,構造函數有參數public:Base1(int i) cout Constructing Base1 i endl; ;class Base2 /基類Base2,構造函數有參數public:Base2(int j) cout Constructing Base2 j endl; ;class Base3 /基類Ba

20、se3,構造函數無參數public:Base3() cout Constructing Base3 * endl; ;派生類的構造、析構函數2021/7/2642class Derived: public Base2, public Base1, public Base3 /派生新類Derived,注意基類名的順序public:/派生類的公有成員Derived(int a, int b, int c, int d): Base1(a), member2(d), member1(c), Base2(b) /注意基類名的個數與順序,/注意成員對象名的個數與順序private:/派生類的私有成員對象

21、Base1 member1;Base2 member2;Base3 member3;int main() Derived obj(1, 2, 3, 4);return 0;運行結果:constructing Base2 2constructing Base1 1constructing Base3 *constructing Base1 3constructing Base2 4constructing Base3 *432021/7/2643繼承時的析構函數析構函數也不被繼承,派生類自行聲明聲明方法與一般(無繼承關系時)類的析構函數相同。不需要顯式地調用基類的析構函數,系統會自動隱式調用。析

22、構函數的調用次序與構造函數相反。派生類的構造、析構函數2021/7/2644例7-5 派生類析構函數舉例派生類的構造、析構函數#include using namespace std;class Base1 /基類Base1,構造函數有參數public:Base1(int i) cout Constructing Base1 i endl; Base1() cout Destructing Base1 endl; ;class Base2 /基類Base2,構造函數有參數public:Base2(int j) cout Constructing Base2 j endl; Base2() co

23、ut Destructing Base2 endl; ;class Base3 /基類Base3,構造函數無參數public:Base3() cout Constructing Base3 * endl; Base3() cout Destructing Base3 endl; ;2021/7/2645class Derived: public Base2, public Base1, public Base3 /派生新類Derived,注意基類名的順序public:/派生類的公有成員Derived(int a, int b, int c, int d): Base1(a), member2(

24、d), member1(c), Base2(b) /注意基類名的個數與順序,注意成員對象名的個數與順序private:/派生類的私有成員對象Base1 member1;Base2 member2;Base3 member3;int main() Derived obj(1, 2, 3, 4);return 0;462021/7/2646例7-5 運行結果Constructing Base2 2Constructing Base1 1Constructing Base3 *Constructing Base1 3Constructing Base2 4Constructing Base3 *De

25、structing Base3Destructing Base2Destructing Base1Destructing Base3Destructing Base1Destructing Base22021/7/2647同名隱藏規則當派生類與基類中有相同成員時:若未強行指名,則通過派生類對象使用的是派生類中的同名成員。如要通過派生類對象訪問基類中被隱藏的同名成員,應使用基類名限定。派生類成員的標識與訪問2021/7/2648例7-6 多繼承同名隱藏舉例(1)派生類成員的標識與訪問#include using namespace std;class Base1 /定義基類Base1public

26、:int var;void fun() cout Member of Base1 endl; ;class Base2 /定義基類Base2public:int var;void fun() cout Member of Base2 endl; ;class Derived: public Base1, public Base2 /定義派生類Derivedpublic:int var;/同名數據成員void fun() cout Member of Derived Base2:var = 3;/作用域分辨符標識p-Base2:fun();/訪問Base2基類成員return 0;502021/

27、7/2650二義性問題在多繼承時,基類與派生類之間,或基類之間出現同名成員時,將出現訪問時的二義性(不確定性)采用虛函數(參見第8章)或同名隱藏規則來解決。當派生類從多個基類派生,而這些基類又從同一個基類派生,則在訪問此共同基類中的成員時,將產生二義性采用虛基類來解決。派生類成員的標識與訪問2021/7/2651二義性問題舉例(一)class A public:void f();class B public:void f();void g();class C: public A, piblic B public:void g();void h();如果定義:C c1;則 c1.f() 具有二義

28、性而 c1.g() 無二義性(同名隱藏)派生類成員的標識與訪問2021/7/2652二義性的解決方法解決方法一:用類名來限定c1.A:f() 或 c1.B:f()解決方法二:同名隱藏在C 中聲明一個同名成員函數f(),f()再根據需要調用 A:f() 或 B:f()派生類成員的標識與訪問2021/7/2653二義性問題舉例(二)class B public:int b;class B1: public B private:int b1;class B2: public B private:int b2;class C : public B1,public B2 public:int f();p

29、rivate:int d;派生類成員的標識與訪問有二義性:C c;c.bc.B:b無二義性:c.B1:bc.B2:b2021/7/2654虛基類虛基類的引入用于有共同基類的場合聲明以virtual修飾說明基類例:class B1:virtual public B作用主要用來解決多繼承時可能發生的對同一基類繼承多次而產生的二義性問題.為最遠的派生類提供唯一的基類成員,而不重復產生多次拷貝注意:在第一級繼承時就要將共同基類設計為虛基類。2021/7/2655虛基類舉例class B public: int b; ;class B1: virtual public B public: int b1;

30、 ;class B2: virtual public B public: int b2; ;class C: public B1, public B2 public: float d; ;下面的訪問是正確的:C cobj;cobj.b; 虛 基 類2021/7/2656例7-8虛基類舉例 虛 基 類DerivedBase0:var0:intBase1:var1:intBase2:var2:intvar :intBase0:fun0():voidfun():voidBase1+ var1 : intBase2+ var2 : intDerived+ var : int+ fun() : void

31、 Base0+ var0 : int+ fun0() : void2021/7/2657#include using namespace std;class Base0 /定義基類Base0public:int var0;void fun0() cout Member of Base0 endl; ;class Base1: virtual public Base0 /定義派生類Base1public:/新增外部接口int var1;class Base2: virtual public Base0 /定義派生類Base2public:/新增外部接口int var2;582021/7/2658

32、class Derived: public Base1, public Base2 /定義派生類Derived public:/新增外部接口int var;void fun() cout Member of Derived endl;int main() /程序主函數Derived d;/定義Derived類對象dd.var0 = 2;/直接訪問虛基類的數據成員d.fun0();/直接訪問虛基類的函數成員return 0;592021/7/2659虛基類及其派生類構造函數建立對象時所指定的類稱為最(遠)派生類。虛基類的成員是由最派生類的構造函數通過調用虛基類的構造函數進行初始化的。在整個繼承結

33、構中,直接或間接繼承虛基類的所有派生類,都必須在構造函數的成員初始化表中給出對虛基類的構造函數的調用。如果未列出,則表示調用該虛基類的默認構造函數。在建立對象時,只有最派生類的構造函數調用虛基類的構造函數,該派生類的其他基類對虛基類構造函數的調用被忽略。 虛 基 類2021/7/2660有虛基類時的構造函數舉例 虛 基 類#include using namespace std;class Base0 /定義基類Base0public:Base0(int var) : var0(var) int var0;void fun0() cout Member of Base0 endl; ;clas

34、s Base1: virtual public Base0 /定義派生類Base1public:/新增外部接口Base1(int var) : Base0(var) int var1;class Base2: virtual public Base0 /定義派生類Base2public:/新增外部接口Base2(int var) : Base0(var) int var2;2021/7/2661class Derived: public Base1, public Base2 /定義派生類Derivedpublic:/新增外部接口Derived(int var) : Base0(var), B

35、ase1(var), Base2(var) int var;void fun() cout Member of Derived endl; ;int main() /程序主函數Derived d(1);/定義Derived類對象dd.var0 = 2;/直接訪問虛基類的數據成員d.fun0();/直接訪問虛基類的函數成員return 0;622021/7/2662組合與繼承組合與繼承:通過已有類來構造新類的兩種基本方式組合:B類中存在一個A類型的內嵌對象有一個(has-a)關系:表明每個B類型對象“有一個” A類型對象A類型對象與B類型對象是部分與整體關系B類型的接口不會直接作為A類型的接口深

36、 度 探 索2021/7/2663“has-a”舉例class Engine /發動機類public:void work();/發動機運轉;class Wheel /輪子類public:void roll();/輪子轉動;class Automobile /汽車類public:void move();/汽車移動private:Engine engine;/汽車引擎Wheel wheels4;/4個車輪;意義一輛汽車有一個發動機一輛汽車有四個輪子接口作為整體的汽車不再具備發動機的運轉功能,和輪子的轉動功能,但通過將這些功能的整合,具有了自己的功能移動深 度 探 索2021/7/2664公有繼承的

37、意義公有繼承:A類是B類的公有基類是一個(is-a)關系:表明每個B類型對象“是一個” A類型對象A類型對象與B類型對象是一般與特殊關系回顧類的兼容性原則:在需要基類對象的任何地方,都可以使用公有派生類的對象來替代B類型對象包括A類型的全部接口深 度 探 索2021/7/2665“is-a”舉例class Truck: public Automobile/卡車public:void load();/裝貨void dump();/卸貨private:;class Pumper: public Automobile /消防車public:void water();/噴水private:;意義卡車是

38、汽車消防車是汽車接口卡車和消防車具有汽車的通用功能(移動)它們還各自具有自己的功能(卡車:裝貨、卸貨;消防車:噴水)深 度 探 索2021/7/2666派生類對象的內存布局派生類對象的內存布局因編譯器而異內存布局應使類型兼容規則便于實現一個基類指針,無論其指向基類對象,還是派生類對象,通過它來訪問一個基類中定義的數據成員,都可以用相同的步驟不同情況下的內存布局單繼承:基類數據在前,派生類新增數據在后多繼承:各基類數據按順序在前,派生類新增數據在后虛繼承:需要增加指針,間接訪虛基類數據深 度 探 索2021/7/2667單繼承情形class Base ;class Derived: public Base ;Derived *pd = new Derived();Base *pb = pd;Derived類型指針pd轉換為Base類型指針時,地址不需要改變Base類數據成員Derived類新增數據成員pb, pdDerived對象深 度 探 索2021/7/2668多繼承情形class Base1 ;class Base2 ;class Derived: public Base1, public Base2 ;Derived *pd = new Derived();Base1 *pb1 = pd

溫馨提示

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

評論

0/150

提交評論