程序設計首都師范大學chapter_第1頁
程序設計首都師范大學chapter_第2頁
程序設計首都師范大學chapter_第3頁
程序設計首都師范大學chapter_第4頁
程序設計首都師范大學chapter_第5頁
已閱讀5頁,還剩115頁未讀, 繼續免費閱讀

下載本文檔

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

文檔簡介

1、第四章編譯時多態性封裝性是面向對象程序設計的基礎,可以說沒有封裝性就沒有面向對象的程序設計。封裝性從根本上解 決了數據的安全性,為實現不同數據的操作同一性奠 定了基礎,這種操作的同一性反映了客觀世界中規范不同對象行為的需要,它了面向對象程序設計多態性的一部分。不過這種多態性必須預先確定操作所施加的數據類型,因此不是完備意義上的多態性,而 只是基于對象的多態性。這種不同數據的操作同一性 必須在程序編譯時靜態確定,故稱為編譯時多態性, 或稱為靜態多態性。實現這種靜態多態性的途徑是: 函數重載和運算符重載。4.1 函數重載函數重載為實現不同數據的操作同一性提供了根本 編程機制。函數重載的基本規則:

2、函數名必須相同:體現了操作同一性。 函數的參數必須不同:體現了同一操作的實現差異和所施加的數據差異。在面向對象的程序中函數重載表現在兩個方面: 類外全局函數重載 類成員函數重載4.1.1 類外全局函數重載在面向對象程序設計中,全局函數常常用來作為類 的友元函數,因此全局函數重載可以用來實現不同類對象的同一類外操作。關于全局函數重載的方法已經在第二章中講述,本章不再贅述。為了進一步理解函數重載是如何在編譯時確定同一操作施加于不同類型的數據,我們模擬分析 C+ 編譯器如何采用“名子壓延”的方法實現重載函數的調用。所謂“名子壓延”是指編譯器編譯重載函數調用時,先將原重載函數名和參數類型結合起來,以創

3、建新重 載函數名。然后用新重載函數名替代原重載函數名。例如,一個程序中有兩個重載函數,原型為:int myAns(float x, int j); int myAns(int I, char c);編譯時,編譯器首先壓延修改這兩個重載函數名。修改后的新函數名或許會變成如下形式:int myAnsFLTINT(float x, int j); int myAnsINTCHAR(int i, char c);這樣,就把原來無法區來的相同的函數名變成可以區分的不同的函數名。例如:調用 myAns(float x, int j) 時,則調用 myAnsFLTINT(float x, int j); 而

4、調用 myAns(int I, char c) 時,則調用 myAnsINTCHAR(int i, char c)。假如調用這兩個函數的語句為:exam1 = myAns(15.3, 15); exam2 = myAns(45, 'a');用新函數名替代原函數名后,使得調用這兩個函數的語句會發生如下相應的改變:exam1 = myAnsFLTINT(15.3, 15);exam2 = myAnsINTCHAR(45, 'a');4.1.2 類成員函數重載類成員函數重載分為兩類:1 構造函數重載為類對象的創建和類對象的數據成員賦初值提供不 同的方法。重載構造函數的

5、參數必須不同。2 公有成員函數重載提供響應相同消息的不同接口方法。這類重載有兩 種情況: 同一類中的重載成員函數的參數必須有差別。 派生類中分屬于基類和派生類的重載成員函數的參數可以相同,但基類的重載成員函數被覆蓋。 例如:class point int x, y;public:point(int a, int b) x = a; y = b; float area() return 0.0; ;class circle:public point int radius;public:circle(int x, int y, int rad):point(x, y) radius = rad;

6、float area()/ 重載成員函數return float(3.1416)*float(radius)*float(radius); ;main()point p(20, 20);circle c(8, 8, 30);cout << p.area() << endl; cout << c.area() << endl;cout << c.point:area() << endl;return 0;其中 area 是參數相同的重載成員函數。編譯過程中區別這兩個不同版本的 area 的情況有兩種: 使用對象名只能分別調用

