一、const關(guān)鍵字
如果把const放在變量類型名前,說(shuō)明這個(gè)變量的值是保持不變的,該變量必須在定義時(shí)初始化,初始化后對(duì)它進(jìn)行的任何賦值都是非法的。
當(dāng)指針或者引用指向一個(gè)常量時(shí),必須在類型名前使用const標(biāo)識(shí)這個(gè)指針或者引用指向的“變量”為常量,沒有的話就是語(yǔ)法錯(cuò)誤。如: const int x = 5; const int* px = &x; const int& rx = x;這樣一來(lái),直接修改x是不可能的,通過(guò)*px或者rx修改x也是不可能的。當(dāng)然,這個(gè)指針還能指向其他的地方,因?yàn)橹羔槺旧聿]有被標(biāo)識(shí)為const的。比如,px = &y;
假如變量是一個(gè)非常量變量,而指針或者引用的類型名前使用了const,那么,可以直接修改變量,不能通過(guò)指針或者引用修改變量。
如果想讓一個(gè)指針本身成為const的,就要在*后加const,即int* const p = &x;這個(gè)時(shí)候,p就不能再指向其他變量。假如x是非常量的,那它可以通過(guò)指針進(jìn)行修改,因?yàn)?SPAN>x并沒有標(biāo)識(shí)為const。當(dāng)然,引用天生就是const的,它必須被初始化,而且初始化后就不能再指向新的變量。比如,int x = 5; int& r = x; r = y;第二句代碼不會(huì)讓r指向y,而是讓x的值變成了y。
如果在函數(shù)接口(參數(shù))中使用const,和在值、指針中使用是類似的。但是,這就更難讓函數(shù)返回指向這個(gè)參數(shù)對(duì)象的指針或者引用了。如果允許的話,客戶代碼就有可能通過(guò)別名修改常量。比如,
class Point
{
int x, y;
public:
Point closestPointVal(const Point& pt)
{ if( x*x+y*y < pt.x*pt.x+pt.y*pt.y)
return *this;
else return pt;}
Point* closestPointPtr(const Point& pt)
{ return (x*x+y*y < pt.x*pt.x+pt.y*pt.y) ? this : &pt;}
Point& closestPointRef(const Point& pt)
{ return (x*x+y*y < pt.x*pt.x+pt.y*pt.y) ? *this : pt;}
};
第一個(gè)函數(shù)是返回值的,不管用不用const,都不會(huì)修改實(shí)參pt。而第二個(gè)函數(shù)可以返回指向pt實(shí)參的指針??蛻舸a就可以使用這個(gè)指針修改實(shí)際參數(shù)對(duì)象的狀態(tài)。同樣,第三個(gè)函數(shù)返回的引用也能修改實(shí)參對(duì)象的狀態(tài)。假如這樣的函數(shù)是合法的,那么就有可能誤用,誤用后如果出了錯(cuò),很難弄清是這里出的錯(cuò),所以編譯程序直接就認(rèn)為這樣的函數(shù)不合法。也就是,參數(shù)對(duì)象使用了const,而返回類型沒有,這是錯(cuò)誤的。
C++提供了三種方法解決這個(gè)問題:1.參數(shù)不使用const;2.在成員函數(shù)內(nèi)部,使用const_cast運(yùn)算符移去const屬性;3.在另外合適的地方添加const。一個(gè)真正的程序員是不會(huì)放過(guò)任何一個(gè)機(jī)會(huì),將設(shè)計(jì)人員的設(shè)計(jì)意圖傳達(dá)給客戶代碼程序員和維護(hù)人員的。所以,假如某個(gè)參數(shù)沒有被修改,就要用const來(lái)明確說(shuō)明。而使用const_cast也很糟糕,不易理解。所以只能使用第三種方法,在函數(shù)返回值前加上const,接收該返回值的指針或者引用也要是const的。這樣的話,返回值只能作為右值,不能作為左值。這樣的返回值不能調(diào)用它所在類的成員函數(shù)。
C++提供const關(guān)鍵字,不是為了保證一個(gè)變量不被修改,而是為了方便編譯程序和維護(hù)人員弄清楚一個(gè)實(shí)體在程序中是否被修改了。如果函數(shù)接口中聲明參數(shù)為const,我們就認(rèn)為這個(gè)參數(shù)不會(huì)改變,如果沒有聲明為const,就認(rèn)為這個(gè)參數(shù)一定被改變了,而不管函數(shù)到底有沒有改變這個(gè)參數(shù)。
其實(shí),const還有一個(gè)含義,就是在函數(shù)的參數(shù)列表的“)”之后,函數(shù)體的“{”之前。這種用法說(shuō)明函數(shù)不會(huì)修改目標(biāo)對(duì)象的值。
二、靜態(tài)類成員
每建立一個(gè)類對(duì)象實(shí)例,都會(huì)為該實(shí)例創(chuàng)建一個(gè)單獨(dú)的數(shù)據(jù)成員集合。不管是將對(duì)象定義為有名的局部變量,有名的全局變量,或者使用new定義為未命名的動(dòng)態(tài)變量,或者按值傳遞一個(gè)對(duì)象作為函數(shù)參數(shù),或者從一個(gè)函數(shù)按值返回一個(gè)對(duì)象。而對(duì)于成員函數(shù),它們的目標(biāo)代碼卻只產(chǎn)生一次。因?yàn)槊總€(gè)程序函數(shù)都有一個(gè)隱含的參數(shù),即this指針。
1 用全局變量作為類特性
類為對(duì)象實(shí)例提供了一個(gè)藍(lán)圖或者說(shuō)是模板,類的成員變量都是這個(gè)類的所有實(shí)例的共性,類的每一個(gè)實(shí)例都有一個(gè)單獨(dú)的副本。但是有時(shí)我們需要為一個(gè)類的所有實(shí)例提供共有的數(shù)據(jù)成員副本,這比在每個(gè)類對(duì)象中維護(hù)單獨(dú)的副本能更有效的合理利用內(nèi)存。最常見的例子就是對(duì)類的實(shí)例進(jìn)行計(jì)數(shù)。假如這個(gè)計(jì)數(shù)變量count被定義在類中,在構(gòu)造函數(shù)中對(duì)其加1,在析構(gòu)函數(shù)中對(duì)其減1,似乎能實(shí)現(xiàn)計(jì)數(shù)作用。但是實(shí)際上卻是做不到的。因?yàn)?SPAN>count是類的成員變量,每個(gè)實(shí)例都有一個(gè)count,構(gòu)造函數(shù)對(duì)其加1,只是對(duì)自己的count加了1。
使用全局變量就能解決這個(gè)問題。但是將它用在大型程序中,存在一些問題,這些問題在前面都講過(guò)了。首先在客戶代碼任意地方都可能使用count,增加了模塊之間的依賴性。其次,全局變量名增加了重名機(jī)會(huì)。最重要的是,count作為全局變量出現(xiàn)時(shí),維護(hù)人員不能很好的了解它是做什么的,除非看注釋或者查閱大量其他代碼。
2 static的含義
static和const一樣很復(fù)雜。
首先,將static用于一個(gè)全局變量,是說(shuō)明該全局變量只對(duì)定義在同一個(gè)文件中的函數(shù)可見。即使在另一個(gè)文件中使用了extern,另一個(gè)文件中的函數(shù)也不能訪問這個(gè)全局變量。
static的第二個(gè)含義是用于一個(gè)函數(shù)前,說(shuō)明該函數(shù)只能在同一個(gè)文件中調(diào)用。
第三個(gè)含義是static用于函數(shù)的局部變量。它表明該變量的值不會(huì)因?yàn)楹瘮?shù)終止而消失,它會(huì)被保存下來(lái)。再次調(diào)用該函數(shù)時(shí),這個(gè)保存下來(lái)的值會(huì)用來(lái)初始化該變量。
第四個(gè)含義就是用于類的成員變量。它表明對(duì)類的所有對(duì)象,這個(gè)數(shù)據(jù)成員都只有一個(gè)實(shí)例。這個(gè)實(shí)例被所有對(duì)象共有。static的成員變量可以是private、public、protected的。定義和訪問的語(yǔ)法也和其他數(shù)據(jù)成員一樣。
3 靜態(tài)數(shù)據(jù)成員的初始化
靜態(tài)數(shù)據(jù)成員在類規(guī)格說(shuō)明外部被初始化,這一點(diǎn)和全局變量相似。但是全局變量可以被隱式地初始化為0,而靜態(tài)成員變量作為類的數(shù)據(jù)成員,必須被顯式地初始化,因?yàn)轭惖乃械臄?shù)據(jù)成員都必須被顯式初始化。賦值和初始化存在著重要的區(qū)別,如果類型名后緊跟著變量名,就是初始化,如果變量名前沒有類型名,就是賦值。初始化對(duì)于公共的和非公共的靜態(tài)數(shù)據(jù)成員是合法的,但是對(duì)非公共的靜態(tài)數(shù)據(jù)成員進(jìn)行賦值卻是不合法的。比如,如果count是private的,那么Point::count = 0就是錯(cuò)誤的。
靜態(tài)數(shù)據(jù)成員只能被初始化一次,所以它應(yīng)該和其他的成員函數(shù)的定義一起放在.cpp文件中,而不能放在頭文件中。一個(gè)靜態(tài)數(shù)據(jù)成員不能是聯(lián)合的成員,也不能是位域的類。聯(lián)合和位域都表示屬于某個(gè)特定對(duì)象的特殊內(nèi)存用途,而靜態(tài)數(shù)據(jù)成員卻不屬于特定對(duì)象,它屬于整個(gè)類。
4 靜態(tài)成員函數(shù)
static的第五種含義是用在類的成員函數(shù)前,表明這個(gè)函數(shù)不訪問非靜態(tài)數(shù)據(jù)成員,它只能訪問它的參數(shù)、類的靜態(tài)數(shù)據(jù)成員、全局變量。注意到靜態(tài)成員函數(shù)訪問的三種類型的數(shù)據(jù),都不是描述對(duì)象狀態(tài)的。而在函數(shù)參數(shù)列表后面使用const表明該函數(shù)不會(huì)修改該函數(shù)的目標(biāo)對(duì)象的數(shù)據(jù)成員。所以,一個(gè)靜態(tài)成員函數(shù)沒有必要在參數(shù)列表之后添加一個(gè)const。
靜態(tài)成員函數(shù)可以通過(guò)對(duì)象來(lái)調(diào)用,也可以直接使用類名來(lái)調(diào)用。因?yàn)樗穷惖奶匦?,而不是?duì)象的性質(zhì)。
定義類的某個(gè)數(shù)據(jù)成員為靜態(tài)變量,以表明此全局?jǐn)?shù)據(jù)邏輯上屬于該類。定義類的成員函數(shù)為靜態(tài)函數(shù),表明此全局函數(shù)邏輯上屬于該類,而該函數(shù)只對(duì)靜態(tài)數(shù)據(jù)、全局?jǐn)?shù)據(jù)、參數(shù)進(jìn)行操作。