譚浩強版《C++程序設計》第9章.ppt

上傳人:xt****7 文檔編號:20379914 上傳時間:2021-03-14 格式:PPT 頁數:89 大小:476.50KB
收藏 版權申訴 舉報 下載
譚浩強版《C++程序設計》第9章.ppt_第1頁
第1頁 / 共89頁
譚浩強版《C++程序設計》第9章.ppt_第2頁
第2頁 / 共89頁
譚浩強版《C++程序設計》第9章.ppt_第3頁
第3頁 / 共89頁

下載文檔到電腦,查找使用更方便

14.9 積分

下載資源

還剩頁未讀,繼續(xù)閱讀

資源描述:

《譚浩強版《C++程序設計》第9章.ppt》由會員分享,可在線閱讀,更多相關《譚浩強版《C++程序設計》第9章.ppt(89頁珍藏版)》請在裝配圖網上搜索。

1、第 9章 關于類和對象的進一步討論 9.1 構造函數 9.2 析構函數 9.3 調用構造函數和析構函數的順序 9.4 對象數組 9.5 對象指針 9.6 共用數據的保護 9.7 對象的動態(tài)建立和釋放 9.8 對象的賦值和復制 9.9 靜態(tài)成員 9.10 友元 9.11 類模板 在建立一個對象時 ,作某些初始化的工作如 對數據 成員賦初值 。即在 創(chuàng)建對象(分配內存時)進行數 據成員的初始化 ,因為對象是實實在在的對象,不 能無具體屬性值。 注意: 類的數據成員是不能在聲明類時初始化的 。 9.1 構造函數 作用: 創(chuàng)建對象(分配內存時)時進行數據 成員的初始化 9.1.1 對象的初始化 如果一

2、個類中所有的成員都是公用的,則可以在定 義對象時對數據成員進行初始化。如 class Time public: /聲明為公用成員 hour; minute; sec; ; Time t1=14,56,30; /將 t1初始化為 14:56:30 但是, 一般數據成員是私有的,或者類中有 private 或 protected的成員,就不能用這種方法初始化 。 如何實現? C+提供了構造函數 (constructor)來處理對象的初 始化。 構造函數是特殊的成員函數,與其他成員函數不 同, 不需要用戶來調用它,而是在建立對象時自動 執(zhí)行 。 構造函數的 名字必須與類名同名 ,而不能由用戶 任意命

3、名,以便編譯系統(tǒng)能識別它并把它作為構造 函數處理。 它不具有任何類型,不返回任何值 。 構造函數的功能是由用戶定義的, 用戶根據初始 化的要求設計函數體和函數參數 。 9.1.2 構造函數的作用 例 9.1 在例 8.3基礎上定義構造成員函數。 #include using namespace std; class Time public: Time( ) /定義構造成員函數,函數名與類名相同 hour=0; /利用構造函數對對象中的數據成員賦初值 minute=0; sec=0; void set_time( ); /函數聲明 void show_time( ); /函數聲明 private

4、: int hour; /私有數據成員 int minute; int sec; ; void Time set_time( ) /定義成員函數,向數據成員賦值 cinhour; cinminute; cinsec; void Time show_time( ) /定義成員函數,輸出數據成員的值 couthour:minute:secendl; int main( ) Time t1; /建立對象 t1, 同時調用構造函數 t1.Time( ) t1.set_time( ); /對 t1的數據成員賦值 t1.show_time( ); /顯示 t1的數據成員的值 Time t2; /建立對象

5、t2, 同時調用構造函數 t2.Time( ) t2.show_time( ); /顯示 t2的數據成員的值 return 0; 程序運行的情況為: 10 25 54 (從鍵盤輸入新值賦給 t1的數據成員 ) 10:25:54 (輸出 t1的時、分、秒值 ) 0:0:0 (輸出 t2的時、分、秒值 ) 也可以 在類外定義構造函數: Time Time( ) /要加上類名 Time和域限定 符 “ ” hour=0; minute=0; sec=0; 有關構造函數的使用,有以下說明: (1) 在類對象進入其作用域時調用構造函數。 (2) 構造函數沒有返回值,因此也不需要在定義構 造函數時聲明類型

6、,這是它和一般函數的一個重要 的不同之點。 (3) 構造函數 不需用戶調用 ,也不能被用戶調用。 (4) 如果用戶自己沒有定義構造函數,則 C+系統(tǒng) 會自動生成一個構造函數,只是這個構造函數的函 數體是空的,也沒有參數,不執(zhí)行初始化操作。 不帶參數構造函數, 這種方式使該類的每一個對 象都得到同一組初值。 帶參數的構造函數,用戶希望對不同的對象賦予 不同的初值 。 構造函數首部的一般格式: 構造函數名 (類型 1 形參 1,類型 2 形參 2, ) 實參是在定義對象時給出的 。 定義對象的一般格式為 : 類名 對象名 (實參 1,實參 2, ); 9.1.3 帶參數的構造函數 例 9.2 有兩

7、個長方柱,其長、寬、高分別為: (1)12,20,25; (2)10,14,20。求它們的體積。編一個 基于對象的程序,在類中用帶參數的構造函數。 #include using namespace std; class Box public: Box(int,int,int); /聲明帶參數的構造函數 int volume( ); /聲明計算體積的函數 private: int height; int width; int length; ; Box Box(int h,int w,int len) /在類外定義帶參數的構造函數 height=h; width=w; length=len; i

