你也許有常量的概念並會用關鍵字const聲明一個變數為常量,但是你知道const除了聲明常量外,還可以聲明const指標和const成員函數。本文將討論const對象是如何建立、const對象的用途以及他們的文法。
Const聲明
僅僅用一個關鍵字就可以聲明三種截然不同的結構,所以const聲明常常讓人混淆。讓我們仔細看看這些這三種不同的const結構。
Const對象
當你把某個對象定義為const類型(在這裡“對象”一詞用的是它的廣義語義,例如儲存一個變數或者類對象的一段記憶體)之前,你需要確保程式不會再修改它。Const聲明必須包括合適的初始化。代碼清單A中包含有不同const對象的執行個體。
Const指標
如果把一個指標定義為const類型,那麼程式就不能再讓它指向一個新的地址。不過const指標所指向的對象仍可以修改,如代碼清單B所示。
Const成員函數
一個類對象的狀態由它的非待用資料成員組成。類中不需要修改對象狀態的成員函數應該定義為const類型。你可以在類的成員函數的參數表後面加上關鍵字const來聲明它為const函數。例如:
class Person{public:
int getAge() const {return age;} //const成員函數
private: int age;};
注意const成員函數是約束文法設計(the Design by Contract idiom)的一個重要的組成部分。Const成員函數確保了編程者承諾要實現的功能(如,保持對象的狀態不變)由編譯器強制實現。任何試圖從內部修改const成員變數以及非const成員變數的舉動都會導致編譯時間錯誤的產生:
int Person::getAge() const{
return ++age; //編譯時間出錯
}
另外,const聲明可以證明函數的行為。
混合類型
到目前為止,一切似乎都沒有問題。然而,聲明混合類型時麻煩不期而至。你能在下面的例子中分辨出x和y是哪一種類型嗎?
const char * x= “hello”;
char const * y= “world”;
x和y的類型是一樣的,即“指向const類型字串的指標”——雖然const出現在x和y的位置上。關鍵字const可以出現在它聲明的變數的類型名(字)的前面或者後面,這不會影響到聲明的意義——這與下面例子中的關鍵字long和unsigned很相似:
int long x;
long int y;
unsigned long l;
long unsigned m;
現在,讓我們看看下面這個const聲明:
char * const z= “world”;
這裡,z的類型是“指向非const類型字串的const型指標”。編譯器是如何知道變數z是const指標而x和y不是呢?
--------------------------------------------------------------------------------
const指標和指向const變數的指標的區別是什嗎?
通過檢查關鍵字出現在const星號之前還是之後,你可以區分const指標和const變數。“* const”聲明一個const指標,與之相反,當const出現在星號之前,指標指向的對象是const類型的。
--------------------------------------------------------------------------------
指向const對象的const指標
你可以在單個聲明中把const對象和const指標聯在一起,即聲明指向const對象的const指標。例如:
const int n=10;
const int * const p= & n; // 指向const int對象的const指標
因為p的聲明中出現了“* const”,所以p是const型指標。p指向一個const型的整數,因在它的聲明中,關鍵字const出現在星號之前。在通過有固定地址的記憶體緩衝來訪問ROM裝置的情況下,指向const對象的const指標的用法就特別有用。
現在,你也許正在想“我是不是可以在單個的聲明語句中把三種const結構聯在一起?”,答案是肯定的。在下面的例子中,我們定義一個返回一個指向const整數的const指標的const成員函數:
class A{
//…
const int * const get_scores(int id) const;
};
運算子const_cast<>
const_cast<>運算子用來消除對象的const屬性。不過,使用這個運算子還有幾個限制,我們將簡單的討論這些限制。
消除const屬性
儘管const_cast<>可以消除一個對象的const屬性,但這並不意味著你就可以修改該對象。考慮下面的例子:
const char * p= “hello world”;int main() {
char * s = const_cast (p); // 消除const屬性
strcpy(s, “danger!”); // 不可以,不明確的狀態
}
試圖重寫字串s將會導致不明確的狀態。其原因是const對象可能儲存在系統的唯讀記憶體中。如果你強制去除一個對象的const屬性可以把該對象當著非const對象使用(例如,把它傳遞給一個接受char *型別參數的函數),但不能修改它。
非const型到const型的轉換
絕大多數程式員不知道const_cast<>也有相反的操作,也就是說,它可以把一個非const型的對象轉換為const型對象。例如:
char s[]= “fasten your seatbelt”;
size=strlen(const_cast (s));// 更明確
但是你可以看到使用const_cast<>h的機會很少,原因是C++在需要使用const型對象的環境下會自動執行非const型到const型的轉換。因此,你不會真正需要這樣使用const_cast<>,除非你想驗證你的代碼。
結論
const在文法上的障礙常常使得編程者放棄使用它,這就削弱了編程品質和產生的代碼的可讀性以及可維護性。誠然,指向const對象的指標和const指標的區分不應該這麼容易混淆——尤其是在處理混合類型時;不過,通過檢查const限定詞相對星號的位置可以消除上述的模糊性,並且,const成員函數很容易辨識(const出現在參數表後)。如果你對const感到頭痛的話,相信通過本文,你會發現const的妙用。