有時候咱們但願定義這樣一種變量,它的值不能被改變。例如,用一個變量來表示緩衝區的大小,使用變量的好處是當咱們以爲緩衝區大小再也不合適時,很容易對其進行調整。另外一方面,也應隨時警戒防止程序一不當心改變了這個值,爲了知足這一要求,能夠用關鍵字const對變量的類型加以限定;指針
const int buffSize = 512; //輸入緩衝區的大小對象
這樣就把buffSize定義成了一個常量,任何試圖爲buffSize賦值的行爲都將引起錯誤;ip
buffSize = 512; //錯誤,試圖向const對象寫值ci
由於const對象一旦建立後其值就不能再改變,因此const對象必須初始化,初始值能夠是任意複雜的表達式;get
const int i = get_size(); //運行時初始化編譯器
const int j = 42; //正確,編譯時初始化pip
const int k; //錯誤,k是一個未經初始化的常量。編譯
與非const類型所能參與的操做相比,const類型的對象能完成其中大部分,但也不是全部的操做都適合,主要的限制就是隻能在const類型的對象上執行不改變其內容的操做。例如,const int和普通的int同樣都能參與算術運算,也都能轉換成一個布爾值,等等。dva
在不改變const對象的操做中還有一種是初始化,若是利用一個對象去初始化另一個對象,則它們是否是const都可有可無;變量
int i = 42;
const int ci = i; //正確,i的值
int j = ci; //正確,ci的值被拷貝給了j;
儘管ci是整型常量,但不管如何ci中的值仍是一個整型數,ci的常量特徵僅僅在執行改變ci的操做時纔會發揮做用。當用ci去初始化j時,根本無須在乎ci是否是一個常量,拷貝一個對象的值不會改變它,一旦拷貝完成,新的對象就和原來的對象沒什麼關係了
默認狀態下,const對象僅在文件內有效
當以編譯時初始化的方式定義一個const對象時,就如對buffsize的定義同樣:
const int buffsize = 512; //輸入緩衝區大小
編譯器將在編譯過程當中把用到該變量的地方都替換成對應的值,也就是說,編譯器會找到代碼中全部用到buffsize的地方,而後用512替換。
爲了執行上述替換,編譯器必須知道變量的初始值,若是程序包含多個文件,則每一個用了const對象的文件都必須能訪問它的初始值才行,要作到這一點,就必須在每個用到變量的文件中都有它的定義。爲了支持這一用法,同時避免對同一變量的重複定義,默認狀況下,const對象被設定爲僅在文件內有效。當多個文件中出現了同名的const變量時,其實等同於在不一樣文件中分別定義了獨立的變量。
某些時候有這樣一種const變量,它的初始值不是一個常量表達式,但又確實有必要在文件間共享,這種狀況下,咱們不但願編譯器爲每一個文件分別生成獨立的變量,相反,咱們想讓這類const對象像其餘(很是量)對象同樣工做,也就是說,只在一個文件中定義const,而在其餘多個文件中聲明並使用它。
解決的辦法是,對於const變量不論是聲明仍是定義都添加extern關鍵字,這樣只需定義一次就能夠了:
//file_1.cc定義並初始化了一個常量,該常量能被其餘文件訪問
extern const int buffsize = fcn();
//file_1.h 頭文件
extern const int buffsize; //與file_1.cc中定義的buffsize是同一個
如上述程序所示,file_1.cc定義並初始化了buffsize。由於這條語句包含了初始值,因此它是一次定義,然而,由於buffsize是一個常量,必須用extern加以限定使其被其餘文件使用。
file_1.h頭文件中的聲明也有extern作了限定,其做用是指明buffsize並不是本文件所獨有,它的定義將在別處出現。
const的引用
能夠把引用綁定到const對象上,就像綁定到其它對象上同樣,咱們稱之爲對常量的引用(reference to const).與普通引用不一樣的是,對常量的引用不能被用做修改它所綁定的對象:
const int ci = 1024;
const int &r1 = ci; //正確,引用及其對應的對象都是常量。
r1 = 42; //錯誤,r1是對常量的引用。
int &r2 = ci; //錯誤,試圖讓一個很是量引用指向一個常量對象
由於不容許直接爲ci賦值,固然也就不能經過引用去改變ci。所以,對r2的初始化是錯誤的,假設該初始化是合法,則能夠經過r2來改變它引用對象的值,這顯然是不正確的。
對const的引用可能引用一個並不是const的對象
必須認識到,常量引用僅對引用可參與的操做作了限定,對於引用的對象自己是否是一個常量未作限定,由於對象也多是個很是量,因此容許經過其餘途徑改變它的值;
int i = 42;
int &r1 = i; //引用ri綁定對象i;
const int &r2 = i; //r2也綁定對象i,可是不容許經過r2修改i的值
r1 = 0;//r1並不是常量,i的值修改成0;
r2 = 0; //錯誤,r2是一個常量引用
r2綁定(很是量)整數i是合法的行爲,然而,不容許經過r2修改i的值,儘管如此,i的值仍然容許經過其餘途徑修改,既能夠直接給i賦值,也能夠經過像r1同樣綁定到i的其餘引用來修改
指針和const
與引用同樣,也能夠令指針指向常量或很是量,相似於常量引用,指向常量的指針(pointer to const)不能用於改變其所指對象的值。要想存放常量對象的地址,只能使用指向常量的指針。
const double pi = 3.14; //pi是一個常量,它的值不能改變
double *ptr = π //錯誤,ptr是一個普通指針,能夠經過ptr修改pi的值
const double *cptr = π //正確,cptr能夠指向一個雙精度常量
*cptr = 42; //錯誤,不能給*cptr賦值
可是容許令一個指向常量的指針指向一個很是量對象
double dval = 3.14; // dval是一個雙精度浮點數,它的值能夠改變
cptr = &dval; //正確,可是不能經過cptr改變dval的值
和常量引用同樣,指向常量的指針也沒有規定其所指的對象必須是一個常量。所謂指向常量的指針僅僅要求不能經過該指針改變對象的值,而沒有規定那個對象的值不能經過其餘途徑改變
const指針
指針是對象而引用不是,所以就像其餘對象類型同樣,容許指針自己定義爲常量。常量指針(const pointer)必須初始化,並且一旦初始化完成,則它的值(也就是存放在指針中的那個地址)就不能再改變了。把*放在const關鍵字以前用以說明指針是一個常量,這樣書寫形式隱含着一層意味,即不變的是指針自己而非指向的那個值:
int errNum = 0;
int *const curErr = &errNum; //curErr 將一直指向errNum
const double pi = 3.14;
const double *const pip = π //pip是一個指向常量對象的常量指針
要想弄清楚這些聲明的含義最行之有效的辦法是從右向左閱讀,此例中,離curErr最近的符號是const,意味着curErr自己是一個常量對象,對象的類型是由聲明符的其他部分肯定,聲明符中的下一個符號是*,意思是curErr是一個常量指針,最後,該聲明語句的基本類型部分肯定了常量指針指向的是一個int對象,與之類似,咱們也能推斷出,pip是一個常量指針,它指向的對象是一個雙精度浮點型常量。
指針自己是一個常量並不意味着不能經過指針修改其所指對象的值,可否這樣作徹底依賴於所指對象的類型,例如,pip是一個指向常量的常量指針,則不管是pip所指的對象仍是pip本身存儲的那個地址都不能改變,相反,curErr指向的是一個通常的很是整數,那麼就徹底能夠用curErr去修改errNum的值;
*pip = 2.17 //錯誤,pi是一個指向常量的指針
if( *curErr )
{
*curErr = 0; //正確。把curerr所指的對象的值重置
}