第6章 類和對象
6.1 類的定義
1類的定義
類的定義可以分為兩部分:說明部分和實現(xiàn)部分。說明部分說明類中包含的數(shù)據(jù)成員和成員函數(shù),實現(xiàn)部分是對成員函數(shù)的定義。類定義的一般格式如下:
//類的說明部分
class<類名>
{
public:
<成員函數(shù)或數(shù)據(jù)成員的說明> //公有成員,外部接口
protected:
<數(shù)據(jù)成員或成員函數(shù)的說明> //保護成員
private:
<數(shù)據(jù)成員或成員函數(shù)的說明> //私有成員
。;
//類的實現(xiàn)部分
<各個成員函數(shù)的實現(xiàn)>
其中,class是聲明類的關(guān)鍵字;<類名>是要聲明的類的名字,必須符合標識符定義規(guī)則;花括號表示類的聲明范圍,說明該類的成員,其后的分號表示類聲明結(jié)束。類的成員包括數(shù)據(jù)成員和成員函數(shù),分別描述類所表達的問題的屬性和行為。關(guān)鍵字public、private和protected稱為訪問權(quán)限修飾符,它們限制了類成員的訪問控制范圍。
各個成員函數(shù)的實現(xiàn)即可以在類體內(nèi)定義,也可以在類體外定義。如果一個成員函數(shù)在類體內(nèi)進行了定義,它將不出現(xiàn)在類的實現(xiàn)部分;如果所有的成員函數(shù)都在類體內(nèi)進行了定義,則可以省略類的實現(xiàn)部分。在類體內(nèi)定義的成員函數(shù)都是內(nèi)聯(lián)函數(shù)。
2類成員的訪問控制
類中提供了3種訪問控制權(quán)限:公有(public)、私有(private)和保護(protected)。其中,公有類型定義了類的外部接口,任何一個外部的訪問都必須通過外部接口進行;私有類型的成員只允許本類的成員函數(shù)訪問,來自類外部的任何訪問都是非法的;保護類型介于公有類型和私有類型之間,在繼承和派生時可以體現(xiàn)出其特點。
3類的數(shù)據(jù)成員
類中的數(shù)據(jù)成員描述類所表達的問題的屬性。數(shù)據(jù)成員在類體中進行定義,其定義方式與一般變量相同,但對數(shù)據(jù)成員的訪問要受到訪問權(quán)限修飾符的控制。
在定義類的數(shù)據(jù)成員時,要注意以下幾個問題。
(1)類中的數(shù)據(jù)成員可以是任意類型,包括整型、浮點型、字符型、數(shù)組、指針和引用等,也可以是對象。但是要注意,只有另外一個類的對象,才可以作為該類的成員,即作為該類的成員對象而存在。自身類的對象是不可以作為自身類的成員存在的,但自身類的指針可以。
(2)在類體中不允許對所定義的數(shù)據(jù)成員進行初始化。
4類的成員函數(shù)
類的成員函數(shù)描述類所表達的問題的行為。類中所有的成員函數(shù)都必須在類體內(nèi)進行說明。但成員函數(shù)的定義既可以在類體內(nèi)給出,也可以在類體外給出。
第一種方式是將成員函數(shù)直接定義在類的內(nèi)部。
第二種方式是在類聲明中給出對成員函數(shù)的說明,而在類外部對成員函數(shù)進行定義(但成員函數(shù)仍然在類范圍內(nèi))。這種在類外部定義的成員函數(shù)的一般格式是:
<返回類型><類名>::<成員函數(shù)名>(<參數(shù)表>)
{
<函數(shù)體>
。
在類體外定義成員函數(shù)時,要注意必須在成員函數(shù)名前加上類名和作用域運算符(::)。作用域運算符用來標識某個成員屬于某個類。作用域運算符的使用格式如下:
<類名>::<成員函數(shù)名>(<參數(shù)表>)
或
<類名>::<數(shù)據(jù)成員名>
成員函數(shù)的兩種定義方式之間是有差別的。如果一個成員函數(shù)的聲明和定義都在類體內(nèi),那么這個成員函數(shù)就是內(nèi)聯(lián)函數(shù)。如果一個成員函數(shù)的聲明在類體內(nèi),而定義在類體外,這時對該成員函數(shù)的調(diào)用是按一般函數(shù)進行的。如果要將定義在類體外的成員函數(shù)也作為內(nèi)聯(lián)函數(shù)處理,就必須在成員函數(shù)的定義前加上關(guān)鍵字“inline”,以此顯式地說明該成員函數(shù)也是一個內(nèi)聯(lián)函數(shù)。
成員函數(shù)除了可以定義為內(nèi)聯(lián)函數(shù)以外,也可以進行重載,可以對其參數(shù)設(shè)置默認值。
6.2 對象的定義
1對象的定義
對象是類的實例,一個對象必須屬于一個已知的類。因此在定義對象之前,必須先定義該對象所屬的類。
對象的定義格式如下:
<類名><對象名>(<參數(shù)表>);
其中,<類名>是待定義的對象所屬的類的名字。<對象名>中可以有一個或多個對象名,多個對象名之間用逗號分隔。<對象名>中,可以是一般的對象名,也可以是指向?qū)ο蟮闹羔樏蛞妹,還可以是對象數(shù)組名。<參數(shù)表>是初始化對象時需要的,建立對象時可以根據(jù)給定的參數(shù)調(diào)用相應的構(gòu)造函數(shù)對對象進行初始化。無參數(shù)時表示調(diào)用類的缺省構(gòu)造函數(shù)。
2對象的成員
一個對象的成員就是該對象的類所定義的成員,包括數(shù)據(jù)成員和成員函數(shù)。定義了對象后,可以使用“ .”運算符和“->”運算符訪問對象的成員。其中,“ .”運算符適用于一般對象和引用對象,而“->”運算符適用于指針對象(即指向?qū)ο蟮闹羔?。訪問對象成員的一般格式如下:
<對象名> .<數(shù)據(jù)成員名>或<對象名>-><數(shù)據(jù)成員名>
<對象名> .<成員函數(shù)名>(<參數(shù)表>)或<對象名>-><成員函數(shù)名>(<參數(shù)表>)
6.3 構(gòu)造函數(shù)和析構(gòu)函數(shù)
1構(gòu)造函數(shù)和析構(gòu)函數(shù)的定義。構(gòu)造函數(shù)的作用是在對象被創(chuàng)建時利用特定的值構(gòu)造對象,將對象初始化為一種特定的狀態(tài),使該對象具有區(qū)別于其他對象的特征。構(gòu)造函數(shù)在對象被創(chuàng)建的時候由系統(tǒng)自動調(diào)用。
構(gòu)造函數(shù)也是類的成員函數(shù),但它是一種特殊的成員函數(shù),它除了具有一般成員函數(shù)的特性之外,還具有一些特殊的性質(zhì):
(1)構(gòu)造函數(shù)的名字必須與類名相同;
(2)構(gòu)造函數(shù)不指定返回類型,它隱含有返回值,由系統(tǒng)內(nèi)部使用;
(3)構(gòu)造函數(shù)可以有一個或多個參數(shù),因此構(gòu)造函數(shù)可以重載;
(4)在創(chuàng)建對象時,系統(tǒng)會自動調(diào)用構(gòu)造函數(shù)。
2缺省構(gòu)造函數(shù)和缺省析構(gòu)函數(shù)
缺省構(gòu)造函數(shù)就是調(diào)用時不必提供參數(shù)的構(gòu)造函數(shù)。缺省的構(gòu)造函數(shù)的函數(shù)名與類名相同,它的參數(shù)表或者為空,或者它的所有參數(shù)都具有默認值。前面日期類Date的定義中,構(gòu)造函數(shù)Date(int y=2000);就是缺省構(gòu)造函數(shù)。
如果類中定義了一個缺省構(gòu)造函數(shù),則使用該函數(shù);如果一個類中沒有定義任何構(gòu)造函數(shù),編譯器將生成一個不帶參數(shù)的公有缺省構(gòu)造函數(shù),它的定義格式如下:
<類名>::<類名>()
{
}
每個類都必須有一個析構(gòu)函數(shù)。如果一個類沒有聲明析構(gòu)函數(shù),編譯器將生成一個公有的析構(gòu)函數(shù),即缺省析構(gòu)函數(shù),它的定義格式如下:
<類名>::~<類名>()
{
。
3拷貝構(gòu)造函數(shù)
類中有一種特殊的構(gòu)造函數(shù)叫做拷貝構(gòu)造函數(shù),它用一個已知的對象初始化一個正在創(chuàng)建的同類對象?截悩(gòu)造函數(shù)的一般格式如下:
<類名>::<類名>(const<類名>&<引用對象名>)
{
//拷貝構(gòu)造函數(shù)體
。
拷貝構(gòu)造函數(shù)具有以下特點:
(1)也是一種構(gòu)造函數(shù),因此函數(shù)名與類名相同,并且不能指定函數(shù)返順類型。
(2)只有一個參數(shù),是對同類的某個對象的引用。
(3)每一個類中都必須有一個拷貝構(gòu)造函數(shù)。如果類中沒有聲明拷貝構(gòu)造函數(shù),編譯器會自動生成一個具有上述形式的公有的拷貝構(gòu)造函數(shù)。
6.4 對象的生存期
1全局對象、靜態(tài)對象與局部對象
對象的生存期是指對象從被創(chuàng)建開始到被釋放為止的時間。對象按生存期可分為3類:
(1)局部對象:當程序執(zhí)行到局部對象的定義之處時,調(diào)用構(gòu)造函數(shù)創(chuàng)建該對象;當程序退出定義該對象所在的函數(shù)體或程序塊時,調(diào)用析構(gòu)函數(shù)釋放該對象。
(2)靜態(tài)對象:當程序第一次執(zhí)行到靜態(tài)對象的定義之處時,調(diào)用構(gòu)造函數(shù)創(chuàng)建該對象;當程序結(jié)束時調(diào)用析構(gòu)函數(shù)釋放該對象。
(3)全局對象:當程序開始執(zhí)行時,調(diào)用構(gòu)造函數(shù)創(chuàng)建該對象;當程序結(jié)束時調(diào)用析構(gòu)函數(shù)釋放該對象。
2自由存儲對象
動態(tài)內(nèi)存分配技術(shù)可以保證在程序運行過程中按照實際需要申請適量的內(nèi)存,使用結(jié)束后進行釋放。這種在程序運行過程中根據(jù)需要可以隨時建立或刪除的對象稱為自由存儲對象。建立和刪除工作分別由堆運算符new和delete完成。
6.5 this 指針
C+ +提供了一個特殊的對象指針——this指針,它是成員函數(shù)所屬對象的指針,它指向類對象的地址。成員函數(shù)通過這個指針可以知道自己屬于哪一個對象。
this指針是一個隱含的指針,它隱含于每個類的非靜態(tài)成員函數(shù)中,它明確地表示出了成員函數(shù)當前操作的數(shù)據(jù)所屬的對象。當對一個對象調(diào)用成員函數(shù)時,編譯程序先將對象的地址賦值給this指針,然后調(diào)用成員函數(shù),每次成員函數(shù)存取數(shù)據(jù)成員時,則隱含使用this指針。
6.6 靜態(tài)成員
對于類中的非靜態(tài)數(shù)據(jù)成員,每一個類對象都擁有一個拷貝(副本),即每個對象的同名數(shù)據(jù)成員可以分別存儲不同的數(shù)值,這是保證每個對象擁有區(qū)別于其他對象的特征的需要。而類中的靜態(tài)成員則是解決同一個類的不同對象之間的數(shù)據(jù)和函數(shù)共享問題的。靜態(tài)成員的特性是不管這個類創(chuàng)建了多少個對象,它的靜態(tài)成員都只有一個拷貝(副本),這個副本被所有屬于這個類的對象共享。這種共享與全局變量或全局函數(shù)相比,既沒有破壞數(shù)據(jù)隱藏的原則,又保證了安全性。
靜態(tài)成員表示整個類范圍的信息,其聲明以static關(guān)鍵字開始,包括靜態(tài)數(shù)據(jù)成員和靜態(tài)成員函數(shù)。
1靜態(tài)數(shù)據(jù)成員
靜態(tài)數(shù)據(jù)成員聲明時要使用關(guān)鍵字static。
靜態(tài)數(shù)據(jù)成員在每個類對象中并不占有存儲空間,它只是在每個類中分配有存儲空間,供所有對象公用。靜態(tài)數(shù)據(jù)成員的值對每個對象都是一樣的,但它的值可以被任何一個對象更新,從而實現(xiàn)了同一類的不同對象之間的數(shù)據(jù)共享。
靜態(tài)數(shù)據(jù)成員具有靜態(tài)生存期,必須對它進行初始化。靜態(tài)數(shù)據(jù)成員初始化的一般格式如下:
<數(shù)據(jù)類型><類名>::<靜態(tài)數(shù)據(jù)成員名>=<初始值>;
在對靜態(tài)數(shù)據(jù)成員初始化時應注意:
(1)由于在類的聲明中僅僅是對靜態(tài)數(shù)據(jù)成員進行了引用性聲明,因此必須在文件作用域的某個地方對靜態(tài)數(shù)據(jù)成員進行定義并初始化,即應在類體外對靜態(tài)數(shù)據(jù)成員進行初始化(靜態(tài)數(shù)據(jù)成員的初始化與它的訪問控制權(quán)限無關(guān))。
(2)靜態(tài)數(shù)據(jù)成員初始化時前面不加static關(guān)鍵字,以免與一般靜態(tài)變量或?qū)ο蠡煜?/P>
(3)由于靜態(tài)數(shù)據(jù)成員是類的成員,因此在初始化時必須使用作用域運算符(::)限定它所屬的類。
2靜態(tài)成員函數(shù)
公有的靜態(tài)數(shù)據(jù)成員可以直接訪問,但私有的或保護的靜態(tài)數(shù)據(jù)成員卻必須通過公有的接口進行訪問,一般將這個公有的接口定義為靜態(tài)成員函數(shù)。
使用static關(guān)鍵字聲明的成員函數(shù)就是靜態(tài)成員函數(shù),靜態(tài)成員函數(shù)也屬于整個類而不屬于類中的某個對象,它是該類的所有對象共享的成員函數(shù)。
靜態(tài)成員函數(shù)可以在類體內(nèi)定義,也可以在類外定義。當在類外定義時,要注意不能使用static關(guān)鍵字作為前綴。
由于靜態(tài)成員函數(shù)在類中只有一個拷貝(副本),因此它訪問對象的成員時要受到一些限制:靜態(tài)成員函數(shù)可以直接訪問類中說明的靜態(tài)成員,但不能直接訪問類中說明的非靜態(tài)成員;若要訪問非靜態(tài)成員時,必須通過參數(shù)傳遞的方式得到相應的對象,再通過對象來訪問。
6.7 常成員
雖然數(shù)據(jù)隱藏保證了數(shù)據(jù)的安全性,但各種形式的數(shù)據(jù)共享卻又不同程度地破壞了數(shù)據(jù)的安全性。因此,對于既需要共享又需要防止改變的數(shù)據(jù)應該定義為常量進行保護,以保證它在整個程序運行期間是不可改變的。這些常量需要使用const修飾符進行定義。const關(guān)鍵字不僅可以修飾類對象本身,也可以修飾類對象的成員函數(shù)和數(shù)據(jù)成員,分別稱為常對象、常成員函數(shù)和常數(shù)據(jù)成員。
1常對象
使用const關(guān)鍵字修飾的對象稱為常對象,它的定義格式如下:
<類名>const<對象名>
或
const<類名><對象名>
常對象在定義時必須進行初始化,而且不能被更新。
2常成員函數(shù)
使用const關(guān)鍵字說明的成員函數(shù)稱為常成員函數(shù),常成員函數(shù)的說明格式如下:
<返回類型><成員函數(shù)名>(<參數(shù)表>)const;
3常數(shù)據(jù)成員
使用const說明的數(shù)據(jù)成員稱為常數(shù)據(jù)成員。常數(shù)據(jù)成員的定義與一般常量的定義方式相同,只是它的定義必須出現(xiàn)在類體中。
常數(shù)據(jù)成員同樣也必須進行初始化,并且不能被更新。但常數(shù)據(jù)成員的初始化只能通過構(gòu)造函數(shù)的成員初始化列表進行。
常數(shù)據(jù)成員的初始化只能在成員初始化列表中進行,但對于大多數(shù)數(shù)據(jù)成員而言,既可以使用成員初始化列表的方式,也可以使用賦值,即在構(gòu)造函數(shù)體中使用賦值語句將表達式的值賦值給數(shù)據(jù)成員。這兩種方式中,成員初始化列表方式使初始化情況更加明顯,并且可能帶來效率上的優(yōu)勢。
6.8 友元
類具有數(shù)據(jù)封裝和隱藏的特性,只有類的成員函數(shù)才能訪問類的私有成員,外部函數(shù)只能訪問類的公有成員。但在某些情況下,需要在類的外部訪問類的私有成員。這時,如果通過公有成員函數(shù)進行訪問,由于參數(shù)傳遞、類型檢查和安全性檢查等需要時間上的開銷,將影響程序的運行效率。為了解決整個問題,引入了友元。友元可以在類外部直接訪問類的私有成員,提高了程序的運行效率。
友元提供了不同類或?qū)ο蟮某蓡T函數(shù)之間、類的成員函數(shù)與一般函數(shù)之間進行數(shù)據(jù)共享的機制。對于一個類,可以利用friend關(guān)鍵字將一般函數(shù)、其他類的成員函數(shù)或者是其他類聲明為該類的友元,使得這個類中本來隱藏的信息(包括私有成員和保護成員)可以被友元所訪問。如果友元是一般成員函數(shù)或是類的成員函數(shù),稱為友元函數(shù);如果友元是一個類,則稱為友元類,友元類的所有成員函數(shù)都成為友元函數(shù)。
1友元函數(shù)
友元函數(shù)不是當前類的成員函數(shù),而是獨立于當前類的外部函數(shù)(包括普通函數(shù)和其他類的成員函數(shù)),但它可以訪問該類的所有對象的成員,包括私有成員、保護成員和公有成員。
友元函數(shù)要在類定義時聲明,聲明時要在其函數(shù)名前加上關(guān)鍵字friend。該聲明可以放在公有部分,也可以放在私有部分。友元函數(shù)的定主既可以在類內(nèi)部進行,也可以在類外部進行。
2友元類
友元除了可以是函數(shù)外,還可以是類,即一個類可以作為另一個類的友元,稱為友元類。友元類的所有成員函數(shù)都是另一個類的友元函數(shù),都可以訪問另一個類中的隱藏信息(包括私有成員和保護成員)。
友元類可以在另一個類的公有部分或私有部分進行說明,說明方法如下:
friend<類名>;//友元類類名
6.9 對象數(shù)組
對象數(shù)組是指數(shù)組元素為對象的數(shù)組,該數(shù)組中的每一個元素都是同一個類的對象。
對象數(shù)組的定義格式如下:
<類名><數(shù)組名>[<大小>]……
使用對象數(shù)組成員的一般格式是:
<數(shù)組名>[<下標>].<成員名>
6.10 成員對象
類的數(shù)據(jù)成員可以是簡單類型或自定義類型,也可以是類類型的對象。因此,可以利用已定義的類來構(gòu)成新的類,使得一些復雜的類可以由一些簡單類組合而成。類的聚集,描述的就是一個類內(nèi)嵌其他類的對象作為成員的情況。
當一個類的成員是另外一個類的對象時,該對象就稱為成員對象。當類中出現(xiàn)了成員對象時,該類的構(gòu)造函數(shù)要包含對成員對象的初始化,通常采用成員初始化列表的方法來初始化成員對象。定義的一般格式如下:
<類名>::<類名>(<總形參表>):<成員對象1>(<形參表1>),<成員對象2>(<形參表2<),…
{
//類成員的初始化
。
建立一個類的對象時,要調(diào)用它的構(gòu)造函數(shù)。如果這個類有成員對象,要首先執(zhí)行所有的成員對象的構(gòu)造函數(shù),當全部成員對象的初始化都完成之后,再執(zhí)行當前類的構(gòu)造函數(shù)體。析構(gòu)函數(shù)的執(zhí)行順序與構(gòu)造函數(shù)的執(zhí)行順序相反。
當類中有多個成員對象時,要按照定義成員對象的順序建立各個子對象,即成員對象構(gòu)造函數(shù)的執(zhí)行順序僅與成員對象在類中聲明的順序有關(guān),而與成員初始化列表中給出的成員對象的順序無關(guān)。
如果在構(gòu)造函數(shù)的成員初始化列表中沒有給出對成員對象的初始化,則表示使用成員對象的缺省構(gòu)造函數(shù)。如果成員對象所在的類沒有缺省構(gòu)造函數(shù),將產(chǎn)生錯誤。如果所有的成員對象都是調(diào)用缺省構(gòu)造函數(shù)建立的,那么該類的構(gòu)造函數(shù)的成員初始化列表可以省略。
北京 | 天津 | 上海 | 江蘇 | 山東 |
安徽 | 浙江 | 江西 | 福建 | 深圳 |
廣東 | 河北 | 湖南 | 廣西 | 河南 |
海南 | 湖北 | 四川 | 重慶 | 云南 |
貴州 | 西藏 | 新疆 | 陜西 | 山西 |
寧夏 | 甘肅 | 青海 | 遼寧 | 吉林 |
黑龍江 | 內(nèi)蒙古 |