7、基類和派生類的參數相同的重載成員函數。例如:p.area() 將調用 point:area() 方法;c.area() 將調用 circle:area 方法。在對象名后添加 “基類名:”可以使用派生類對象名可以調用基類的參數相同的重載成員函數。例如:c.point:area() 將調用 point:area() 方法。4.2 運算符重載運算符可以視為是一種特殊的函數,它們可以用一 種簡潔的,接近自然語言的表達式方式被調用。C+ 擁有一系列的預定義運算符,這些運算符能對所有的預定義類型的數據進行操作。也就是說,可以 使用預定義類型的數據作參數調用這些運算符函數。 例如,加運算符 "+&

8、quot;:int x, y; float e, f; y = x + y;f = e + f;這種對于不同類型的數據使用相同的表達式調用相同的運算符正是通過重載機制實現的。但這些運算符卻 不能自動施加在自定義類型的對象。例如:自定義的 復數類 complex 的定義如下:class complexdouble real, imag; public:complex(double r = 0, double i = 0) real = r; imag = i; ;main()complexcom1(1.1, 2.2), com2(3.3, 4.4), total;total = com1 + c

9、om2; return 0;/ “+”運算符無法對復數進行操作很顯然,因為運算符 “+”產生上述錯誤的操的作數據類型中不包括 complex 類型,所以編譯器就不知解決的辦法就是擴展 “+” 運算符操作的數據類“+”型,即為復數類complex 定義的運算符操作。C+ 的運算符重載方法為設計不同類對象的同一行為提供了非常有效和方便的。運算符重載是通過對運算符函數的重載實現的,運算符函數重載的原型和定義的一般形式如下:函數類型 operator (參數表列);/ 是運算符名的通配符號函數類型 operator (參數表列) 重載操作代碼 例如,對復數類 complex 的 “+” 運算符重載:c

10、lass complex double real, imag;public:complex(double r = 0, double i = 0) real = r; imag = i; complex operator + (complex);complex complex:operator +(complex &c)complex sum;sum.real = real + c.real; sum.imag = imag + c.imag; return sum;4.2.1 重載運算符的規則 C+ 不定義新的運算符,只能對系統預定義運算符進行重載。 C+ 的預定義運算符中重載的包括

11、:算術運算符+、-、*、/、%關系運算符=、!=、<、>、<=、>=邏輯運算符|、&&、!單目運算符+、-、*、&自增自減運算符+、-位操作運算符|、&、<<、>>賦值運算符=、+=、-=、*=、/=、%=、&=、|=、=、<<=、>>=內存管理運算符new、delete、new、delete其他運算符()、->、 ->*、,、不重載預定義運算符的包括: 重載不能改變預定義運算符函數的參數(操作數)個數。雙目運算符重載后仍為雙目運算符,單目運 算符重載后仍為單目運算符。

12、重載不能改變預定義運算符的原有優先級。 重載不能改變預定義運算符的原有結合律。 重載運算符函數的參數不有缺省值。否則編譯器會認為是改變了運算符函數的參數個數。成員運算符.成員指針運算符.*名域運算符:內存長度運算符sizeof條件運算符?: 重載的運算符必須與用戶自定義類型的對象一起使用,因此重載運算符函數的參數中至少應該有一個是自定義類對象或類對象的。換句話說,重載運算符函數的參數不能全部是預定義類型對象,防止用戶修改預定義運算符的性質。例如:int operator +(int a, int b) return a b; 顯然,這是絕對不對于雙目運算符,的。兩個參數都是自定義類對象,例如兩

13、個復數的加運算;也一個參數是自定義類對象,另一個參數是預定義類型對象,例如一個復數與一個實數的加運算。complex operator +(double d, complex& c) return complex(d + c.real, c.imag); 系統會為每個自定義類缺省重載了賦值運算符“=” 和取地址運算符“&”,其他運算符都需要根據需要進 行重載定義。其中: 賦值運算符“=”用于同類對象之間的數據成員賦值操作。一般情況下,缺省重載的賦值運算符“=”可以滿足要求,但遇到類的數據成員中包含了動態 數據指針,則使用缺省重載的賦值運算符就可能 雖然可以任意定義重載運算符的操

