const:符號常量 使用符號常量寫出的代碼更容易維護;指針是邊讀邊移動,而不是邊寫邊移動,許多函數參數是只讀不寫的。
const最常見用途是作為數組的界和switch分情況標號(也可以用枚舉符代替),分類如下:
常變量: const 類型說明符 變量名
常引用: const 類型說明符 &引用名
常對象: 類名 const 對象名
常成員函數: 類名::fun(形參) const
常數組: 類型說明符 const 數組名[大小]
常指針: const 類型說明符* 指針名 ,類型說明符* const 指針名
在常變量(const 類型說明符 變量名)、常引用(const 類型說明符 &引用名)、常對象(類名 const 對象名)、 常數組(類型說明符 const 數組名[大小]), const” 與 “類型說明符”或“類名”(其實類名是一種自定義的類型說明符) 的位置可以互換。如:
const int a=5; 與 int const a=5; 等同
類名 const 對象名 與 const 類名 對象名 等同
用法1:常量
取代了C中的宏定義,聲明時必須進行初始化(!c++類中則不然)。const限制了常量的使用方式,並沒有描述常量應該如何分配。如果編譯器知道了某const的所有使用,它甚至可以不為該const分配空間。最簡單的常見情況就是常量的值在編譯時已知,而且不需要分配存儲。―《C++ Program Language》
用const聲明的變量雖然增加了分配空間,但是可以保證類型安全。
C標准中,const定義的常量是全局的,C++中視聲明位置而定。
用法2:指針和常量
使用指針時涉及到兩個對象:該指針本身和被它所指的對象。將一個指針的聲明用const“預先固定”將使那個對象而不是使這個指針成為常量。要將指針本身而不是被指對象聲明為常量,必須使用聲明運算符*const。
所以出現在 * 之前的const是作為基礎類型的一部分:
char *const cp; //到char的const指針
char const *pc1; //到const char的指針
const char *pc2; //到const char的指針(后兩個聲明是等同的)
從右向左讀的記憶方式:
cp is a const pointer to char. 故cp不能指向別的字符串,但可以修改其指向的字符串的內容
pc2 is a pointer to const char. 故*pc2的內容不可以改變,但pc2可以指向別的字符串
且注意:允許把非 const 對象的地址賦給指向 const 對象的指針,不允許把一個 const 對象的地址賦給一個普通的、非 const 對象的指針。(C++語言強制要求指向const對象的指針也必須具有const特性)
用法3:const修飾函數傳入參數
將函數傳入參數聲明為const,以指明使用這種參數僅僅是為了效率的原因,而不是想讓調用函數能夠修改對象的值。同理,將指針參數聲明為const,函數將不修改由這個參數所指的對象。
通常修飾指針參數和引用參數:
void Fun( const A *in); //修飾指針型傳入參數
void Fun(const A &in); //修飾引用型傳入參數
用法4:修飾函數返回值
可以阻止用戶修改返回值。返回值也要相應的付給一個常量或常指針。
若函數的返回值是指針,且用const修飾,則函數返回值指向的內容是常數,不可被修改,此返回值僅能賦值給const修飾的相同類型的指針。如:
1 const int * f1(){ 2 int * p; 3 p = new int; 4 *p = 1; 5 return p; 6 } 7 int main(){ 8 const int * p1; 9 p1 = f1(); 10 return 0; 11 }
若第8行改為int *p1;則編譯時報錯:“[8] error: invalid conversion from 'const int*' to 'int*'” (編譯器code::block);
若主函數改為:
7 int main(){ 8 const int * p1; 9 p1 = f1(); 10 *p1 = 2; 11 return 0; 12 }
則編譯時報錯:"[10] error: assignment of read-only location '* p1'" (編譯器code::block);
如果函數返回值是數值(by value),因C++中,返回值會被復制到外部臨時的存儲單元中,故const 修飾,中沒有任何價值的。例:不要把函數int fun1() 寫成const int func1()。
同理不要把函數A GetA(void) 寫成const A GetA(void),其中A 為用戶自定義的數據類型。
如果返回值是對象,將函數A fun2() 改寫為const A & fun2()的確能提高效率。但此要注意,要確定函數究竟是想返回一個對象的“copy”,還是僅返回對象的“別名”即可,否則程序會出錯。
用法5:const修飾成員函數(c++特性)
const對象只能訪問const成員函數,而非const對象可以訪問任意的成員函數,包括const成員函數;
const對象的成員是不能修改的,而通過指針維護的對象確實可以修改的;
const成員函數不可以修改對象的數據,不管對象是否具有const性質。編譯時以是否修改成員數據為依據進行檢查。
面向對象程序設計中,為了體現封裝性,通常不允許直接修改類對象的數據成員。若要修改類對象,應調用公有成員函數來完成。為了保證const對象的常量性,編譯器須區分不安全與安全的成員函數(即區分試圖修改類對象與不修改類對象的函數)。例如,
1 const Screen blankScreen; 2 blankScreen.display(); // 對象的讀操作 3 blankScreen.set(‘*’); // 錯誤:const類對象不允許修改
在C++中,只有被聲明為const的成員函數才能被一個const類對象調用。
要聲明一個const類型的類成員函數,只需要在成員函數參數列表后加上關鍵字const,例如,
1 class Screen { 2 public: 3 char get() const; 4 };
在類體之外定義const成員函數時,還必須加上const關鍵字,例如
1 char Screen::get() const { 2 return _screen[_cursor]; 3 }
若將成員成員函數聲明為const,則該函數不允許修改類的數據成員。例如,
1 class Screen { 2 public: 3 int ok() const {return _cursor; } 4 int error(intival) const { _cursor = ival; } 5 };
在上面成員函數的定義中,ok()的定義是合法的,error()的定義則非法。
值得注意的是,把一個成員函數聲明為const可以保證這個成員函數不修改數據成員,但是,如果據成員是指針,則const成員函數並不能保證不修改指針指向的對象,編譯器不會把這種修改檢測為錯誤。例如,
1 class Name { 2 public: 3 void setName(const string &s) const; 4 private: 5 char *m_sName; 6 }; 7 8 void setName(const string &s) const { 9 m_sName = s.c_str(); // 錯誤!不能修改m_sName; 11 for (int i = 0; i < s.size(); ++i) 12 m_sName[i] = s[i]; // 不好的風格,但不是錯誤的 13 }
雖然m_Name不能被修改,但m_sName是char *類型,const成員函數可以修改其所指向的字符。
const成員函數可以被具有相同參數列表的非const成員函數重載,例如,
1 class Screen { 2 public: 3 char get(int x,int y); 4 char get(int x,int y) const; 5 };
在這種情況下,類對象的常量性決定調用哪個函數。
const Screen cs; Screen cc2; char ch = cs.get(0, 0); // 調用const成員函數 ch = cs2.get(0, 0); // 調用非const成員函數
小結:
1)const成員函數可以訪問非const對象的非const數據成員、const數據成員,也可以訪問const對象內的所有數據成員;
2)非const成員函數可以訪問非const對象的非const數據成員、const數據成員,但不可以訪問const對象的任意數據成員;
3)作為一種良好的編程風格,在聲明一個成員函數時,若該成員函數並不對數據成員進行修改操作,應盡可能將該成員函數聲明為const 成員函數。
用const 修飾函數的參數
2 用const 修飾函數的返回值