物件导向程式语言讲义课件_第1页
物件导向程式语言讲义课件_第2页
物件导向程式语言讲义课件_第3页
物件导向程式语言讲义课件_第4页
物件导向程式语言讲义课件_第5页
已阅读5页,还剩44页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Derived Class前言衍生類別的定義單一繼承public, protected, 和 privated 基底類別virtual 成員函式RTTI (Run-time Type Information)Derived Class前言前言C+ 提供類別繼承的機制來擴充或更改現有類別的功能。我們可以利用此機制來達到以下兩個目的:程式碼的再利用(code reuse)物件導向的設計(object-oriented design)前言C+ 提供類別繼承的機制來擴充或更改現有類別的功能。我假定 B 是一個類別。我們可以用底下的格式來定義一個 B 的衍生類別 D:class D : public B

2、/ members of D;我們稱:D 繼承 B、B 是 D 的 base class(基底類別)或 superclass(父類別)、以及 D 是 B 的 derived class(衍生類別)或 subclass(子類別)。衍生類別的定義class Bclass D類別繼承的圖示假定 B 是一個類別。我們可以用底下的格式來定義一個 B 的類別繼承通常是用來表達 kind-of 關係(或稱 is-a 關係):衍生類別是基底類別的一種。譬如: Manager(經理)也是 Employee(員工),所以我們先規劃好 Employee 類別,然後把 Manager 定義成 Employee 的一個