8、nt Box volume( ) /定義計算體積的函數 return(height*width*length); int main( ) Box box1(12,25,30); /建立對象 box1, 并指定 box1長、寬、高的值 coutThe volume of box1 is box1.volume( )endl; Box box2(15,30,21); /建立對象 box2, 并指定 box2長、寬、高的值 coutThe volume of box2 is box2.volume( )endl; return 0; 程序運行結果如下: The volume of box1 is 9

9、000 The volume of box2 is 9450 注意 : 帶參數的構造函數中的形參, 其對應的實參在定義 對象時給定。 C+還提供另一種初始化數據成員的方法 參數 初始化表 來實現對數據成員的初始化。 這種方法不在函數體內對數據成員初始化,而是在 函數首部實現。 例如例 9.2中定義構造函數可以改 用以下形式: Box Box(int h,int w,int len):height(h), width(w), length(len) 這種寫法方便、簡練,尤其當需要初始化的數據成 員較多時更顯其優(yōu)越性。 甚至可以直接在類體中 (而不是在類外 )定義構造函數。 9.1.4 用參數初始

10、化表對數據成員初始化 在一個類中可以定義多個構造函數,以便對類對 象提供不同的初始化的方法,供用戶選用。 這些構造函數具有相同的名字,而參數的個數或 參數的類型不相同。這稱為 構造函數的重載 。 在第 4章第 4.6節(jié)中所介紹的函數重載的知識也適用 于構造函數。 通過下面的例子可以了解怎樣應用構造函數的重載。 9.1.5 構造函數的重載 例 9.3 在例 9.2的基礎上,定義兩個構造函數,其中 一個無參數,一個有參數。 #include using namespace std; class Box public: Box( ); /聲明一個無參的構造函數 Box(int h,int w,int

11、 len):height(h),width(w),length(len) /聲明一個有參的構造函數,用參數的初始化表對數據成員初始化 int volume( ); private: int height; int width; int length; ; Box Box( ) /定義一個無參的構造函數 height=10; width=10; length=10; int Box volume( ) return(height*width*length); int main( ) Box box1; /建立對象 box1,不指定實參 coutThe volume of box1 is box1

12、.volume( )endl; Box box2(15,30,25); /建立對象 box2,指定 3個實參 coutThe volume of box2 is box2.volume( )endl; return 0; 在本程序中定義了兩個重載的構造函數, 其實還可 以定義其他重載構造函數 。 說明: (1) 調用構造函數時不必給出實參的構造函數,稱 為 默認構造函數 (default constructor)。 顯然,無參 的構造函數屬于默認構造函數。一個類只能有一個 默認構造函數。 (2) 如果在建立對象時選用的是無參構造函數,應 注意正確書寫定義對象的語句。 (3) 盡管在一個類中可以

13、包含多個構造函數,但是 對于每一個對象來說, 建立對象時只執(zhí)行其中一個 構造函數 ,并非每個構造函數都被執(zhí)行。 構造函數中參數的值既可以通過實參傳遞,也可以 指定為某些默認值,即 如果用戶不指定實參值,編 譯系統(tǒng)就使形參取默認值 。 例 9.4 將例 9.3程序中的構造函數改用含默認值的參 數,長、寬、高的默認值均為 10。 在例 9.3程序的基礎上改寫如下: 9.1.6 使用默認參數的構造函數 #include using namespace std; class Box public: Box(int h=10,int w=10,int len=10); /在聲明構造函數時指定默認參數 i

14、nt volume( ); private: int height; int width; int length; ; Box Box(int h,int w,int len) /在定義函數時可以不指定默認參數 height=h; width=w; length=len; int Box volume( ) return(height*width*length); int main( ) Box box1; /沒有給實參 coutThe volume of box1 is box1.volume( )endl; Box box2(15); /只給定一個實參 coutThe volume of

15、box2 is box2.volume( )endl; Box box3(15,30); /只給定 2個實參 coutThe volume of box3 is box3.volume( )endl; Box box4(15,30,20); /給定 3個實參 coutThe volume of box4 is box4.volume( )endl; return 0; 析構函數 (destructor)也是一個特殊的成員函數,它 的作用與構造函數相反,它的 名字是類名的前面加 一個“”符號 。在 C+中 “ ” 是位取反運算符, 從這點也可以想到: 析構函數是與構造函數作用 相反的函數。 當對

16、象的生命期結束時,會自動執(zhí)行析構函數 。 如果在一個函數中定義了一個對象 (它是自動局 部對象 ),當這個函數被調用結束時,對象應該釋 放, 在對象釋放前自動執(zhí)行析構函數。 9.2 析構函數 static局部對象 ,只在 main函數結束或調用 exit函 數結束程序時,才調用 static局部對象的析構函數。 全局對象 ,則在程序的流程離開其作用域時 (如 main函數結束或調用 exit函數 ) 時,調用該全局對 象的析構函數。 如果用 new運算符 動態(tài)地建立了一個對象 ,當用 delete運算符釋放該對象時,先調用該對象的析構 函數。 析構函數的作用: 并不是刪除對象,而是在撤銷對 象