14、作功能,但應該使重載運算符的功能類似于被重載運算符作用于預定義類型數據時的操作功能。 運算符重載函數可以是運算所施加類對象的成員函數,也可以是該類的友元函數,如果不需要類的私有數據成員,還可以是既非類成員函數也非友元函數的普通函數。在 Java 中雖然也不乏運算符重載的例子,例如:String str = "hello" + "there"該表達式相當于在 C+ 中的表達式:string str = "hello" + "there"上述表達式都是因為對 Java 的 String 類型和 C+ 的string 類

15、型的進行了“+” 運算符重載的結果。但要注意的是在 Java 中不用戶進行運算符重載。4.3 使用成員函數重載運算符1 使用成員函數重載運算符的語法形式 在類定義體中要重載的運算符成員函數 type 運算符函數類型; operator 運算符函數名關鍵字; 要重載的運算符名; 參數表 被重載運算符所需要的操作數。參數個數 = 運算符所需操作數個數 - 1,缺省的左操作數必須是運算符所屬類對象。例如:class complex double real, imag; public:complex operator + (complex&); 定義運算符成員函數例如:complex comp

16、lex:operator + (complex& com) return complex(real + com.real, imag + com.imag); 重載運算符的使用運算符的左操作數必須是該運算符成員函數所屬 類的對象。 雙目運算符例如:complex com1, com2, com3; com3 = com1 + com2;或com3 = com1.operator +(com2); 單目運算符例如:complex com;+com; com+; 或com.operator +();com.operator +(0);2 用成員函數重載運算符的使用實例例4-1 定義了一個表

17、達三位置的簡單類 three_d,在此類中含有三位置的坐標。通過運算符重載來實現對此類對象的 +、 和 = 運算。注意:定義中 + 和 運算符函數的返回值不應是被加對象和被減對象,而 = 運算符函數的返回值必須是被賦值的對象。這都是運算符的操作含義所決定的。4.4 使1 使元函數重載運算符元函數重載運算符的語法形式 在類定義體中重載運算符友元函數 friend 友元函數關鍵字 type 運算符函數類型; operator 運算符函數名關鍵字; 要重載的運算符名; 參數表 被重載運算符所需要的操作數。參數個數 = 運算符所需操作數個數。 例如:class point int x, y; publ

18、ic:friend point operator +(point&, point&); 定義重載運算符友元函數例如:point operator +(point& p1, point& p2) return point(p1.x + p2.x, p1.y + p2.y); 重載運算符的使用 雙目運算符例如:point pt1, pt2, pt3;pt3 = pt1 + pt2;或pt3 = pt1.operator +(pt2); 單目運算符例如:point pt;+pt; pt+ 或operator +(pt);operator +(pt, 0);2 使元函數

19、重載運算符的應用實例例4-2 使元函數重載運算符的方法實現復數的四則運算,復數運算規則如下:(a + bi) + (c + di) = (a + c) + (b + d)i;(a + bi) - (c + di) = (a - c) + (b - d)i;(a + bi) * (c + di) = (ac - bd) + (bc + ad)i;(a + bi) / (c + di) = (ac + bd) + (bc - ad)i)/(c2+ d2);定義一個復數類 complex,并和定義相應的友元函數,用于重載運算符 +、-、*、/。例4-3 使元函數重載運算符的方法實現集合運算。集合可以

20、用數組或鏈表表示,在該數組或鏈表中不包含重復元素。定義整型數集合 set,元素個數用整型變量 card 表示。集合的操作包括向集合中追加元素、顯示集合的全部元素以及使元函數重載運算符的方法實現的集合的主要運算,這些運算包含: 判定某一個元素屬于集合的運算符 &; 判定兩個集合相等的運算符 =; 判定兩個集合的不等于運算符 !=; 兩個集合的交運算符 *; 兩個集合的并運算符 +; 判定某集合是另一集合的子集運算符 <=; 判定某集合是另一集合的純子集運算符 <。實例 4-3程序4.5 成員函數與友元函數重載運算符的比較1參數數目: 雙目運算符 重載運算符的成員函數只有一個參

