關(guān)于C的關(guān)鍵字——const的理解和用法
const在C中的用法很靈活(相信C++中也一樣),個人感覺對之既愛又恨,有時候感覺const很好用,同時又經(jīng)
常會因為它的優(yōu)點而犯錯,犯錯的原因除了粗心之外,另一個更重要的,就是以前對const理解不到位。于是今天
自己寫成一篇小總結(jié)。如果是初學(xué)者,建議好好看一下,相信幫助比較大;如果是高手,請不吝賜教!
上面寫了一段廢話,就算是小序吧:)接下來就是正文;
一、關(guān)于const的具體定義:
——個人感覺很難對它下一個標(biāo)準(zhǔn)的定義,因為的用法很靈活,似乎對它定義后總無法讓人能夠明白它的
意思,而且容易讓人產(chǎn)生誤解(也許是偶水平太菜了)。例如,把它有定義:一個能夠讓變量變成無法修改的常量
的關(guān)鍵字。那么,這樣的話,就可能讓人誤解為只要有const在定義變量里面,那變量就無論怎樣都無法修改。這
樣的理解是很片面的(下面用法方面將對這問題做探討)。因此,本人在此不敢對它下定義,其他參考書好象也沒
有下定義。
二、關(guān)于const的具體作用
——const作用就靈活了,一個表達式中const放置的位置不同,效果可能就不一樣了。下面分具體情況分
析(當(dāng)然,所舉的情況并非覆蓋全部情況)
A。const最經(jīng)常的用法
1.為了防止傳遞的函數(shù)參數(shù)不被修改,在調(diào)用函數(shù)的形參中用const關(guān)鍵字.
//Example ->
int FindNum(const int array[], int num, int conut);//聲明函數(shù)
//code...
int FindNum(const int array[], int num, int count)
{
int i;
int flag = 1;
for (i = 0; (i < count) && flag; i++)
{
if (array[i] == num)
{
flag = 0;
break;
}
}
return flag;
}
//code...
上面這例子中,編譯器會把array[]當(dāng)作常量數(shù)據(jù)的數(shù)組看待。所以,假如你不小心給數(shù)組賦值,那么
,編譯器就會報錯了。因此,當(dāng)你不需要也不想修改數(shù)組的數(shù)據(jù)時,最好用const把數(shù)組定義為常量數(shù)組。
2.const可以用來創(chuàng)建數(shù)組常量、指針常量、指向常量的指針等:
const char ch = 'a';
const int a[5] = {1, 2, 3, 4, 5};
const int *p = a; //a是一個數(shù)組的首地址.p是指向常量的指針
int * const p = a; //a是一個數(shù)組的首地址.p是指針常量;
const int * const p = a; //a是一個數(shù)組的首地址。p是指向常量的指針常量
前兩種情況很簡單,現(xiàn)在著重分析一下后三種用法,因為這3種情況容易出錯,偶就有時候怕用錯了
剛脆不用const.
——const int *p = a; //p是指向常量的指針,因此,不可以通過給指針賦值來改變數(shù)組
//中的數(shù)據(jù),例如:
// *p = 10; /*錯誤*/
// *(p + 2) = 1; /*錯誤*/
//假如指向常量指針可以改變值,那么,就等于也改變了數(shù)組的數(shù)
//據(jù)了。假如你不理解,偶倒有一個辦法讓你理解,你就想你和一
//個人綁在一起,有可能你移動了位置而他不跟著你移動嗎!哈哈
——int * const p = a; //看這表達式,const的位置和第一個不同吧!他們的用法和作用
//就完全不一樣了。這時候p是指針常量,我們知道,指針是指向
//了一個數(shù)組的首地址,那么,它的位置就不可以改變了。但是你
//現(xiàn)在應(yīng)該和第一個表達式比較了,現(xiàn)在的數(shù)組并不是常量數(shù)組,
//所以數(shù)組的數(shù)據(jù)是可以改變的,而指針這時候它是不可以移動的
//,指向數(shù)組第一個數(shù)據(jù),所以它可以而且只可以改變數(shù)組第一個
//數(shù)據(jù)的值。這一點請別誤解,指針常量只是它的地址不可以改變
//,并不是它指向的內(nèi)容一定不可以改變,這一點切記!
//好啦。假如你又不理解,偶又有一個比較形象的例子來說明:
//假如有一個固定的人拉著另外一個人的手,注意,固定的人相當(dāng)
//于他是不可以由其他人來替換的。但是他可以拉其他人的手啊,
//并不一定規(guī)定他必須拉同一個人的手啊。現(xiàn)在你應(yīng)該可以有個比
//較深的印象和理解吧:P
//下面舉幾個例子幫助理解:
// *p = 2; /*可以*/
// *(p+1) = 10; /*可以*/
// p++; /*不可以*/
——const int * const p = a; //假如前面兩種表達式的本質(zhì)你理解了,這種表達式你來理解根本
//沒有問題,const現(xiàn)在有兩個,而且一個const的位置是第一種情
//況的位置,第二個const是第二種情況的位置,所以這表達式的功
//能就是前兩種情況的作用總合。這里不多說!
//下面舉幾個例子幫助理解:
// *p = 2; /*不可以*/
// *(p + 2) = 10; /*不可以*/
// p++; /*不可以*/
B。const并不會阻止參數(shù)的修改
之所以把這作為一點來談,就是因為有一些朋友可能會以為在函數(shù)參數(shù)中用了const就一定不可以改變
參數(shù),這實際上是錯誤的理解,因為,它并不阻止參數(shù)的修改,下面舉個簡單的例子來闡述一下;
#include
#include
void ChangeStr(const char *String);
int main(void)
{
char str[] = "The C programme";
Change(str);
printf(str);
system("Pause");
return 0;
}
void ChangeStr(const char *String)
{
char *Source = (char *)String;
while (*Source)
{
*Source = toupper(*Source);
Source++;
}
}
//end
上面的程序把字符串中的每個字符都轉(zhuǎn)換成大寫字母了。因為*String把地址給了*Source,而
*Source的值的改變編譯器并不干涉,可能有的編譯器會發(fā)出警告之類。上面的程序只是為了說明const并不會阻止
參數(shù)的修改,如果象上面程序那樣,個人感覺沒什么意義,只會讓人容易混亂而已。
關(guān)于CONST的用法和理解本人也就只能說這么多了,當(dāng)然,很可能有更多高級或者少用的用法,由于水平
和經(jīng)驗有限,確實不能再說些什么。
三、參考文獻
——《C primer plus 5th》
ID: lj_860603
整理于2006.5.21
2. const in c++ language
面向?qū)ο笫荂++的重要特性.
但是c++在c的基礎(chǔ)上新增加的幾點優(yōu)化也是很耀眼的
就const直接可以取代c中的#define
以下幾點很重要,學(xué)不好后果也也很嚴(yán)重
const
1. 限定符聲明變量只能被讀
const int i=5;
int j=0;
...
i=j; //非法,導(dǎo)致編譯錯誤
j=i; //合法
2. 必須初始化
const int i=5; //合法
const int j; //非法,導(dǎo)致編譯錯誤
3. 在另一連接文件中引用const常量
extern const int i; //合法
extern const int j=10; //非法,常量不可以被再次賦值
4. 便于進行類型檢查
用const方法可以使編譯器對處理內(nèi)容有更多了解。
#define I=10
const long &i=10; /*dapingguo提醒:由于編譯器的優(yōu)化,使
得在const long i=10; 時i不被分配內(nèi)存,而是已10直接代入
以后的引用中,以致在以后的代碼中沒有錯誤,為達到說教效
果,特別地用&i明確地給出了i的內(nèi)存分配。不過一旦你關(guān)閉所
有優(yōu)化措施,即使const long i=10;也會引起后面的編譯錯誤。*/
char h=I; //沒有錯
char h=i; //編譯警告,可能由于數(shù)的截短帶來錯誤賦值。
5. 可以避免不必要的內(nèi)存分配
#define STRING "abcdefghijklmn\n"
const char string[]="abcdefghijklm\n";
...
printf(STRING); //為STRING分配了第一次內(nèi)存
printf(string); //為string一次分配了內(nèi)存,以后不再分配
...
printf(STRING); //為STRING分配了第二次內(nèi)存
printf(string);
...
由于const定義常量從匯編的角度來看,只是給出了對應(yīng)的內(nèi)存地址,
而不是象#define一樣給出的是立即數(shù),所以,const定義的常量在
程序運行過程中只有一份拷貝,而#define定義的常量在內(nèi)存中有
若干個拷貝。
6. 可以通過函數(shù)對常量進行初始化
int value();
const int i=value();
dapingguo說:假定對ROM編寫程序時,由于目標(biāo)代碼的不可改寫,
本語句將會無效,不過可以變通一下:
const int &i=value();
只要令i的地址處于ROM之外,即可實現(xiàn):i通過函數(shù)初始化,而其
值有不會被修改。
7. 是不是const的常量值一定不可以被修改呢?
觀察以下一段代碼:
const int i=0;
int *p=(int*)&i;
p=100;
通過強制類型轉(zhuǎn)換,將地址賦給變量,再作修改即可以改變const常量值。
8. 請分清數(shù)值常量和指針常量,以下聲明頗為玩味:
int ii=0;
const int i=0; //i是常量,i的值不會被修改
const int *p1i=&i; //指針p1i所指內(nèi)容是常量,可以不初始化
int * const p2i=? //指針p2i是常量,所指內(nèi)容可修改
const int * const p3i=&i; //指針p3i是常量,所指內(nèi)容也是常量
p1i=? //合法
*p2i=100; //合法
關(guān)于C++中的const關(guān)鍵字的用法非常靈活,而使用const將大大改善程序的健壯性,參考了康建東兄的const使用詳解一文,對其中進行了一些補充,寫下了本文。
1. const常量,如const int max = 100;
優(yōu)點:const常量有數(shù)據(jù)類型,而宏常量沒有數(shù)據(jù)類型。編譯器可以對前者進行類型安全檢查,而對后者只進行字符替換,沒有類型安全檢查,并且在字符替換時可能會產(chǎn)生意料不到的錯誤(邊際效應(yīng))
2. const 修飾類的數(shù)據(jù)成員。如:
class A
{
const int size;
…
}
const數(shù)據(jù)成員只在某個對象生存期內(nèi)是常量,而對于整個類而言卻是可變的。因為類可以創(chuàng)建多個對象,不同的對象其const數(shù)據(jù)成員的值可以不同。所以不能在類聲明中初始化const數(shù)據(jù)成員,因為類的對象未被創(chuàng)建時,編譯器不知道const 數(shù)據(jù)成員的值是什么。如
class A
{
const int size = 100; //錯誤
int array[size]; //錯誤,未知的size
}
const數(shù)據(jù)成員的初始化只能在類的構(gòu)造函數(shù)的初始化表中進行。要想建立在整個類中都恒定的常量,應(yīng)該用類中的枚舉常量來實現(xiàn)。如
class A
{…
enum {size1=100, size2 = 200 };
int array1[size1];
int array2[size2];
}
枚舉常量不會占用對象的存儲空間,他們在編譯時被全部求值。但是枚舉常量的隱含數(shù)據(jù)類型是整數(shù),其最大值有限,且不能表示浮點數(shù)。
3. const修飾指針的情況,見下式:
int b = 500;
const int* a = & [1]
int const *a = & [2]
int* const a = & [3]
const int* const a = & [4]
如果你能區(qū)分出上述四種情況,那么,恭喜你,你已經(jīng)邁出了可喜的一步。不知道,也沒關(guān)系,我們可以參考《Effective c++》Item21上的做法,如果const位于星號的左側(cè),則const就是用來修飾指針?biāo)赶虻淖兞浚粗羔樦赶驗槌A?;如果const位于星號的右側(cè),const就是修飾指針本身,即指針本身是常量。因此,[1]和[2]的情況相同,都是指針?biāo)赶虻膬?nèi)容為常量(const放在變量聲明符的位置無關(guān)),這種情況下不允許對內(nèi)容進行更改操作,如不能*a = 3 ;[3]為指針本身是常量,而指針?biāo)赶虻膬?nèi)容不是常量,這種情況下不能對指針本身進行更改操作,如a++是錯誤的;[4]為指針本身和指向的內(nèi)容均為常量。
4. const的初始化
先看一下const變量初始化的情況
1) 非指針const常量初始化的情況:A b;
const A a = b;
2) 指針const常量初始化的情況:
A* d = new A();
const A* c = d;
或者:const A* c = new A();
3)引用const常量初始化的情況:
A f;
const A& e = f; // 這樣作e只能訪問聲明為const的函數(shù),而不能訪問一
般的成員函數(shù);
[思考1]: 以下的這種賦值方法正確嗎?
const A* c=new A();
A* e = c;
[思考2]: 以下的這種賦值方法正確嗎?
A* const c = new A();
A* b = c;
5. 另外const 的一些強大的功能在于它在函數(shù)聲明中的應(yīng)用。在一個函數(shù)聲明中,const 可以修飾函數(shù)的返回值,或某個參數(shù);對于成員函數(shù),還可以修飾是整個函數(shù)。有如下幾種情況,以下會逐漸的說明用法:A& operator=(const A& a);
void fun0(const A* a );
void fun1( ) const; // fun1( ) 為類成員函數(shù)
const A fun2( );
1) 修飾參數(shù)的const,如 void fun0(const A* a ); void fun1(const A& a);
調(diào)用函數(shù)的時候,用相應(yīng)的變量初始化const常量,則在函數(shù)體中,按照const所修飾的部分進行常量化,如形參為const A* a,則不能對傳遞進來的指針的內(nèi)容進行改變,保護了原指針?biāo)赶虻膬?nèi)容;如形參為const A& a,則不能對傳遞進來的引用對象進行改變,保護了原對象的屬性。
[注意]:參數(shù)const通常用于參數(shù)為指針或引用的情況,且只能修飾輸入?yún)?shù);若輸入?yún)?shù)采用“值傳遞”方式,由于函數(shù)將自動產(chǎn)生臨時變量用于復(fù)制該參數(shù),該參數(shù)本就不需要保護,所以不用const修飾。
[總結(jié)]對于非內(nèi)部數(shù)據(jù)類型的輸入?yún)?shù),因該將“值傳遞”的方式改為“const引用傳遞”,目的是為了提高效率。例如,將void Func(A a)改為void Func(const A &a)
對于內(nèi)部數(shù)據(jù)類型的輸入?yún)?shù),不要將“值傳遞”的方式改為“const引用傳遞”。否則既達不到提高效率的目的,又降低了函數(shù)的可理解性。例如void Func(int x)不應(yīng)該改為void Func(const int &x)
2) 修飾返回值的const,如const A fun2( ); const A* fun3( );
這樣聲明了返回值后,const按照"修飾原則"進行修飾,起到相應(yīng)的保護作用。const Rational operator*(const Rational& lhs, const Rational& rhs)
{
return Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}
返回值用const修飾可以防止允許這樣的操作發(fā)生:Rational a,b;
Radional c;
(a*b) = c;
一般用const修飾返回值為對象本身(非引用和指針)的情況多用于二目操作符重載函數(shù)并產(chǎn)生新對象的時候。
[總結(jié)]
1. 一般情況下,函數(shù)的返回值為某個對象時,如果將其聲明為const時,多用于操作符的重載。通常,不建議用const修飾函數(shù)的返回值類型為某個對象或?qū)δ硞€對象引用的情況。原因如下:如果返回值為某個對象為const(const A test = A 實例)或某個對象的引用為const(const A& test = A實例) ,則返回值具有const屬性,則返回實例只能訪問類A中的公有(保護)數(shù)據(jù)成員和const成員函數(shù),并且不允許對其進行賦值操作,這在一般情況下很少用到。
2. 如果給采用“指針傳遞”方式的函數(shù)返回值加const修飾,那么函數(shù)返回值(即指針)的內(nèi)容不能被修改,該返回值只能被賦給加const 修飾的同類型指針。如:
const char * GetString(void);
如下語句將出現(xiàn)編譯錯誤:
char *str=GetString();
正確的用法是:
const char *str=GetString();
3. 函數(shù)返回值采用“引用傳遞”的場合不多,這種方式一般只出現(xiàn)在類的賻值函數(shù)中,目的是為了實現(xiàn)鏈?zhǔn)奖磉_。如:
class A
{…
A &operate = (const A &other); //負值函數(shù)
}
A a,b,c; //a,b,c為A的對象
…
a=b=c; //正常
(a=b)=c; //不正常,但是合法
若負值函數(shù)的返回值加const修飾,那么該返回值的內(nèi)容不允許修改,上例中a=b=c依然正確。(a=b)=c就不正確了。
[思考3]: 這樣定義賦值操作符重載函數(shù)可以嗎?
const A& operator=(const A& a);
6. 類成員函數(shù)中const的使用
一般放在函數(shù)體后,形如:void fun() const;
任何不會修改數(shù)據(jù)成員的函數(shù)都因該聲明為const類型。如果在編寫const成員函數(shù)時,不慎修改了數(shù)據(jù)成員,或者調(diào)用了其他非const成員函數(shù),編譯器將報錯,這大大提高了程序的健壯性。如:
class Stack
{
public:
void Push(int elem);
int Pop(void);
int GetCount(void) const; //const 成員函數(shù)
private:
int m_num;
int m_data[100];
};
int Stack::GetCount(void) const
{
++m_num; //編譯錯誤,企圖修改數(shù)據(jù)成員m_num
Pop(); //編譯錯誤,企圖調(diào)用非const函數(shù)
Return m_num;
}
7. 使用const的一些建議
1 要大膽的使用const,這將給你帶來無盡的益處,但前提是你必須搞清楚原委;
2 要避免最一般的賦值操作錯誤,如將const變量賦值,具體可見思考題;
3 在參數(shù)中使用const應(yīng)該使用引用或指針,而不是一般的對象實例,原因同上;
4 const在成員函數(shù)中的三種用法(參數(shù)、返回值、函數(shù))要很好的使用;
5 不要輕易的將函數(shù)的返回值類型定為const;
6除了重載操作符外一般不要將返回值類型定為對某個對象的const引用;
[思考題答案]
1 這種方法不正確,因為聲明指針的目的是為了對其指向的內(nèi)容進行改變,而聲明的指針e指向的是一個常量,所以不正確;
2 這種方法正確,因為聲明指針?biāo)赶虻膬?nèi)容可變;
3 這種做法不正確;
在const A::operator=(const A& a)中,參數(shù)列表中的const的用法正確,而當(dāng)這樣連續(xù)賦值的時侯,問題就出現(xiàn)了:
A a,b,c:
(a=b)=c;
因為a.operator=(b)的返回值是對a的const引用,不能再將c賦值給const常量。
編輯 webmaster
我們也許學(xué)習(xí)過const的使用,但是對于const的細致的技術(shù)細節(jié)卻不一定掌握。const的用法在許多的教材上只是簡單的介紹,在這里我們對 const進行細致的概念以及用法剖析。const 是由c++采用,并加進標(biāo)準(zhǔn)c中,但是他們的意義完全不同,在舊版本(標(biāo)準(zhǔn)前)的c中,如果想建立一個常量,必須使用預(yù)處理器:
#define PI 3.14159
此后無論在何處使用PI,都會被預(yù)處理器以3.14159替代。編譯器不對PI進行類型檢查,也就是說可以不受限制的建立宏并用它來替代值,如果使用不慎,很可能由預(yù)處理引入錯誤,這些錯誤往往很難發(fā)現(xiàn)。
我們也不能得到PI的地址(即不能向PI傳遞指針和引用)。
c++引入了命名常量的概念,命名常量就像變量一樣,只是它的值不能改變,如果試圖改變一個const 對象,編譯器將會產(chǎn)生錯誤。 const 和正常變量一樣有作用域,所以函數(shù)內(nèi)部的const也不會影響程序的其余部分。在c++中const可以取代預(yù)處理器#define來進行值替代, const有安全的類型檢查,所以不用擔(dān)心會像預(yù)處理器一樣引入錯誤。
在通常的情況下const同預(yù)處理器#define一樣只是將所賦值保存入編譯器的符號表中(符號表僅僅在編譯時存在,在編譯過程中編譯器將程序中的名字與之在符號表中定義的數(shù)值作簡單的替換),在使用的時候進行值替換,并不為const創(chuàng)建存儲空間。我們將const的定義放進頭文件里,這樣通過包含頭文件,可以把const定義單獨放在一個地方并把它分配給一個編譯單元,const默認為內(nèi)部連接(內(nèi)部連接意味著只對正在編譯的文件創(chuàng)建存儲空間,別的文件可以使用相同的標(biāo)示符和全局變量,編譯器不會發(fā)現(xiàn)沖突,外部連接意味著為所有被編譯過的文件創(chuàng)建一片單獨的存儲空間,一般全局變量和函數(shù)名的外部連接通過extern聲明,可以通過其他的文件訪問)也就是說const僅能被它所定義過的文件訪問,在定義一個const時,必須賦一個值給它,除非用extern做出說明:
extern const int a;
這表示const的定義在其他的什么地方,這里僅僅是一個聲明,但是這樣的做法使const使用了外部連接,也就是說上面的extern強制進行了對const的存儲空間分配,這樣我們就無法再用const作為常量折疊(在可能的情況下,符號常量的值會代替改名字的出現(xiàn),這個替代過程叫做常量折疊)使用了,即使我們在其他地方定義了const的值,如:
extern const int a=3;
因為const的值被放入了存儲單元,在編譯的過程中,編譯器不會去讀存儲單元的內(nèi)容。如果我們這樣做:
int b[a];
編譯器就會給我們一個錯誤信息。
想不為const分配存儲空間是不可能的,因為對于復(fù)雜的結(jié)構(gòu),例如集合,編譯器不會復(fù)雜到將集合保存到它的符號表中,所以必須分配內(nèi)存空間,這就意味著“這是一塊不能改變的存儲空間”,當(dāng)然也就不能在編譯期間使用它的值,因為編譯器不知道存儲的內(nèi)容:
const int i[]={1,2,3,4};
//float f[i[2]];
//將得到錯誤信息,編譯器提示不能在數(shù)組定義里找到一個常數(shù)表達式。
因為編譯器靠移動棧指針來存儲和讀取數(shù)據(jù)。
也因此,由于無法避免為const分配內(nèi)存,所以const的定義必須默認為內(nèi)部連接,否則由于眾多的const在多個文件中分配內(nèi)存,就會引起錯誤。下面我們看一段簡單有效的代碼來說明const的常量折疊:
#include
const int a=3;
const int b=a+1;
float *f=(float*)&b;
char c[b+3];
void main()
{
const char gc=cin.get();
const char c2=gc+3;
}
我們可以看到,a是一個編譯器期間的const,b是從a中計算出來的,由于a是一個const,b的計算值來自一個常數(shù)表達式,而它自身也是一個編譯器間的const,接著下面指針f取得了b的地址,所以迫使編譯器給b分配了存儲空間,不過即使分配了存儲空間,由于編譯器已經(jīng)知道了b的值,所以仍然不妨礙在決定數(shù)組c的大小時使用b。
在主函數(shù)main()里,標(biāo)識符gc的值在編譯期間是不知道的,這也意味著需要存儲空間,但是初始化要在定義點進行,而且一旦初始化,其值就不能改變,我們發(fā)現(xiàn)c2是由gc計算出來的,它的作用域與其他類型const的作用域是一樣的,這是對#define用法的一種改進。
在c++引進常量的時候,標(biāo)準(zhǔn)c也引入了const,但是在c中const的意思和在c++中有很大不同,在c中const的意思是“一個不能改變的普通變量”,const常量總是被分配存儲空間而且它的名字是全局符即const使用外部連接。于是在c中:
const int size=100;
char c[size];
得出一個錯誤。但是在c中可以這樣寫:
const int size;
因為c中的const被默認為外部連接,所以這樣做是合理的。
在c語言中使用限定符const不是很有用,如果希望在常數(shù)表達式里(必須在編譯期間被求值)使用一個已命名的值,必須使用預(yù)處理器#define。
在c++中可以使指針成為const,這很有用,如果以后想在程序代碼中改變const這種指針的使用,編譯器將給出通知,這樣大大提高了安全性。在用帶有const的指針時,我們有兩種選擇:const修飾指針指向的對象,或者const修飾指針自己指向的存儲空間。
如果要使指向的對象不發(fā)生改變,則需要這樣寫:
const int *p;
這里p是一個指向const int 的指針,它不需要初始化,因為p可以指向任何標(biāo)識符,它自己并不是一個const,但是它所指的值是不能改變的,同樣的,我們可以這樣寫:
int const *p;
這兩種方法是等同的,依據(jù)個人習(xí)慣以及編碼風(fēng)格不同,程序員自己決定使用哪一種形式。
如果希望使指針成為一個const必須將const標(biāo)明的部分放在*右邊。
int a=3;
int *const j=&a
編譯器要求給它一個初始值,這個值在指針的生命期間內(nèi)不變,也就是說指針始終指向a的地址,不過要改變它地址中的值是可以的:
*j+=4;
也可以是一個const指針指向一個const對象:
const int *j1=&a;
int const *j2=&a;
這樣指針和對象都不能改變,這兩種形式同樣是等同的。在賦值的的時候需要注意,我們可以將一個非const的對象地址賦給一個const指針,但是不能將一個const對象地址賦給一個非const指針,因為這樣可能通過被賦值的指針改變對象的值,當(dāng)然也可以用類型的強制轉(zhuǎn)換來進行const對象的賦值,但是這樣做打破了const提供的安全性。
const也被用于限定函數(shù)參數(shù)和函數(shù)的返回值,如果函數(shù)參數(shù)是按值傳遞時,即表示變量的初值不會被函數(shù)改變,如果函數(shù)的返回值為const那么對于內(nèi)部類型來說按值返回的是否是一個cosnt是無關(guān)緊要的,編譯器不讓它成為一個左值,因為它是一個值而不是一個變量,所以使用const是多余的,例如:
const int f(){return 1;}
void main(){int a=f();}
但是當(dāng)處理用戶定義類型的時候,按值返回常量就很有意義了,這時候函數(shù)的返回值不能被直接賦值也不能被修改。僅僅是非const返回值能作為一個左值使用,但是這往往失去意義,因為函數(shù)返回值在使用時通常保存為一個臨時量,臨時量被作為左值使用并修改后,編譯器將臨時量清除。結(jié)果丟失了所有的修改。
可以用const限定傳遞或返回一個地址(即一個指針或一個引用):
const int * const func(const int *p)
{ static int a=*p;
return &a;
}
參數(shù)內(nèi)的const限定指針p指向的數(shù)據(jù)不能被改變,此后p的值被賦給靜態(tài)變量a,然后將a的地址返回,這里a是一個靜態(tài)變量,在函數(shù)運行結(jié)束后,它的生命期并沒有結(jié)束,所以可以將它的地址返回。因為函數(shù)返回一個const int* 型,所以函數(shù)func的返回值不可以賦給一個非指向const的指針,但它同時接受一個const int * const和一個const int *指針,這是因為在函數(shù)返回時產(chǎn)生一個const臨時指針用以存放a的地址,所以自動產(chǎn)生了這種原始變量不能被改變的約定,于是*右邊的const只有當(dāng)作左值使用時才有意義。
const同樣運用于類中,但是它的意義又有所不同,我們可以創(chuàng)建const的數(shù)據(jù)成員,const的成員函數(shù),甚至是const的對象,但是保持類的對象為const比較復(fù)雜,所以const對象只能調(diào)用const成員函數(shù)。
const的數(shù)據(jù)成員在類的每一個對象中分配存儲,并且一旦初始化這個值在對象的生命期內(nèi)是一個常量,因此在類中建立一個const數(shù)據(jù)成員時,初始化工作必須在構(gòu)造函數(shù)初始化列表中。如果我們希望創(chuàng)建一個有編譯期間的常量成員,這就需要在該常量成員的前面使用static限定符,這樣所有的對象都僅有一個實例:
class X
{
static const int size=50;
int a[size];
public:
X();
};
const對象只能調(diào)用const成員函數(shù),一個普通對象同樣可以調(diào)用const成員函數(shù),因此,const成員函數(shù)更具有一般性,但是成員函數(shù)不會默認為const。聲明一個const成員函數(shù),需要將const限定符放在函數(shù)名的后面:
void f (void ) const;
當(dāng)我們運用const成員函數(shù)時,遇到需要改變數(shù)據(jù)成員,可以用mutable進行特別的指定:
class X
{
mutable int i;
public:
X();
void nochange() const;
};
void X::nochange const(){i++;}
const消除了預(yù)處理器的值替代的不良影響,并且提供了良好的類型檢查形式和安全性,在可能的地方盡可能的使用const對我們的編程有很大的幫助。聯(lián)系客服