17、占用的內存之前完成一些清理工作,使這部分內 存可以被程序分配給新對象使用 。 程序設計者事先設計好析構函數,只要對象的生命 期結束,程序就自動執(zhí)行析構函數來完成這些工作。 析構函數不返回任何值,沒有函數類型,也沒有函 數參數。 因此它不能被重載 。 一個類可以有多個構 造函數,但只能有一個析構函數。 析構函數的作用 并不僅限于釋放資源 方面,它還 可以 輸出有關的信息 。 一般情況下,類的設計者應當在聲明類的同時定義 析構函數,以指定如何完成 “ 清理 ” 的工作。 如果用戶沒有定義析構函數, C+編譯系統(tǒng)會自動 生成一個析構函數,但它只是徒有析構函數的名稱 和形式, 實際上什么操作都不進行

18、。想讓析構函數 完成任何工作,都必須在定義的析構函數中指定。 例 9.5 包含構造函數和析構函數的 C+程序。 #include #include using namespace std; class Student /聲明 Student類 public: student(int n,string nam,char s ) /定義構造函數 num=n; name=nam; sex=s; coutConstructor called.endl; /輸出有關信息 Student( ) /定義析構函數 coutDestructor called.endl; /輸出有關信息 void display

19、( ) /定義成員函數 coutnum: numendl; coutname: nameendl; coutsex: sexendlendl; private: int num; char name10; char sex; ; int main( ) Student stud1(10010,Wang_li,f); / 建立對象 stud1 stud1.display( ); /輸出學生 1的數據 Student stud2(10011,Zhang_fun,m); /定義對象 stud2 stud2.display( ); /輸出學生 2的數據 return 0; 程序運行結果如下: Cons

20、tructor called. (執(zhí)行 stud1的構造函數 ) num: 10010 (執(zhí)行 stud1的 display函數 ) name:Wang_li sex: f Constructor called. (執(zhí)行 stud2的構造函數 ) num: 10011 (執(zhí)行 stud2的 display函數 ) name:Zhang_fun sex:m Destructor called. (執(zhí)行 stud2的析構函數 ) Destructor called. (執(zhí)行 stud1的析構函數 ) 在使用構造函數和析構函數時,需要特別注意對它 們的調用時間和調用順序。 在一般情況下, 調用析構函

21、數的次序正好與調用構 造函數的次序相反: 最先被調用的構造函數,其 對應的 (同一對象中的 )析構函數最后被調用,而最 后被調用的構造函數,其對應的析構函數最先被調 用。如圖 9.1示意。 9.3 調用構造函數和析構函數的順序 圖 9.1 但是,并不是在任何情況下都是按這一原則處理的。 對象可以在不同的作用域中定義,可以有不同的存 儲類別。這些會影響調用構造函數和析構函數的時 機。 下面歸納一下什么時候調用構造函數和析構函數: (1) 在全局范圍中定義的對象 它的構造函數在文件中的所有函數 (包括 main函數 ) 執(zhí)行之前調用。當 main函數執(zhí)行完畢或調用 exit函 數時 (此時程序終止

22、 ),調用析構函數。 (2) 如果定義的是局部自動對象 (例如在函數中定義 對象 ),則在建立對象時調用其構造函數。如果函 數被多次調用,則在每次建立對象時都要調用構造 函數。在函數調用結束、對象釋放時先調用析構函 數。 (3) 如果在函數中定義靜態(tài) (static)局部對象,則只 在程序第一次調用此函數建立對象時調用構造函數 一次,只在 main函數結束或調用 exit函數結束程序 時,才調用析構函數。 數組也可以由對象組成 (對象數組的每一個元素都 是同類的對象 )。 例如一個班有 50個學生,每個學生的屬性包括姓名、 性別、年齡、成績等。如果為每一個學生建立一個 對象,需要分別取 50個

23、對象名。用程序處理很不方 便。這時可以定義一個 “ 學生類 ” 對象數組,每一 個數組元素是一個 “ 學生類 ” 對象。例如 Student stud50; /假設已聲明了 Student類,定義 stud數組,有 50個元素 9.4 對象數組 在建立數組時,同樣要調用構造函數。如果有 50個 元素,需要調用 50次構造函數。 如果構造函數有多個參數,則不能用在定義數組時 直接提供所有實參的方法,因為一個數組有多個元 素,對每個元素要提供多個實參,如果再考慮到構 造函數有默認參數的情況,很容易造成實參與形參 的對應關系不清晰,出現歧義性。 如果構造函數有多個參數,在定義對象數組時應當 怎樣實現

24、初始化呢? 在花括號中分別寫出構造函數并指定實參。如果構 造函數有 3個參數,分別代表學號、年齡、成績。 則可以這樣定義對象數組: Student Stud3= /定義對象數組 Student(1001,18,87), /調用第 1個元素的構造函數,為它提 供 3個實參 Student(1002,19,76), /調用第 2個元素的構造函數,為它提 供 3個實參 Student(1003,18,72) /調用第 3個元素的構造函數,為它提 供 3個實參 ; 在建立對象數組時,分別調用構造函數,對每個元 素初始化。每一個元素的實參分別用括號包起來, 對應構造函數的一組形參,不會混淆。 例 9.6

25、 對象數組的使用方法。 #include using namespace std; class Box public: Box(int h=10,int w=12,int len=15): height(h),width(w),length(len) /聲明有默認參數的構造函數,用參數初始化表對數據成員初始 化 int volume( ); private: int height; int width; int length; ; int Box volume( ) return(height*width*length); int main( ) Box a3= /定義對象數組 Box(10,

