C++ const 關鍵字小結
技術標籤:# C/C++程式語言學習之路
const 是 constant 的縮寫,本意是不變的,不易改變的意思。在 C++ 中是用來修飾內建型別變數,自定義物件,成員函式,返回值,函式引數。
C++ const 允許指定一個語義約束,編譯器會強制實施這個約束,允許程式設計師告訴編譯器某值是保持不變的。如果在程式設計中確實有某個值保持不變,就應該明確使用const,這樣可以獲得編譯器的幫助。
一、const修飾普通型別的變數
const int a = 7;
int b = a; // 正確
a = 8; // 錯誤,不能改變
a 被定義為一個常量,並且可以將 a 賦值給 b,但是不能給 a 再次賦值。對一個常量賦值是違法的事情,因為 a 被編譯器認為是一個常量,其值不允許修改。
接著看如下的操作:
例項
#include<iostream>
using namespace std;
int main(void)
{
const int a = 7;
int *p = (int*)&a;
*p = 8;
cout<<a;
system("pause");
return 0;
}
對於 const 變數 a,我們取變數的地址並轉換賦值給 指向 int 的指標,然後利用 *p = 8; 重新對變數 a 地址內的值賦值,然後輸出檢視 a 的值。
從下面的除錯視窗看到 a 的值被改變為 8,但是輸出的結果仍然是 7。
從結果中我們可以看到,編譯器然後認為 a 的值為一開始定義的 7,所以對 const a 的操作就會產生上面的情況。所以千萬不要輕易對 const 變數設法賦值,這會產生意想不到的行為。
如果不想讓編譯器察覺到上面到對 const 的操作,我們可以在 const 前面加上 volatile 關鍵字。
Volatile 關鍵字跟 const 對應相反,是易變的,容易改變的意思。所以不會被編譯器優化,編譯器也就不會改變對 a 變數的操作。
例項
#include<iostream> using namespace std; int main(void) { volatile const int a = 7; int *p = (int*)&a; *p = 8; cout<<a; system("pause"); return 0; }
輸出結果如我們期望的是 8。
二、const 修飾指標變數。
const 修飾指標變數有以下三種情況。
- A: const 修飾指標指向的內容,則內容為不可變數。
- B: const 修飾指標,則指標為不可變數。
- C: const 修飾指標和指標指向的內容,則指標和指標指向的內容都為不可變數。
對於 A:
const int *p = 8;
則指標指向的內容 8 不可改變。簡稱左定值,因為 const 位於 * 號的左邊。
對於 B:
int a = 8;
int* const p = &a;
*p = 9; // 正確
int b = 7;
p = &b; // 錯誤
對於 const 指標 p 其指向的記憶體地址不能夠被改變,但其內容可以改變。簡稱,右定向。因為 const 位於 * 號的右邊。
對於 C:則是 A 和 B的合併
int a = 8;
const int * const p = &a;
這時,const p 的指向的內容和指向的記憶體地址都已固定,不可改變。
對於 A,B,C 三種情況,根據 const 位於 * 號的位置不同,我總結三句話便於記憶的話:"左定值,右定向,const修飾不變數"。
三、const引數傳遞和函式返回值。
對於 const 修飾函式引數可以分為三種情況。
A:值傳遞的 const 修飾傳遞,一般這種情況不需要 const 修飾,因為函式會自動產生臨時變數複製實參值。
例項
#include<iostream>
using namespace std;
void Cpf(const int a)
{
cout<<a;
// ++a; 是錯誤的,a 不能被改變
}
int main(void)
{
Cpf(8);
system("pause");
return 0;
}
B:當 const 引數為指標時,可以防止指標被意外篡改。
例項
#include<iostream>
using namespace std;
void Cpf(int *const a)
{
cout<<*a<<" ";
*a = 9;
}
int main(void)
{
int a = 8;
Cpf(&a);
cout<<a; // a 為 9
system("pause");
return 0;
}
C:自定義型別的引數傳遞,需要臨時物件複製引數,對於臨時物件的構造,需要呼叫建構函式,比較浪費時間,因此我們採取 const 外加引用傳遞的方法。
並且對於一般的 int、double 等內建型別,我們不採用引用的傳遞方式。
例項
#include<iostream>
using namespace std;
class Test
{
public:
Test(){}
Test(int _m):_cm(_m){}
int get_cm()const
{
return _cm;
}
private:
int _cm;
};
void Cmf(const Test& _tt)
{
cout<<_tt.get_cm();
}
int main(void)
{
Test t(8);
Cmf(t);
system("pause");
return 0;
}
結果輸出8。
對於 const 修飾函式的返回值。
Const 修飾返回值分三種情況。
A:const 修飾內建型別的返回值,修飾與不修飾返回值作用一樣。
例項
#include<iostream>
using namespace std;
const int Cmf()
{
return 1;
}
int Cpf()
{
return 0;
}
int main(void)
{
int _m = Cmf();
int _n = Cpf();
cout<<_m<<" "<<_n;
system("pause");
return 0;
}
B: const 修飾自定義型別的作為返回值,此時返回的值不能作為左值使用,既不能被賦值,也不能被修改。
C: const 修飾返回的指標或者引用,是否返回一個指向 const 的指標,取決於我們想讓使用者幹什麼。
四、const修飾類成員函式
const 修飾類成員函式,其目的是防止成員函式修改被呼叫物件的值,如果我們不想修改一個呼叫物件的值,所有的成員函式都應當宣告為 const 成員函式。
注意:const 關鍵字不能與 static 關鍵字同時使用,因為 static 關鍵字修飾靜態成員函式,靜態成員函式不含有 this 指標,即不能例項化,const 成員函式必須具體到某一例項。
下面的 get_cm()const; 函式用到了 const 成員函式:
例項
#include<iostream>
using namespace std;
class Test
{
public:
Test(){}
Test(int _m):_cm(_m){}
int get_cm()const
{
return _cm;
}
private:
int _cm;
};
void Cmf(const Test& _tt)
{
cout<<_tt.get_cm();
}
int main(void)
{
Test t(8);
Cmf(t);
system("pause");
return 0;
}
如果 get_cm() 去掉 const 修飾,則 Cmf 傳遞的 const _tt 即使沒有改變物件的值,編譯器也認為函式會改變物件的值,所以我們儘量按照要求將所有的不需要改變物件內容的函式都作為 const 成員函式。
如果有個成員函式想修改物件中的某一個成員怎麼辦?這時我們可以使用 mutable 關鍵字修飾這個成員,mutable 的意思也是易變的,容易改變的意思,被 mutable 關鍵字修飾的成員可以處於不斷變化中,如下面的例子。
例項
#include<iostream>
using namespace std;
class Test
{
public:
Test(int _m,int _t):_cm(_m),_ct(_t){}
void Kf()const
{
++_cm; // 錯誤
++_ct; // 正確
}
private:
int _cm;
mutable int _ct;
};
int main(void)
{
Test t(8,7);
return 0;
}
這裡我們在 Kf()const 中通過 ++_ct; 修改 _ct 的值,但是通過 ++_cm 修改 _cm 則會報錯。因為 ++_cm 沒有用 mutable 修飾。
原文地址:https://www.cnblogs.com/Forever-Kenlen-Ja/p/3776991.html