const 是constant的縮寫,本意是不變的,不易改變的意思。 const 在C++中是用來修飾內置類型變量,自定義對象,成員函數,返回值,函數參數。 一、const修飾普通類型的變量。 如下: 1 const int a = 7; 2 3 int b = a; //it's right 4 5 a = 8; // it's wrong, a被定義為一個常量,並且可以將a賦值給b,但是不能給a再次賦值。對一個常量賦值是違法的事情,因為a被編譯器認為是一個常量,其值不允許修改。 接著看如下的操作: 復制代碼 1 2 3 #include<iostream> 4 5 using namespace std; 6 7 int main(void) 8 9 { 10 11 const int a = 7; 12 13 int *p = (int*)&a; 14 15 *p = 8; 16 17 cout<<a; 18 19 system("pause"); 20 21 return 0; 22 23 } 復制代碼 對於const變量a,我們取變量的地址並轉換賦值給 指向int的指針,然後利用*p = 8;重新對變量a地址內的值賦值,然後輸出查看a的值。 從下面的調試窗口看到a的值被改變為8,但是輸出的結果仍然是7。 從結果中我們可以看到,編譯器然後認為a的值為一開始定義的7,所以對const a的操作就會產生上面的情況。所以千萬不要輕易對const變量設法賦值,這會產生意想不到的行為。 如果不想讓編譯器察覺到上面到對const的操作,我們可以在const前面加上volatile關鍵字 Volatile關鍵字跟const對應相反,是易變的,容易改變的意思。所以不會被編譯器優化,編譯器也就不會改變對a變量的操作。 復制代碼 1 #include<iostream> 2 3 using namespace std; 4 5 int main(void) 6 7 { 8 9 volatile const int a = 7; 10 11 int *p = (int*)&a; 12 13 *p = 8; 14 15 cout<<a; 16 17 system("pause"); 18 19 return 0; 20 21 } 復制代碼 輸出結果如我們期望的是8 二、const 修飾指針變量。 const 修飾指針變量有以下三種情況。 A:const 修飾指針指向的內容,則內容為不可變量。 B:const 修飾指針,則指針為不可變量。 C:const 修飾指針和指針指向的內容,則指針和指針指向的內容都為不可變量。 對於A: 1 const int *p = 8; //則指針指向的內容8不可改變。簡稱左定值,因為const位於*號的左邊。 對於B: 復制代碼 1 int a = 8; 2 3 int* const p = &a; 4 5 *p = 9; //it’s right 6 7 int b = 7; 8 9 p = &b; //it’s wrong 復制代碼 //對於const指針p其指向的內存地址不能夠被改變,但其內容可以改變。簡稱,右定向。因為const位於*號的右邊。 對於C: 則是A和B的合並, 1 int a = 8; 2 3 const int * const p = &a; 4 5 //這時,const p的指向的內容和指向的內存地址都已固定,不可改變。 對於A,B,C三種情況,根據const位於*號的位置不同,我總結三句話便於記憶的話, “左定值,右定向,const修飾不變量”。 三、const參數傳遞和函數返回值。 對於const修飾函數參數可以分為三種情況。 A:值傳遞的const修飾傳遞,一般這種情況不需要const修飾,因為函數會自動產生臨時變量復制實參值。 復制代碼 1 #include<iostream> 2 3 using namespace std; 4 5 void Cpf(const int a) 6 7 { 8 9 cout<<a; 10 11 // ++a; it's wrong, a can't is changed 12 13 } 14 15 int main(void) 16 17 { 18 19 Cpf(8); 20 21 system("pause"); 22 23 return 0; 24 25 } 復制代碼 B:當const參數為指針時,可以防止指針被意外篡改。 復制代碼 1 #include<iostream> 2 3 using namespace std; 4 5 void Cpf(int *const a) 6 7 { 8 9 cout<<*a<<" "; 10 11 *a = 9; 12 13 } 14 15 int main(void) 16 17 { 18 19 int a = 8; 20 21 Cpf(&a); 22 23 cout<<a; // a is 9 24 25 system("pause"); 26 27 return 0; 28 29 } 復制代碼 C:自定義類型的參數傳遞,需要臨時對象復制參數,對於臨時對象的構造,需要調用構造函數,比較浪費時間,因此我們采取const外加引用傳遞的方法。 並且對於一般的int ,double等內置類型,我們不采用引用的傳遞方式。 復制代碼 1 #include<iostream> 2 3 using namespace std; 4 5 class Test 6 7 { 8 9 public: 10 11 Test(){} 12 13 Test(int _m):_cm(_m){} 14 15 int get_cm()const 16 17 { 18 19 return _cm; 20 21 } 22 23 private: 24 25 int _cm; 26 27 }; 28 29 30 31 void Cmf(const Test& _tt) 32 33 { 34 35 cout<<_tt.get_cm(); 36 37 } 38 39 int main(void) 40 41 { 42 43 Test t(8); 44 45 Cmf(t); 46 47 system("pause"); 48 49 return 0; 50 51 } 復制代碼 //結果輸出 8 對於const修飾函數的返回值 Const修飾返回值分三種情況。 A:const修飾內置類型的返回值,修飾與不修飾返回值作用一樣。 復制代碼 1 #include<iostream> 2 3 using namespace std; 4 5 const int Cmf() 6 7 { 8 9 return 1; 10 11 } 12 13 int Cpf() 14 15 { 16 17 return 0; 18 19 } 20 21 int main(void) 22 23 { 24 25 int _m = Cmf(); 26 27 int _n = Cpf(); 28 29 30 31 cout<<_m<<" "<<_n; 32 33 system("pause"); 34 35 return 0; 36 37 } B:const 修飾自定義類型的作為返回值,此時返回的值不能作為左值使用,既不能被賦值,也不能被修改。 C: const 修飾返回的指針或者引用,是否返回一個指向const的指針,取決於我們想讓用戶干什麼。 四、const修飾類成員函數. const 修飾類成員函數,其目的是防止成員函數修改被調用對象的值,如果我們不想修改一個調用對象的值,所有的成員函數都應當聲明為const成員函數。注意:const關鍵字不能與static關鍵字同時使用,因為static關鍵字修飾靜態成員函數,靜態成員函數不含有this指針,即不能實例化,const成員函數必須具體到某一實例。 下面的get_cm()const;函數用到了const成員函數 復制代碼 1 #include<iostream> 2 3 using namespace std; 4 5 class Test 6 7 { 8 9 public: 10 11 Test(){} 12 13 Test(int _m):_cm(_m){} 14 15 int get_cm()const 16 17 { 18 19 return _cm; 20 21 } 22 23 private: 24 25 int _cm; 26 27 }; 28 29 30 31 void Cmf(const Test& _tt) 32 33 { 34 35 cout<<_tt.get_cm(); 36 37 } 38 39 int main(void) 40 41 { 42 43 Test t(8); 44 45 Cmf(t); 46 47 system("pause"); 48 49 return 0; 50 51 } 復制代碼 如果get_cm()去掉const修飾,則Cmf傳遞的const _tt即使沒有改變對象的值,編譯器也認為函數會改變對象的值,所以我們盡量按照要求將所有的不需要改變對象內容的函數都作為const成員函數。 如果有個成員函數想修改對象中的某一個成員怎麼辦?這時我們可以使用mutable關鍵字修飾這個成員,mutable的意思也是易變的,容易改變的意思,被mutable關鍵字修飾的成員可以處於不斷變化中,如下面的例子。 復制代碼 1 #include<iostream> 2 using namespace std; 3 class Test 4 { 5 public: 6 Test(int _m,int _t):_cm(_m),_ct(_t){} 7 void Kf()const 8 { 9 ++_cm; //it's wrong 10 ++_ct; //it's right 11 } 12 private: 13 int _cm; 14 mutable int _ct; 15 }; 16 17 int main(void) 18 { 19 Test t(8,7); 20 return 0; 21 }