26、12,15), /調用構造函數 Box, 提供第 1個元素的實參 Box(15,18,20), /調用構造函數 Box, 提供第 2個元素的實參 Box(16,20,26) /調用構造函數 Box, 提供第 3個元素的實參 ; coutvolume of a0 is a0.volume( )endl; coutvolume of a1 is a1.volume( )endl; coutvolume of a2 is a2.volume( )endl; 運行結果如下: volume of a0 is 1800 volume of a1 is 5400 volume of a2 is 8320 在

27、建立對象時,編譯系統(tǒng)會為每一個對象分配一定 的存儲空間,以存放其成員。 對象空間的起始地址 就是對象的指針 ??梢远x一個指針變量,用來存 放對象的指針。如果有一個類: class Time public: int hour; int minute; int sec; void get_time( ); ; 9.5 對象指針 9.5.1 指向對象的指針 void Time get_time( ) couthour:minute:sechour pt所指向的對象中的 hour成員,即 t1.hour (*pt).get_time ( ) 調用 pt所指向的對象中的 get_time函數,即 t1

28、.get_time pt-get_time ( ) 調用 pt所指向的對象中的 get_time函數,即 t1.get_time 對象有地址,存放對象初始地址的指針變量就是指 向對象的指針變量。對象中的成員也有地址,存放 對象成員地址的指針變量就是指向對象成員的指針 變量。 9.5.2 指向對象成員的指針 1. 指向對象數據成員的指針 定義指向對象數據成員的指針變量的方法和 定義指 向普通變量的指針變量方法相同 。例如 int *p1; /定義指向整型數據的指針變量 定義指向對象數據成員的指針變量的一般形式為 數據類型名 *指針變量名; 如果 Time類的數據成員 hour為 公用的 整型數據

29、, 則 可以在類外 通過指向對象數據成員的指針變量訪 問對象數據成員 hour。 p1= /將對象 t1的數據成員 hour的地址賦給 p1, p1指向 t1.hour cout*p1endl; /輸出 t1.hour的值 例 9.7 有關對象指針的使用方法。 #include using namespace std; class Time public: Time(int,int,int); int hour; int minute; int sec; void get_time( ); /聲明公有成員函數 ; Time Time(int h,int m,int s) hour=h; min

30、ute=m; sec=s; void Time get_time( ) /定義公有成員函數 couthour:minute: secendl; int main( ) Time t1(10,13,56); /定義 Time類對象 t1 int *p1= /定義指向整型數據的指針變量 p1, 并使 p1指 向 t1.hour cout*p1get_time( ); /調用 p2所指向對象 (即 t1)的 get_time函數 每個對象中的數據成員都分別占有存儲空間,如果 對同一個類定義了 n個對象,則有 n組同樣大小的空 間以存放 n個對象中的數據成員。但是,不同對象 都調用同一個函數代碼段。

31、那么,當不同對象的成員函數引用數據成員時,怎 么能保證引用的是所指定的對象的數據成員呢? 9.5.3 this 指針 在每一個成員函數中都包含一個特殊的指針,這個 指針的名字是固定的,稱為 this。 它是指向本類對象的指針,它的值是當前被調用的 成員函數所在的對象的起始地址 。例如,當調用成 員函數 a.volume時,編譯系統(tǒng)就把對象 a的起始地 址賦給 this指針,于是在成員函數引用數據成員時, 就按照 this的指向找到對象 a的數據成員。例如 volume函數要計算 height*width*length的值,實際 上是執(zhí)行: (this-height)*(this-width)*

32、(this-length) 由于當前 this指向 a, 因此相當于執(zhí)行: (a.height)*(a.width)*(a.length) 這就計算出長方體 a的體積。 this指針是隱式使用的,它是作為參數被傳遞給成 員函數的 。本來,成員函數 volume的定義如下: int Box volume( ) return (height*width*length); C+把它處理為 int Box volume(Box *this) return(this-height * this-width * this-length); 即在成員函數的形參表列中增加一個 this指針。在 調用該成員函數

33、時,實際上是用以下方式調用的: a.volume( 將對象 a的地址傳給形參 this指針。然后按 this的指 向去引用其他成員。 需要說明: 這些都是編譯系統(tǒng)自動實現的,編程 序者不必人為地在形參中增加 this指針,也不必將 對象 a的地址傳給 this指針。 在需要時也可以顯式地使用 this指針 。例如在 Box 類的 volume函數中,下面兩種表示方法都是合法的、 相互等價的。 return(height * width * length); /隱含使用 this指針 return(this-height * this-width * this-length); /顯式使用 th

34、is指針 可以用 *this表示被調用的成員函數所在的對象, *this就是 this所指向的對象,即當前的對象。 例如 在成員函數 a.volume( )的函數體中,如果出現 *this, 它就是本對象 a。 上面的 return語句也可寫成 return(*this).height * (*this).width * (*this).length); 注意 *this兩側的括號不能省略,不能寫成 *this.height。 所謂 “ 調用對象 a的成員函數 f”, 實際上是在調用 成員函數 f時使 this指針指向對象 a, 從而訪問對象 a 的成員。在使用 “ 調用對象 a的成員函數 f

35、”時,應 當對它的含義有正確的理解。 有時人們希望在需要用到對象時才建立對象,在不 需要用該對象時就撤銷它,釋放它所占的內存空間 以供別的數據使用。這樣 可提高內存空間的利用率 。 用 new運算符動態(tài)建立對象,用 delete運算符撤銷 對象。 如果已經定義了一個 Box類,可以用下面的方法動 態(tài)地建立一個對象: 9.7 對象的動態(tài)建立和釋放 new Box; 編譯系統(tǒng)開辟了一段內存空間,并在此內存空間中 存放一個 Box類對象,同時調用該類的構造函數, 以使該對象初始化 (如果已對構造函數賦予此功能 的話 )。但是此時用戶還無法訪問這個對象,因為 這個對象既沒有對象名,用戶也不知道它的地址