21、數,用于表示表達式的右操作數。 重載運算符的友元函數有兩個參數,分別用于表達式的左、右操作數 單目運算符 重載運算符的成員函數無參數。 重載運算符的友元函數帶有一個參數,用于表示表達式的唯一操作數。2無論是使用成員函數還是友元函數重載的運算符,調用它們的表達式形式是一致的,而調用它們的函數形式是有差別的。表達式形式友元函數調用形式成員函數調用形式a boperator (a, b)a. operator (b) aoperator (a)a. operator ()a operator (a, 0)a. operator (0)3大部分運算符重載函數既可以是成員函數,又可以是友元函數。究竟選擇

22、哪一種好呢?要視實際情況 和程序員的習慣。但是應注意以下情況: 對于雙目運算符,如果使用成員函數重載,則在有些情況下會產生操作數類型錯誤。例如: class complex double real, imag;public:complex operator +(double x);complex complex:operator +(double x) return complex(real + x, imag); 對 complex 的對象 com 進行如下的 + 運算:com = com + 100.0;由于對象 com 是 + 運算符的左操作數,所以它調用了 complex 類重載的 +

23、 運算符,把實數 100.0 加到 com 的實部數據成員 real 上。如果按照 + 運算符操作數的交換律,將上面的表達式寫成:com = 100.0 + com;則會引起操作數類型錯誤,無法完成表達式所要 求的操作。這是因為 complex 類 + 運算符的左操作數是 complex 類對象,而表達式的左操作數是實數,無法調用 complex 類 + 運算符函數。如果使用兩個友元函數來替換上述 complex 類的 +運算符重載:friend operator +(complex com, double x); friend operator +(double x, complex com

24、);complex operator +(complex com, double x) return complex(com.real + x, imag); complex operator +(double x, complex com) return complex(x + com.real, imag); 則可避免上述問題,因為友元運算符函數的兩個 操作數都是顯式地傳遞給運算符函數的,能滿足 加運算操作數的交換律。所以一般建議使元函數重載雙目運算符,特別是期望雙目運算符的左操作數的類型能夠隱式轉換的情況,則重載雙目運算符必須使元函數,而不能使用成員函數。 若一個運算符需要修改其操作所施

25、加對象(特別是該對象為該運算符的第一操作數)的狀態,則選擇使用成員函數重載運算符不破壞類對象的封裝性,更符合面向對象程序設計的原則。4.6 自增 + 和自減 - 運算符的重載從運算操作的正確性考慮,使用成員函數還是使用友元函數重載自增運算符 + 和自減運算符 - 的效果是一致的。但從面向對象設計原則出發,類對象的自增和自減運算實際上是對被封裝的類數據成員依次進行的。因此,建議使用成員函數實現自增運算符 + 和自減運算符 - 的重載。使用 + 和 - 的表達式形式分為前綴和后綴兩種形式,這兩種形式對操作數的最終修改雖然相同,但運 算符函數的返回值不同,前綴形式返回修改后的操作數,而后綴形式返回修

26、改前的操作數。例如:int i = 0, j = 0;i+; +j;cout << i <<","<< j << endl;/ 顯示1,1cout << i+ <<","<< +j << endl;/ 顯示1,2在 C+ 2.1 及以后的版本中,編譯器可以通過判別在自增或自減運算符函數的參數列表中是否增加了一個附加的整型 int 形式參數來區分所重載的自增或自減運算符可以用于前綴表達式還是用于后綴表達式。1自增運算符 “+” 使用成員函數重載 原型前綴形式:類