3、衍生類別:class Employee / other membersprivate:string first_name, family_name;char middle_initial;Date hiring_date;short department;class Manager : public Employee / other membersprivate:set group;short level;類別繼承通常是用來表達 kind-of 關係(或稱 is-aManager 的資料成員:string first_name, family_name;char middle_initial;

4、Date hiring_date;short department;set group;short level;衍生類別除了本身的資料成員以外,也具有基底類別的資料成員。譬如:Employee 的資料成員:string first_name, family_name;char middle_initial;Date hiring_date;short department;Manager 的資料成員:衍生類別除了本身的資料成員以外,由於衍生類別是基底類別的子類別,因此衍生類別的物件可視為基底類別的物件;基底類別型態的指標也可用於衍生類別的物件,而不須要經過型態轉換。但反過來就不成立了。譬如:v

5、oid foo (Manager mm, Employee ee)Employee *pe = &mm;/ okManager *pm = ⅇ/ errorpe-level = 2;/ error: Employee doesnt have/ data member:levelpm = static_cast (pe);/ ok: explicit type castingpm-level = 2;/ ok: since pe points to a/ Manager object由於衍生類別是基底類別的子類別,因此衍生類別的物件可視為基底衍生類別及其朋友(friends)可以直接使用基

6、底類別的 public 或 protected 成員,但是不能使用基底類別的 private 成員。譬如:class B public:void f(); protected:void g();int _x;private:void h();int _y;class D : public B friend void k(D);void a() f(); / okvoid b() g(); / okvoid c() _x = 0; / ok void d() h(); / errorvoid e() _y = 0; / error ;void k (D obj)obj.g();/ okobj.f

7、();/ okobj._x = 0;/ okobj._y = 0;/ error衍生類別及其朋友(friends)可以直接使用基底類別的 p存取控制 本身與其朋友 衍生類別與其朋友外界publicprotectedprivatexxx我們用下表來總結類別成員的存取控制:存取控制 本身與其朋友 衍生類別與其朋友如果基底類別的成員函式不符合所需的話,我們可以在衍生類別中重新改寫(override)。譬如:class Employee public:void print();/ other members;void Employee:print()cout first_name middle_ini

8、tial family_name;class Manager : public Employee public:void print();/ other members;void Manager :print()Employee:print();cout level;這項改寫的機制讓 code reuse 可以很容易地達成。如果基底類別的成員函式不符合所需的話,我們可以在衍生類別中重衍生類別的建構函式必須呼叫基底類別的建構函式(如果它存在的話),而且前者的參數必須包含後者的參數。譬如:class Employee public:Employee (const string & n, int d

9、): family_name(n), department(d) / other members;class Manager : public Employee public:Manager (const string & n, int d, int lvl): Employee(n, d), level(lvl) / other members;衍生類別的建構函式必須呼叫基底類別的建構函式(如果它存在的話衍生類別的物件建構順序如下:1. 執行基底類別的建構函式。2. 執行衍生類別資料成員的建構函式。3. 執行衍生類別的建構函式。衍生類別的物件解構順序則恰恰相反,即:1. 執行衍生類別的解構函

10、式。2. 執行衍生類別資料成員的解構函式。3. 執行基底類別的解構函式。資料成員和基底類別是按照宣告的順序來建構,解構則按照相反的順序。衍生類別的物件建構順序如下:衍生類別的物件解構順序則恰恰相反拷貝衍生類別物件至基底類別物件時,只拷貝基底類別的資料成員。class Employee Employee (const Employee &);Employee& operator=(const Employee &);/ ;void f (const Manager &m)Employee e = m; / construct e from Employee part of me = m; / a

11、ssign Employee part of m to e拷貝衍生類別物件至基底類別物件時,只拷貝基底類別的資料成員。範例class CPoint2D public:CPoint2D (int x = 0, int y = 0) : _x(x), _y(y) int x () return _x; int y () return _y; void setX (int x) _x = x; void setY (int y) _y = y; void set (int x, int y) _x = x; _y = y; bool isZero () return _x = 0 & _y = 0;

12、 double distance () return sqrt(_x * _x + _y * _y); protected:int _x, _y;我們利用 CPoint2D 類別來定義 CPoint3D 類別。首先我們把 CPoint2D 的 private 成員改成 protected 成員,讓 CPoint3D 可以使用它們。範class CPoint2D 我們利用 CPoint2Dclass CPoint3D : public CPoint2D public:CPoint3D (int x = 0, int y = 0, int _z = 0): CPoint2D(x, y), _z(z

13、) int z() return _z; void setZ (int z) _z = x; void set (int x, int y, int z) set(x, y); setZ(z); bool isZero () return _x = 0 & _y = 0 & _z = 0; double distance () return sqrt(_x * _x + _y * _y + _z * _z); private:int _z;CPoint3D 改寫(override) CPoint2D 的 isZero() 和 distance() 函式。class CPoint3D : pub

14、lic CPoint#include using namespace std;#include “CPoint3D.h”ostream& operator (ostream &os, CPoint3D p)os ( p.x() “, “ p.y() “, “ p.z() );return os;我們可以為 CPoint3D 類別定義輸出運算子 如下:#include 我們可以為 CPoin測試程式#include “CPoint3D.h”int main ()CPoint3D p(1, 2, 3);cout p endl;CPoint2D q(5, 6);q = p;cout q endl;p

15、 = q;/ error輸出結果(1, 2, 3)(1, 2)測試程式#include “CPoint3D.h”輸出結果範例typedef unsigned short big5char;class big5string : public string public:bool is_big5char (int idx);big5char next_char(int idx);int big5_length();private:bool in (char c, int min, int max) return min = c & c = 0 & idx length() );char *cp =

16、 c_str();if (cpidx 128 | idx = length() -1)return false;if (is_big5hiByte(cpidx) & is_big5loByte(cpidx+1)return true;elsereturn false;bool big5string:is_big5char (big5char big5string:next_char (int idx)char *cp = c_str();return is_big5char(idx)? cpidx*256+cpidx : cpidx;int big5string:big5_length ()i

17、nt len = 0, k = 0;while (k length() k = is_big5char(k) ? k+2 : k+1;len+;return len;big5char big5string:next_char測試程式#include #include #include “big5string.h”int main ()string s1(“Hi, “);big5string s2;s2 = s1 + “好久不見”;cout s2 endl;cout “# of characters is: “ s2.big5_length() endl;return 0;輸出結果Hi, 好久不

18、見# of characters is: 8big5string 物件除了可使用 big5string 的功能以外,也可使用 string 所提供的各項功能。測試程式#include 輸出結果big單一繼承類別可隨需要而建立層層的繼承關係。譬如:class Employee ;class Manager : public Employee ;class Director : public Manager ;Director 是 Manager 的衍生類別、 Manager又是 Employee 的衍生類別。因此 Director 是 Manager 的子類別,也是 Employee 的子類別。

19、之前所說 Manager 和Employee 間的關係同樣適用於 Director 和 Employee 之間。譬如: Director 包含 Employee 所有的資料成員、也可以直接使用 Employee 的 public 和 protected 成員、等等。EmployeeManagerDirector單一繼承類別可隨需要而建立層層的繼承關係。譬如:EmployBD1D2Dn衍生類別也可以作為其他類別的基底類別。如此一來,類別的繼承就形成如右圖所示的線性結構。D1, D2, , Dn 都是基底類別 B 的衍生類別(或子類別),其中 D1 稱為 B 的直接衍生類別、 D2, , Dn 稱

20、為 B 的間接衍生類別。此外,D1, D2, , Dn 型態的指標都可以轉換成 B 型態的指標。BD1D2Dn衍生類別也可以作為其他類別的基底類別。如此一來root class有些時候,單一繼承的類別會形成如圖所示的階層狀(樹狀)的結構。其中最上層的類別稱為根類別(root class)。其他的類別都是根類別的子類別。root class有些時候,單一繼承的類別會形成如圖所示的public, protected, 和 privated 基底類別基底類別可以指定成 public、protected、或 private。譬如:class X : public B ;class Y : protec

21、ted B ;class Z : privated B ;如果省略這些指定,則 class 的基底類別預設為 private、而 struct 的基底類別預設為 public 。譬如:class X : B ;/ B is a private basestruct X : B ; / B is a public basepublic, protected, 和 privated 這三種的差別在於以下的限制對間接衍生類別有所不同: 基底類別 public 和 protected 成員的存取; 把指標和參照從衍生類別的型態轉換成基底類別的型態。假定 D 是 B 的一個直接衍生類別。B 是一個 pr

22、ivate 基底類別B 的 protected 和 public 成員在 D 中變成 private 成員。這使得只有 D 的成員和朋友可以使用它們,而其他函式(包含 D 的衍生類別之成員和朋友)則不能使用它們。此外,只有 D 的成員和朋友可以把 D* 轉換成 B*。這三種的差別在於以下的限制對間接衍生類別有所不同:class B public:void foo ();protected:void bar ();class D1 : private B void f () bar(); / ok;class D2 : public D1 void g () bar(); / errorvoid

23、 h () foo(); / error;void func ()B b;D1 d1;D2 d2;b.foo(); / okb.bar(); / error d1.foo(); / errord1.bar(); / errord2.foo(); / errord2.bar(); / errorB *bp = &D1; / errorclass B void func ()B 是一個 protected 基底類別B 的 protected 和 public 成員在 D 中變成 protected 成員,使得只有 D 及其衍生類別的成員和朋友可以使用它們,而其他函式則不能使用它們。此外,只有 D

24、及其衍生類別的成員和朋友可以把 D* 轉換成 B*。B 是一個 public 基底類別B 的 protected 和 public 成員在 D 中仍維持相同的存取模式。此外,任何函式都可以把 D* 轉換成 B*。B 是一個 protected 基底類別B 是一個 publclass B public:void foo ();protected:void bar ();class D1 : protected B void f () bar(); / ok;class D2 : public B void g () bar(); / okvoid h () foo(); / ok;void fu

25、nc ()B b;D1 d1;D2 d2;b.foo(); / okb.bar(); / error d1.foo(); / errord1.bar(); / errord2.foo(); / errord2.bar(); / errorB *bp = &D1; / errorclass B void func ()class B public:void foo ();protected:void bar ();class D1 : public B void f () bar(); / ok;class D2 : public B void g () bar(); / okvoid h ()

26、 foo(); / ok;void func ()B b;D1 d1;D2 d2;b.foo(); / okb.bar(); / error d1.foo(); / okd1.bar(); / errord2.foo(); / okd2.bar(); / errorB *bp = &D1; / okclass B void func () 使用 B 的 public 和 protected 成員D 的成員D 及其衍生類別其他函式與朋友的成員與朋友private Bprotected Bpublic Bxpublic butnot protectedpublic butnot protected

27、public butnot protected 使用 B 的 把 D* 轉換成 B*D 的成員D 及其衍生類別其他函式與朋友的成員與朋友private Bprotected Bpublic Bxxx 從以上的比較我們得知:對基底類別成員的存取,以 public 的限制最少、protected 次之、而 private 最多。此外,只有 public 的繼承方式允許在非成員的函式中,把衍生類別型態的指標轉換成基底類別型態的指標。由於這些原因,public 的繼承方式是最常用來定義基底類別的子類型(subtype)。如果我們想把基底類別當成 implementation 內部的一個類別,不希望外界

28、直接地使用它,最好使用 protected 和 private 的繼承方式,其中又以 private 的隔離效果比 protected 來得大。從以上的比較我們得知:對基底類別成員的存取,以 publicVirtual Functions假定我們有如右邊所示的類別繼承關係。由於資料成員多寡不一,每個類別因此各自定義了一個 print() 成員函式,用來列印相關的員工資料。class Employee public:void print ();/ ;class Manager : public Employee public:void print ();/ ;class Director : M

29、anager public:void print ();/ ;Virtual Functions假定我們有如右邊所示的類別假定你想寫一個函式能夠列印任何一類員工的資料。由於 Manager 和 Director 都屬於 Employee 類別,因此你可能認為以下的函式就可以達到這個目的:void print_emp (Employee *e)e-print();其實不然。原因是:e 是 Employee 型態的指標,所以不論傳進來的引數型態是 Employee、 Manager、或 Director,e-print() 永遠是呼叫 Employee 的成員函式 print()。假定你想寫一個函

30、式能夠列印任何一類員工的資料。由於 Mana你可以在 Employee 類別中,加入一個儲存員工類型的資料成員來解決前述的問題。譬如:class Employee public:enum emp_type EMPLOYEE, MANAGER, DIRECTOR;void print ();emp_type type() return _type; / private:emp_type _type;並在三個類別的建構函式中,加入資料成員 type 的設定。經過這些加工之後,你就可以寫出下一頁的列印函式。你可以在 Employee 類別中,加入一個儲存員工類型的資void print_emp (E

31、mployee *e)switch (e-type() case Employee:EMPLOYEE:e-print();break;case Employee:MANAGER:static_cast(e)-print(); / Managers print()break;case Employee:DIRECTOR:static_cast(e)-print(); / Directors print()break;void print_emp (Employee *e)上述的解決方案有下面兩個缺點:就如同 print_emp() 函式所示,程式設計師必須判斷物件的類別,然後採取適當的型態轉換。

32、這種作法不僅增加程式撰寫的負擔,也容易造成錯誤。print_emp() 函式只能列印 Employee、Manager、和 Director 三種類別的物件。如果其他人用繼承的方式定義另一種員工的類別,如 Secretary,則已經寫死的 print_emp() 函式將無法用來列印這個新類別的物件。class Secretary : public Employee public:void print ();/ ;上述的解決方案有下面兩個缺點:print_emp() 函式只為了解決上述的問題,C+ 提供一種稱為 virtual 函式的特別成員函式。你只要在成員函式的宣告之前加上關鍵字 virtu

33、al,就可以把它變成 virtual 函式,即virtual return_type func_name (parameter list)當類別含有 virtual 成員函式時,C+ 編譯器會為它產生一個 virtual function table,其中包含此類別所有 virtual 成員函式的位址。此外,屬於此類別的物件,除了儲存資料成員外,會另外儲存一個指向此 virtual function table 的指標。宣告 virtual 函式為了解決上述的問題,C+ 提供一種稱為 virtual 函舉例來說,假定類別 X 的宣告如下:class X public:virtual void

34、vf1 ();virtual void vf2 ();void f ();private:int _x, _y;X a, b;則 X 類別的物件結構將如右圖所示。_x_y_ _vptr_ _XX:vf1()X:vf1()virtual tablefor class X_x_y_ _vptr_ _Xab舉例來說,假定類別 X 的宣告如下:_x_y_ _vptr_衍生類別的 virtual 函式會覆蓋(override)基底類別的同名同參數列的 virtual 函式。class Base virtual void foo (int);/ other members;class Derived :

35、public Basevirtual void foo (int);/ other members;override衍生類別的 virtual 函式會覆蓋(override)基若我們把前述 Employee 類別和它衍生類別中的 print() 成員函式都改成 virtual(如左圖所示),則函式 print_emp() 就變得簡單多了,而且也克服前述的一些缺點。class Employee public:virtual void print ();/ ;class Manager : public Employee public:virtual void print ();/ ;class

36、Director : Manager public:virtual void print ();/ ;若我們把前述 Employee 類別和它衍生類別中的 pr利用 virtual 函式的 print_emp() 定義如下:void print_emp (Employee *e)e-print();則 e-print() 會呼叫傳進來物件所定義的 print() 成員函式。譬如:若傳進來 Emploee 型態的物件時,e-print() 等同於e-Emploee:print();若是 Manager 型態的物件時,e-print() 等同於 e- Manager :print()。利用 vir

37、tual 函式的 print_emp() 定義如型態相同的物件(都是 Employee)卻具有不同的行為(不同的 print() 功能),稱之為 polymorphism(多型)。具有 virtual 成員函式的類別稱為多型型態(polymorphic type)。在 C+ 中,若要使用多型,你必須: 把一些成員函式定義成 virtual。 透過物件指標或參照來呼叫這些 virtual 成員函式。若透過物件直接呼叫 virtual 成員函式,因為編譯時會固定呼叫的對像,所以會達不到多型的效果。譬如:Employee e;e.print();/ 一定呼叫 Employee:print()型態相同

38、的物件(都是 Employee)卻具有不同的行為(不Pure Virtual Functions如果基底類別的 virtual 成員函式只是用來規定衍生類別應該具備的使用介面(interface),而且基底類別也不用來定義物件的話,我們可以用以下的格式把 virtual 成員函式設定無定義的函式:virtual return_type fucn_name (parameter_list) = 0這樣的函式稱為 pure virtual function。Pure Virtual Functions如果基底類別的 Abstract Classes本身擁有或繼承但不改變 pure virtual functions 的類別稱為抽象類別(abstract class)。由於 pure virtual functions 是沒有定義的函式,因此抽象類別不可用來定義物件。譬如底下的 Abstract_Base 和Abstract_Derived 是抽象類別,而 Concrete_Derived 就不再是了:class Abst

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论