36、。 這種對象稱為 無名對象 ,它確實是存在的,但它沒 有名字。 用 new運算符動態(tài)地分配內存后,將返回一個指向 新對象的指針的值,即所分配的內存空間的起始地 址。用戶 需要定義一個指向本類的對象的指針變量 來存放該地址 。如 Box *pt; /定義一個指向 Box類對象的指針變量 pt pt=new Box; /在 pt中存放了新建對象的起始地址 在程序中就可以通過 pt訪問這個新建的對象。如 coutheight; /輸出該對象的 height成員 coutvolume( ); /調用該對象的 volume函數,計算并輸出體積 C+還 允許在執(zhí)行 new時,對新建立的對象進行初 始化。

37、如 (調帶參數的構造函數) Box *pt=new Box(12,15,18); 這種寫法是把上面兩個語句 (定義指針變量和用 new 建立新對象 )合并為一個語句,并指定初值。這樣 更精煉。新對象中的 height, width和 length分別獲 得初值 12,15,18。 調用對象既可以通過對象名,也可以通過指針 。用 new建立的動態(tài)對象一般是不用對象名的,是通過 指針訪問的,它主要應用于動態(tài)的數據結構,如鏈 表。訪問鏈表中的結點,并不需要通過對象名 , 在執(zhí)行 new運算時,如果內存量不足,無法開辟所 需的內存空間,目前大多數 C+編譯系統(tǒng)都使 new 返回一個 0指針值。 只要檢

38、測返回值是否為 0,就可 判斷分配內存是否成功。 在不再需要使用由 new建立的對象時,可以用 delete運算符予以 釋放 。如 delete pt; /釋放 pt指向的內存空間 這就撤銷了 pt指向的對象。 在執(zhí)行 delete運算符時,在釋放內存空間之前,自 動 調用析構函數 ,完成有關善后清理工作 。 如果對一個類定義了兩個或多個對象,則這些 同類 的對象之間可以互相賦值 ,即一個對象的值可以賦 給另一個同類的對象。 對象的值是指對象中所有數 據成員的值 。 對象之間的賦值也是通過賦值運算符 “ =” 進行的。 本來,賦值運算符 “ =” 只能用來對單個的變量賦 值,現在被 擴展為兩個

39、同類對象之間的賦值,這是 通過對 賦值運算符的重載 實現的 。實際這個過程是 通過成員復制來完成的,即將一個對象的成員值一 一復制給另一對象的對應成員。 9.8 對象的賦值和復制 COPY 9.8.1 對象的賦值 對象名 1 = 對象名 2; 注意對象名 1和對象名 2必須屬于同一個類 。 例如 Student stud1,stud2; /定義兩個同類的對象 stud2=stud1; /將 stud1賦給 stud2 通過下面的例子可以了解怎樣進行對象的賦值。 例 9.9 對象的賦值。 #include using namespace std; class Box public: Box(in

40、t=10,int=10,int=10); /聲明有默認參數的構造函數 int volume( ); private: int height; int width; int length; ; Box Box(int h,int w,int len) height=h; width=w; length=len; int Box volume( ) return(height*width*length); /返回體積 int main( ) Box box1(15,30,25),box2; /定義兩個對象 box1和 box2 coutThe volume of box1 is box1.volu

41、me( )endl; box2=box1; /將 box1的值賦給 box2 coutThe volume of box2 is box2.volume( )endl; return 0; 運行結果如下: The volume of box1 is 11250 The volume of box2 is 11250 說明: (1) 對象的賦值只對其中的數據成員賦值,而不對 成員函數賦值。 (2) 類的數據成員中 不能包括動態(tài)分配的數據,否 則在賦值時可能出現嚴重后果 。 有時需要用到多個完全相同的對象。此外,有時需 要將對象在某一瞬時的狀態(tài)保留下來。這就是對象 的復制機制。 用一個已有的對象快