27、名后綴形式:類名例如:class point int x, y; public:operator +();operator +(int);point operator +();point operator +(int); 定義前綴形式:類名后綴形式:類名例如:類名:operator +() 類名:operator +(int) point point:operatot +() +x,+y;return *this;point point:operatot +(int) point temp(x, y);+x,+y;return temp; 調用前綴表達式形式:+類對象名; 后綴表達式形式:類對

28、象名+; point pt;例如:+pt;pt+;/ 前綴表達式/ 后綴表達式前綴函數形式:對象名.operator +();后綴函數形式:對象名.operator +(0); point pt;例如:pt.operator +();pt.operator +(0);/ 前綴函數形式/ 后綴函數形式2自減運算符 “-” 使用成員函數重載 原型前綴形式:類名后綴形式:類名例如:class point int x, y; public:operator -();operator -(int);point operator -();point operator -(int); 定義前綴形式:類名后綴

29、形式:類名例如:類名:operator -() 類名:operator -(int) point point:operatot -() -x,-y;return *this;point point:operatot -(int) point temp(x, y);x-, y-;return temp; 調用前綴表達式形式:-類對象名; 后綴表達式形式:類對象名-; point pt;例如:-pt;pt-;/ 前綴表達式/ 后綴表達式前綴函數形式:對象名.operator -();后綴函數形式:對象名.operator -(0); point pt;例如:pt.operator -();/ 前綴

30、函數形式pt.operator -(0); / 后綴函數形式例4-4 在 point 類中對運算符 + 和 - 進行重載,其操作是對 point 類的數據成員 x 和 y 分別進行的。注意,如果在程序中沒有重載能用于后綴表達式形式的 + 和 -,則在后綴表達式形式調用 + 和- 時,會出現編譯警告,而運行時會以前綴表達式的結果替代后綴表達式的操作結果。4.7 調用運算符 () 和下標運算符 的重載1調用運算符“()”顧名思義,調用運算符可以用函數調用表達式的形 式使運算符操作所施加的對象完成任何需要的功能 操作,例如,為操作對象的各個數據成員賦值。調 用該運算符的表達式的一般形式為:類對象名(

31、調用參數列表);可以認為調用運算符是一個雙目運算符。左操作數: 必須是該運算符操作所施加的類對象。右操作數: 為操作類對象所需要傳遞的一組參數, 這組參數可以由任意個數、任何類型的對象組成,當然也可以無參數。顯然,應該使用成員函數重載調用運算符。 原型返回類型 operator ()(調用參數列表);其中返回類型可以是任何合法類型。例如:class point int x, y;public:void operator ()(int, int); 定義返回類型 類名:operator ()(調用參數列表)例如:void point:operator ()(int x, int y)this-&

32、gt;x = x; this->y = y; 調用類對象名(調用參數列表); point pt;pt(50, 80);例如:是一個使用成員函數重載調用運算符 “()”例4-5單的簡實例,有助于對調用運算符定義形式、使用方法和用途的理解。注意,調用運算符函數的右操作數,調用參數列表中的參數也有缺省參數值。2下標運算符“”與調用運算符相似,下標運算符可以用下標表達式的形式對運算符操作所施加對象的相關數據成員進任何需要的和操作,例如,讀寫矩陣類對象封裝的內部矩陣(數組)的某個指定元素。調用該運算符的表達式的一般形式為: 類對象名下標列表;可以認為下標運算符也是一個雙目運算符。左操作數: 必須是

33、該運算符操作所施加的類對象。右操作數: 為類對象所需要傳遞的一組下標,這組下標可以由任意個數的整型對象組成,不無下標。顯然,應該使用成員函數重載下標運算符。 原型返回類型 operator (<下標列表>);其中返回類型可以是除 void 外的任何合法類型。例如:class Matrix double mt10, 10;public:double& operator (int, int); 定義返回類型 類名:operator (下標列表)例如:double& Matrix:operator (int x, int y)if(x > -1 &&

34、 x < 10) && (x > -1 && x < 10) return mtx, y; 調用類對象名調用參數列表;Matrix matrix; matrix5, 8 = 38.5;例如:例4-6 是一個使用成員函數重載下標運算符的簡單實例,有助于對下標運算符定義形式、使用方法和用途的理解。 通過重載的下標運算符函數可以方便地私有數據成員 divisionTotals 中的每一個元素。 重載下標運算符 “” 時,返回一個 int得的,使通過重載的 “”的元素既能夠讀,也能夠寫。因此,下標表達式可以出現在賦值語句的左邊。例4-7 是一個用來處理

35、矩陣運算操作的實例。假定有一個實數矩陣,需要對它進行加法、減法和乘法運算(通過重載運算符+、-、*來實現),為此需要通過重載函數調用運算符(),來返回矩陣元素,以便 在實現矩陣的加、減、乘運算中使用。注意:矩陣的析構函數matrix:matrix() if(elems) /矩陣是否不為空delete elems;elems = 0;4.8 動態內存管理運算符的重載用于動態內存管理的運算符包括 new、delete (用于單個對象的內存分配和對象數組的內存分配和)和 new、delete (用于)。系統預定義的這兩對運算符可以適用于所有類型對象的動態內存管理,但不能確保對所有類型對象的動態內存管

36、理都高效,尤 其是對那些占用內存空間小、結構簡單的類型對象, 使用那些為了適應復雜情況管理的時、空開銷是沒有 必要的,因此大大降低運行效率。解決這一問題的辦 法就是重載動態內存管理運算符,定義適應類型對象 動態創建和撤消的內存管理操作。重載動態內存管理運算符的方式有兩種: 全局方式:重載的動態內存管理運算符完全替代了原有的動態內存管理運算符 new、delete、new、delete 。這種方式一般很少使用,除非確實需要修改原來的通用管理算法或添加所有類型對象創建和撤消都需要的附加操作。 局部方式:重載的運算符函數只對某個特定類對象的創建和撤消有效,這是重載動態內存管理運算符的常用方式。顯然,

37、將動態內存管理運算符重載函數定義為成員函數更符合面向對象程序設計原則。重載 new 和 delete 運算符的一般方法: 定義可以存放 n 個被管理類型對象的靜態數組,作為動態分配的內備。 定義空閑鏈,將被回收的對象內存鏈結起來。 定義指示變量,用于指示作為內空間是否已經被初次順序分配完。備的靜態數組 重載動態內存分配運算符 new,用于順序從靜態數組中或從空閑鏈中為動態創建的對象分配空間。 重載動態內存回收運算符 delete,用于動態創建的對象,并將該被閑鏈中。對象的內存空間鏈結到空例4-8 定義適用于 point 類對象動態創建和撤消的內存管理運算符 new 和 delete 重載成員函