42、速地復制出多個 完全相同的對象 。如 Box box2(box1); 其作用是用已有的對象 box1去克隆出一個新對象 box2。 其一般形式為 類名 對象 2(對象 1); 用對象 1復制出對象 2。 9.8.2 對象的復制 可以看到: 它與前面介紹過的定義對象方式類似, 但是 括號中給出的參數不是一般的變量,而是對象 。 在建立對象時 調用一個特殊的構造函數 復制構 造函數 (copy constructor)。 這個函數的形式是這樣 的: /The copy constructor definition. Box Box(const Box width=b.width; length=b

43、.length; 復制構造函數也是構造函數,但它只有一個參數, 這個參數是本類的對象 (不能是其他類的對象 ),而 且采用對象的引用的形式 (一般約定加 const聲明, 使參數值不能改變,以免在調用此函數時因不慎而 使對象值被修改 )。 此復制構造函數的 作用就是將實參對象的各成員值 一一賦給新的對象中對應的成員 。 回顧復制對象的語句 Box box2(box1); 這實際上也是建立對象的語句,建立一個新對象 box2。 由于在括號內給定的實參是對象,因此編譯 系統(tǒng)就調用復制構造函數 (它的形參也是對象 ),而 不會去調用其他構造函數。 實參 box1的地址傳遞給 形參 b(b是 box1

44、的引用 ),因此執(zhí)行復制構造函數的 函數體時,將 box1對象中各數據成員的值賦給 box2 中各數據成員。 如果用戶自己未定義復制構造函數,則編譯系統(tǒng)會 自動提供一個默認的復制構造函數 , 其作用只是簡 單地復制類中每個數據成員。 C+還提供另一種方便用戶的復制形式,用賦值號 代替括號 ,如 Box box2=box1; /用 box1初始化 box2 其一般形式為 類名 對象名 1 = 對象名 2; 可以在一個語句中進行多個對象的復制。如 Box box2=box1,box3=box2; 按 box1來復制 box2和 box3。 可以看出: 這種形式 與變量初始化語句類似,請與下面定義變

45、量的語句 作比較: int a=4,b=a; 這種形式看起來很直觀,用起來很方便。 但是其作 用都是調用復制構造函數。 請注意對象的復制和對象的賦值在概念上和語法上 的區(qū)別 。對象的賦值是對一個已經存在的對象賦值, 因此必須先定義被賦值的對象,才能進行賦值。而 對象的復制則是從無到有地建立一個新對象,并使 它與一個已有的對象完全相同 (包括對象的結構和 成員的值 )。 可以對例 9.7程序中的主函數作一些修改 : int main( ) Box box1(15,30,25); /定義 box1 coutThe volume of box1 is box1.volume( )endl; Box

46、box2=box1,box3=box2; /按 box1來復制 box2,box3 coutThe volume of box2 is box2.volume( )endl; coutThe volume of box3 is box3.volume( )endl; 執(zhí)行完第 3行后, 3個對象的狀態(tài)完全相同。 請注意普通構造函數和復制構造函數的區(qū)別 ! (1) 在形式上 類名 (形參表列 ); /普通構造函數的聲明,如 Box(int h,int w,int len); 類名 (類名 /復制構造函數的聲明,如 Box(Box (2) 在建立對象時,實參類型不同。系統(tǒng)會根據實 參的類型決定調用

47、普通構造函數或復制構造函數。 如 Box box1(12,15,16); /實參為整數,調用普通構造函數 Box box2(box1); /實參是對象名,調用復制構造函數 ( 3) 在什么情況下被調用 普通構造函數在程序中建立對象時被調用。 復制構造函數在用已有對象復制一個新對象時被調 用, 在以下 3種情況下需要克隆對象 : 程序中需要新建立一個對象,并用另一個同類 的對象對它初始化,如前面介紹的那樣。 當函數的參數為類的對象時。在調用函數時需 要將實參對象完整地傳遞給形參,也就是需要建立 一個實參的拷貝,這就是按實參復制一個形參,系 統(tǒng)是通過調用復制構造函數來實現的,這樣能保證 形參具有和

48、實參完全相同的值。如 void fun(Box b) /形參是類的對象 int main( ) Box box1(12,15,18); fun(box1); /實參是類的對象,調用函數時將復制一個新對象 b return 0; 函數的返回值是類的對象。在函數調用完畢將 返回值帶回函數調用處時。此時需要將函數中的對 象復制一個臨時對象并傳給該函數的調用處。如 Box f( ) /函數 f的類型為 Box類類型 Box box1(12,15,18); return box1; /返回值是 Box類的對象 int main( ) Box box2; /定義 Box類的對象 box2 box2=f(

49、); /調用 f函數,返回 Box類的臨時對象,并將它賦值給 box2 以上幾種調用復制構造函數都是由編譯系統(tǒng)自動實 現的,不必由用戶自己去調用,讀者只要知道在這 些情況下需要調用復制構造函數就可以了 。 如果有 n個同類的對象,那么每一個對象都分別有 自己的數據成員,不同對象的數據成員各自有值, 互不相干。 但是有時人們希望有某一個或幾個數據 成員為所有對象所共有。這樣可以實現數據共享 。 在第 7章中曾介紹過全局變量,它能夠實現數據共 享。如果在一個程序文件中有多個函數,在每一個 函數中都可以改變全局變量的值,全局變量的值為 各函數共享。但是用全局變量的安全性得不到保證, 由于在各處都可以

50、自由地修改全局變量的值,很有 可能偶一失誤,全局變量的值就被修改,導致程序 的失敗。因此在實際工作中很少使用全局變量。 如果想在同類的多個對象之間實現數據共享,也不 要用全局對象,可以用靜態(tài)的數據成員。 9.9 靜態(tài)成員 靜態(tài)數據成員是一種特殊的數據成員。 它以關鍵字 static開頭 。例如 class Box public: int volume( ); private: static int height; /把 height定義為靜態(tài)的數據成員 int width; int length; ; 如果希望各對象中的 height的值是一樣的,就可以 把它定義為靜態(tài)數據成員,這樣 它就為各

51、對象所共 有,而 不只屬于某個對象的成員 , 9.9.1 靜態(tài)數據成員 所有對象都可以引用它。每個對象都可以引用這個 靜態(tài)數據成員。靜態(tài)數據成員的值對所有對象都是 一樣的。如果改變它的值,則在各對象中這個數據 成員的值都同時改變了。這樣可以節(jié)約空間,提高 效率 。 說明: (1) 靜態(tài)數據成員不屬于某一個對象,在為對象所 分配的空間中不包括靜態(tài)數據成員所占的空間。靜 態(tài)數據成員是在所有對象之外單獨開辟空間。 只要 在類中定義了靜態(tài)數據成員,即使不定義對象,也 為靜態(tài)數據成員分配空間,它可以被引用 。 (2) 靜態(tài)數據成員 ,它不隨對象的建立而分配空間, 也不隨對象的撤銷而釋放 。 靜態(tài)數據成員

52、是在程序 編譯時被分配空間的,到程序結束時才釋放空間。 (3) 靜態(tài)數據成員可以初始化 ,但 只能在類體外進 行初始化 。如 int Box height=10; /表示對 Box類中的數據成員初始化 其一般形式為 數據類型類名 靜態(tài)數據成員名 =初值; 不必在初始化語句中加 static。 注意: 不能用參數初始化表對靜態(tài)數據成員初始 化 。如在定義 Box類中這樣定義構造函數是錯誤的: Box(int h,int w,int len):height(h) /錯誤, height是靜態(tài)數據成員 如果未對靜態(tài)數據成員賦初值,則編譯系統(tǒng)會自動 賦予初值 0。 (4) 靜態(tài)數據成員既可以通過對象名

53、引用,也可以 通過類名來引用。 例 9.10 引用靜態(tài)數據成員。 #include using namespace std; class Box public: Box(int,int); int volume( ); static int height; /把 height定義為公用的靜態(tài)的數據成員 int width; int length; ; Box Box(int w,int len) /通過構造函數對 width和 length賦初值 width=w; length=len; int Box volume( ) return(height*width*length); int Bo

54、x height=10; /對靜態(tài)數據成員 height初始化 int main( ) Box a(15,20),b(20,30); couta.heightendl; /通過對象名 a引用靜態(tài)數據成員 coutb.heightendl; /通過對象名 b引用靜態(tài)數據成員 coutBox heightendl; /通過類名引用靜態(tài)數據成員 couta.volume( )endl; /調用 volume函數,計算體積,輸出結果 上面 3個輸出語句的輸出結果相同 (都是 10)。所有對 象的靜態(tài)數據成員實際上是同一個數據成員。 請注意: 在上面的程序中將 height定義為公用的靜 態(tài)數據成員,所

55、以在類外可以直接引用。 在類外 可以通過對象名引用公用的靜態(tài)數據成員, 也可以通過類名引用靜態(tài)數據成員。 即使沒有定義 類對象,也可以通過類名引用靜態(tài)數據成員 。 這說明 靜態(tài)數據成員并不是屬于對象的,而是屬于 類的,但類的對象可以引用它 。 類內可隨便訪問。 如果靜態(tài)數據成員被定義為私有的,則不能在類外 直接引用,而必須通過公用的成員函數引用 。 (5) 靜態(tài)數據成員的作用 :各對象之間的數據有了 溝通的渠道,實現數據共享,因此可以不使用全局 變量。 全局變量破壞了封裝的原則,不符合面向對 象程序的要求 。 注意公用靜態(tài)數據成員與全局變量的不同, 靜態(tài)數 據成員的作用域只限于定義該類的作用域

56、內 在此作 用域內,可以通過類名和域運算符 “ ” 引用靜態(tài) 數據成員,而不論類對象是否存在。 成員函數也可以定義為靜態(tài)的,在類中聲明函數的 前面加 static就成了靜態(tài)成員函數。如 static int volume( ); 和靜態(tài)數據成員一樣, 如果要在類外調用公用的靜 態(tài)成員函數,要用類名和域運算符“ ”。 如 Box volume( ); 實際上也允許通過對象名調用靜態(tài)成員函數,如 a.volume( ); 但這并不意味著此函數是屬于對象 a的, 而只是用 a 的類型而已 。 9.9.2 靜態(tài)成員函數 靜態(tài)成員函數的作用: 不是為了對象之間的溝通, 而是為了能處理靜態(tài)數據成員 。 前

57、面曾指出: 當調用一個對象的成員函數 (非靜態(tài) 成員函數 )時,系統(tǒng)會把該對象的起始地址賦給成 員函數的 this指針。而靜態(tài)成員函數并不屬于某一 對象, 它與任何對象都無關,因此靜態(tài)成員函數沒 有 this指針 。 既然它沒有指向某一對象,就無法對 一個對象中的非靜態(tài)成員進行默認訪問 。 靜態(tài)成員函數與非靜態(tài)成員函數的 根本區(qū)別 : 非靜態(tài)成員函數有 this指針,而靜態(tài)成員函數沒有 this指針。由此,靜態(tài)成員函數不能訪問本類中的 非靜態(tài)成員 。 靜態(tài)成員函數可以直接引用本類中的靜態(tài)數據成員, 靜態(tài)成員函數主要用來訪問靜態(tài)數據成員,而不訪 問非靜態(tài)成員。 假如在一個靜態(tài)成員函數中有以下 語

58、句: coutheightendl; /若 height已聲明為 static, 則引用本類中的靜態(tài)成員,合法 coutwidthendl; /若 width是非靜態(tài)數據成員,不合法 如果一定要引用本類的非靜態(tài)成員,應該加對象名 和成員運算符 “ .” 。如 couta.widthendl; /引用本類對象 a中的非靜態(tài)成員 假設 a已定義為 Box類對象,且在當前作用域內有效, 則此語句合法。 例 9.11 靜態(tài)成員函數的應用。 #include using namespace std; class Student /定義 Student類 public: Student(int n,int

59、 a,float s):num(n),age(a),score(s) /定義構造函數 void total( ); static float average( ); /聲明靜態(tài)成員函數 private: int num; int age; float score; static float sum; /靜態(tài)數據成員 static int count; /靜態(tài)數據成員 ; void Student total( ) /定義非靜態(tài)成員函數 sum+=score; /累加總分, 類內訪問靜態(tài)數據成員 count+; /累計已統(tǒng)計的人數, 類內訪問靜態(tài)數據成員 float Student averag

60、e( ) /定義靜態(tài)成員函數 return(sum/count); /只能訪問靜態(tài)數據成員 float Student sum=0; /對靜態(tài)數據成員初始化 int Student count=0; /對靜態(tài)數據成員初始化 int main( ) Student stud3= /定義對象數組并初始化 Student(1001,18,70), Student(1002,19,78), Student(1005,20,98) ; int n; coutn; /輸入需要求前面多少名學生的平均成績 for(int i=0;in;i+) /調用 3次 total函數 studi.total( ); co

61、utthe average score of n students is Student average( )endl; /調用靜態(tài)成員函數 return 0; 運行結果為 please input the number of students:3 the average score of 3 students is 82.3333 說明: (1)在 Student類中定義了兩個靜態(tài)數據成員 sum(總 分 )和 count(累計需要統(tǒng)計的學生人數 ), 這是由于 這兩個數據成員的值是需要進行累加的, 它們并不 是只屬于某一個對象元素,而是由各對象元素共享 的,可以看出: 它們的值是在不斷變化

62、的,而且 無論對哪個對象元素而言,都是相同的,而且始終 不釋放內存空間。 (2)注意: total是公有的成員函數,公有的成員函 數可以引用本對象中的一般數據成員 (非靜態(tài)數據 成員 ),也可以引用類中的靜態(tài)數據成員。 score是 非靜態(tài)數據成員, sum和 count是靜態(tài)數據成員。 (3) average是靜態(tài)成員函數,可以 直接 引用私有的 靜態(tài)數據成員 (不必加類名或對象名 ),函數返回成 績的平均值。 (4) 在 main函數中,引用 total函數要加對象名 (今用 對象數組元素名 ),引用靜態(tài)成員函數 average函數 要用 類名或對象名 。 類的基本訪問規(guī)則: 在一個類中可

63、以有公用的 (public)成員和私有的 (private)成員。在類外可以訪 問公用成員,只有本類中的函數可以訪問本類的私 有成員。 現在,我們來補充介紹一個例外 友元 (friend)。 友元可以訪問與其有好友關系的類中的私有成員。 友元包括友元函數和友元類。 9.10 友元 如果 在本類以外的其他地方定義了一個函數 (這個 函數可以是不屬于任何類的非成員函數,也可以是 其他類的成員函數 )。 在類體中用 friend對其進行聲明,此函數就稱為本 類的友元函數。友元函數可以訪問這個類中的私有 成員,但是 外部訪問 。 9.10.1 友元函數 1. 將普通函數聲明為友元函數 例 9.12 友

64、元函數的簡單例子。 #include using namespace std; class Time public: Time(int,int,int); friend void display(Time /聲明 display函數為 Time類的友元函數 private: /以下數據是私有數據成員 int hour; int minute; int sec; ; Time Time(int h,int m,int s) /構造函數,給 hour,minute,sec賦初值 hour=h; minute=m; sec=s; void display(Time int main( ) Time

65、t1(10,13,56); display(t1); return 0; /調用 display函數,實參 t1是 Time類對象 程序輸出結果如下: 10:13:56 由于聲明了 display是 Time類的 friend函數,所以 display函數可以引用 Time中的私有成員 hour,minute,sec。 但注意在引用這些私有數據成員時,必須加上對象 名,不能寫成 couthour:minute:secendl; 與類的成員函數的區(qū)別! 只能以外部方式訪問私有 成員。 2. 友元成員函數 friend函數不僅可以是一般函數 (非成員函數 ),而 且 可以是另一個類中的成員函數 。

66、 例 9.13 友元成員函數的簡單應用。 #include using namespace std; class Date; /對 Date類的提前引用聲明,?因在 Time類中用到 class Time /定義 Time類 public: Time(int,int,int); void display(Date /display是成員函數,形參是 Date類對象的引用 private: int hour; int minute; int sec; ; class Date /聲明 Date類 public: Date(int,int,int); friend void Time display(Date /聲明 Time中的 display函數 為本類的友元成 員函數 private: int month; int day; int year; ; Time Time(int h,int m,int s) /類 Time的構造函數 hour=h; minute=m; sec=s; void Time display(Date /引用 Date類對象 中的私有數據 couthour:m

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

相關資源

更多
正為您匹配相似的精品文檔
關于我們 - 網站聲明 - 網站地圖 - 資源地圖 - 友情鏈接 - 網站客服 - 聯系我們

copyright@ 2023-2025  zhuangpeitu.com 裝配圖網版權所有   聯系電話:18123376007

備案號:ICP2024067431-1 川公網安備51140202000466號


本站為文檔C2C交易模式,即用戶上傳的文檔直接被用戶下載,本站只是中間服務平臺,本站所有文檔下載所得的收益歸上傳人(含作者)所有。裝配圖網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對上載內容本身不做任何修改或編輯。若文檔所含內容侵犯了您的版權或隱私,請立即通知裝配圖網,我們立即給予刪除!

五月丁香婷婷狠狠色,亚洲日韩欧美精品久久久不卡,欧美日韩国产黄片三级,手机在线观看成人国产亚洲