38、數,并測試使用重載的 new 和 delete 動態創建和撤消 point類對象的情況。注意,此例中重載的 new 和 delete 運算符每次只能為單個 point 類對象分配和內存 ,而如果分配和point 類對象數組則需要重載 new 和 delete 運算符。1問題分析分配一個具有足夠空間靜態數組(數組元素的結構應滿足存放 point 類對象屬性和動態分配和的需要。對該數組的分配和操作示意如下:usedfree_listusedfree_listx1 x2x3y1 y2y3x1y1x2y2x3y3x4y4x4y4從數組中順序分配隨機刪除已分配元素其中:·靜態數組的元素由存放位

39、置屬性 x 和 y 的單元,以及用于存放被元素地址的指針單元組成。·靜態數據成員used 用于指示從靜態數組中已經被初次順序分配的元素個數。·靜態數據成員freelist 用于指向被鏈表?;厥盏脑卦氐膭討B分配和操作分為兩種情況: 靜態數組中的元素未被初次順序分配完,即 used的值小于靜態數組的元素總數時(元素第一次被 分配): 分配:以 used 的值為下標,為用戶動態分配一個元素,并修改 used 的值。:將新的元素與 freelist 指向的存放已元素的空閑鏈連接,并使 freelist 指向修改后的空閑鏈。 數組中的元素已被初次順序分配完,即 used 的值大于

40、靜態數組的元素總數時(數組中元素的再分配): 分配:如果被元素的空閑鏈不為空,則從空閑鏈中動態分配一個元素,并修改空閑鏈。:將新的元素與 freelist 指向的存放已元素的空閑鏈連接,并使 freelist 指向修改后的空閑鏈。將運算符 new 和 delete 重載函數和定義為 point類的成員函數,point 類的類圖被描述如下:2詳細設計 類設計 point 類類定義class point int x, y;static int used; static block *freelist;public:point(int vx, int vy);void* operator new (

41、size_t size); void operator delete ();void print();其中,block 為用于動態定義 point 對象時,分配其屬性空間的數據結構:struct block int x, y; block *next;算法描述重載運算符 new 的算法 N-S 圖:重載運算符 delete 的算法 N-S 圖: 類的應用在主函數 main 中動態創建 point 對象、顯示對象信息;然后動態動態創建 point 對象,用以測試重載的動態內存管理運算符 new 和 delete 的功能。程序編碼34.9 賦值運算符重載類對象之間的賦值操作是在一一對應的數據成員之

42、 間進行的。所以賦值運算符的重載函數應作為類的成 員函數。賦值運算符成員函數的原型、定義和調用表 達式如下: 原型類名operator =(const 類名&);例如:class point int x, y;public:point operator = (const point&); 定義類名 類名:operator = (const 類名&對象名)例如:point point:operator = (const point& pt)x = pt.x; y = pt.y;return *this; 調用對象名1 = 對象名2;point pt1, pt2;p

43、t2 = pt1;系統會為每一個類缺省定義一個隱含的賦值運算符例如:成員函數。通常情況下,缺省賦值運算符是可以勝任類對象之間的賦值操作,但在某些特殊情況下,如類中有指針類型的數據成員時,使用缺省賦值運算符就可能產生錯誤。例如:#include <iostream.h> #include <string.h> class stringchar* ptr;public:string(char* s)ptr = new charstrlen(s) + 1; strcpy(ptr, s);string() delete ptr; void print() cout <&l

44、t; ptr << endl; ;void main()string p1("Chen");string p2(“Zhang"); p2 = p1;cout << "p2:"p2.print();cout << "p1:"p1.print();在執行 p1.print(); 時將發生錯誤。是執行賦值語句 p2 = p1,使 p2.ptr 和 p1.ptr 都指向了 “Chen” 占用的內存空間,而 p2.ptr存原來所指向的 “Zhang”占用的內空間被泄漏。當 p2 的生命周期結束時,系

45、統自動調用析構函數將這一內存空間撤消。這時 p1 的指針成員ptr 所指向的內存空間已經成為不的空間了。下圖描述了p這1一動錯態誤空間產1 生的p2過程動:態空間2執行 p2 = p1 之前執行 p2 = p1 之后P2 生命周期結束后解決這一問題的方法是顯式地定義一個重載賦值運算符的成員函數,使 p1 和 p2 有各自的string& operator = (const string&);string& string:operator = (const string& s)空間。if (this = &s) return *this;if (ptr)

46、delete ptr;/ 類對象自身賦值/原有動態內存指針ptr = new charstrlen(s.ptr) + 1;/ 為賦值操作分配新內存strcpy(ptr, s.ptr);return *this;/ 賦值字符串值/ 返回被賦值的類對象兩點說明: 重載類的賦值運算符應該使用成員函數,而不應使元函數,如果上述賦值運算符重載使用了友元函數:friend string& operator = (string& p2, string& p1);這時,表達式p1 = "chen"將被解釋為operator = (p1, "chen&quo

47、t;)這顯然是沒有什麼問題的,但對于表達式"chen" = p1將被解釋為:/ 錯誤的賦值語句operator = ("chen", p1)C+ 編譯器首先將 “chen”即string轉換成一個隱含的對象,然后該隱含對象。因此并不認為這個表達式是錯誤的,從而將導致賦值語法的。 類的賦值運算符可以被重載,但重載的賦值運算符成員函數在其派生類中是不能被繼承的。建議:在屬性的操作上,重載賦值運算符和拷貝構造函數具有一致性。因此,在需要用戶自定義重載賦值運算符和拷貝構造函數的情況下,一般先定義一個實現屬性操作的私有成員函數,然后在重載賦值運算符和拷貝構造函數定

48、義中調用此私有成員函數。這樣既保證了重載賦值運算符和拷貝構造函數的4.10 重載輸入運算符 ">>"輸入運算符 “>>”講是輸入流類(將在第八章中詳細述)的成員函數。該運算符也是一個雙目運算符,它的左操作數必須是輸入流類對象的,表示被輸入的信息必須來自標準的輸入流設備;而右操作數是接收輸入信息的指定類對象的。因此,為自定義類重載的輸入運算符函數只能是類的友元函數。1原型friend 輸入流類&operator >> (輸入流類&,類名&);例如:class pointint x, y;public:friend i

49、stream& operator >> (istream&, point&);2定義輸入流& operator >>(輸入流&流對象名, 類名& 對象名)return 流對象名;例如:istream& operator >> (istream& in, point& pt);cout << “順序輸入點的位置坐標 x 和 y:”;in >> pt.x >> pt.y; return in;3調用cin >> 類對象名;例如:point pt

50、;cin >> pt;注意: 輸入運算符重載函數的第一個參數的類型必須是輸入流類 istream 對象的,形參名(流對象名)可以使用任何合法的標識符。 輸入運算符重載函數的第二個參數的類型必須是接收輸入信息的指定類對象的,例如 point&, 而不能使用指定類名,例如 point。 輸入運算符重載函數的返回類型必須是輸入流類istream 對象的的輸入流類對象的,并且在函數體中由 return 返回名必須與第一個參數的形參名(流對象名)一致。4.11 重載輸出運算符 “<<"輸出運算符 “<<”講是輸出流類(將在第八章中詳細述)的成員函數。

51、該運算符也是一個雙目運算符,它的左操作數必須是輸出流類對象的,表示信息必須被輸出到標準的輸出流設備;而右操作數是輸出信息的指定類對象。因此,為自定義類重載的輸入運算符函數也只能是類的友元函數。1原型friend 輸出流類&operator << (輸出流類&,類名);例如:class pointint x, y;public:friend istream& operator >> (istream&, point&); friend ostream& operator << (ostream&, poi

52、nt);2定義輸出流& operator <<(輸出流&流對象名, 類名 對象名)return 流對象名;例如:ostream& operator << (ostream& out, point pt);out << “點的位置坐標 x 和 y:”;out << pt.x << “,” <<return out;pt.y << endl;3調用cout << 類對象名;例如:point pt;cin >> pt;cout << pt;注意: 輸

53、出運算符重載函數的第一個參數的類型必須是輸出流類 ostream 對象的,形參名(流對象名)可以使用任何合法的標識符。 輸出運算符重載函數的第二個參數的類型必須是輸出信息的指定類對象或對象的或 point。,例如 point&, 輸出運算符重載函數的返回類型必須是輸出流類ostream 對象的的輸出流類對象的,并且在函數體中由 return 返回名必須與第一個參數的形參名(流對象名)一致。例8-3 中對輸入運算符 >> 和輸出運算符 << 進行了重載,使它們能對按照指定形式(例如由實部和虛部組成的代數和形式,20 + 35i )表示的復數進行標準輸入輸出操作。例8-4 中對輸入運算符 >> 和輸出運算符 << 進行了重載,使它們能對按照指定形式(例如由分母組成的分數形式,3/8) 表示的有理數進行標準輸入輸出操作。4.12 類型轉換所謂類型轉換是指編譯器將一種數據類型值轉換

溫馨提示

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

評論

0/150

提交評論