前言 在學習C++時,const關鍵字的知識點分散在書的各個章節。當我們嘗試在編程時使用const時,總會感覺有一些細節被遺忘,因而不能得心應手地使用const關鍵字。因此,本篇文章嘗試著對const關鍵字的做一些總結。參考書籍《C++ Primer Plus》 const總結 這裡是我做的關於co ...
前言
在學習C++時,const關鍵字的知識點分散在書的各個章節。當我們嘗試在編程時使用const時,總會感覺有一些細節被遺忘,因而不能得心應手地使用const關鍵字。因此,本篇文章嘗試著對const關鍵字的做一些總結。參考書籍《C++ Primer Plus》
const總結
這裡是我做的關於const關鍵字的一些總結,之後的各章便是對書中知識點的理解。
const
限定符創建的常量不可再次修改。- 創建常量時記得初始化。
const
創建的常量可以用來聲明數組長度。const int * p;
,p
指向常量,p
可修改,*p
不可修改。int * const p;
,p
指向變數,p
不可修改,*p
可修改。const
指針可以接受const
數據和非const
數據。- 非
const
指針僅可以接收非const
數據。 - 不允許將非
const
指針的地址賦值給const
指針。 const
引用創建臨時變數的兩種情況。const
全局變數是內部鏈接性,如static
。可用extern
更改鏈接性。可在頭文件中使用。- cv-限定符。
const
成員函數,void show() const;
,表示函數不會修改調用對象(類成員)。
const限定符
const關鍵字是C++中較為常用的一個關鍵字。當我們想創建一個符號常量時,按照C語言的習慣,我們一般會使用#define這種預處理器方法,例如#define ZERO 0
。但在C++中,提供了一種更好的處理符號常量的方法,那就是const關鍵字。
創建常量
創建一個常量的通用格式:const type name = value;
例如:
const int zero = 0; // 一個普通的常量·
這個例子中,被const修飾過的變數zero會變為常量。常量zero被初始化後,其值就被固定了,C++編譯器不允許再次修改常量的值。
常量初始化
這裡有一點需要註意:在用const聲明一個變數時,需要進行初始化。例如下麵的代碼是錯誤的:
const int zero;//聲明常量時需要進行初始化,否則zero的值未知。
zero = 0;//因為C++編譯器不允許再次修改常量的值。所以此處錯誤。
另外,常量可以用來作為聲明名數組時的元素數目。例如:
const int ten = 10;//創建了一個常量。
int array[ten];//用常量創建數組。
一級指針與const
在用const
修飾指針時則會出現一些很微妙的地方。在C++中,可以用兩種不同的方式將const關鍵字用於指針。第一種方法是讓指針指向一個常量,第二種方法是讓指針本身就是常量。其中,第一種方法可以防止使用該指針來修改所指向的值。而第二種方法可以防止改變指針所指的位置。例如:
int value = 0;
const int * p1 = &value; //第一種用法,防止利用p1修改value的值。
int * const p2 = &value; //第二種用法,p2本身不能再修改了。
這裡有一個特殊情況令人在意。如果將一個指向變數的指針指向一個常量會發生什麼?代碼如下:
const int value = 0;
int * p2 = &value; //這是錯誤的,C++禁止這樣的行為。
我們發現,value
是常量。但p2
是指向value
的,那麼我們可以通過* p2
對value
的值進行修改。可是做這樣的話const
的作用就失效了。
事實上,C++禁止這樣的用法。也就是說,C++禁止將const
常量的地址賦值給非const
指針。因此,上面的代碼是錯誤的。在邏輯上也很好解釋。
我們可以這樣理解:為保證常量不可再次修改的屬性,我們不能通過指針修改常量,因此,非const
指針僅可以接收非const
數據。與之類似,因為我們聲明瞭const
指針目的是不會通過當前指針修改其指向的數據,因此其指向的數據一直都是安全的,自然const
數據和非const
數據都可以。因此,const
指針可以接受const
數據和非const
數據。
二級指針使用const
的限制
關於二級指針與常量的關係有些複雜,我們來看下麵的代碼:
const int ** pp;//這是一個二級const指針
int *p;
const int value = 0;//這是一個常量
pp = &p; //這裡是錯誤的,雖然高亮沒有提示錯誤。
//錯誤C2440:初始化:無法從int **轉換為const int **。
*pp = &value;//兩個都是常量,賦值沒有問題
*p = 10; //通過p修改了value的值!
如果pp = &p
允許的話,那麼我們可以通過二級指針繞開const
的限制,如上訴代碼一樣。C++規定,僅當且只有一層間接關係(如指針指向基本數據類型)時,才能將非const
地址賦值給const
指針。也就是說,C++不允許將非const
指針的地址賦值給const
指針。
最後,關於const
與指針的關係,下麵還有幾個例子,請看:
const int value =0;//這是常量
const int *p1;//p1可變,*p1不可變
int * const p2 = &value;//p2不可變,*p2可變
const int ** pp3;//pp3可變,*pp3可變,**pp3不可變
int * const * pp4;//pp4可變,*pp4不可變,**pp4可變
int ** const pp5;//pp5不可變,*pp5不可變,**pp5可變
const int * const *const p6 = &p1;//pp6不可變,*pp6不可變,**pp6不可變
const
引用
我們在使用函數的時候,一般會使用引用形參。原因就是因為速度快,無需走複製的流程。當我們使用引用的時候,如果實參與引用參數不匹配,那麼C++將產生臨時變,關於const
引用卻有需要瞭解的知識點。
如果引用形參是const
,則C++編譯器將在下麵兩種情況下生成臨時變數:
- 實參的類型正確,但不是左值。
- 實參的類型不正確,但可以轉換為正確的類型。
左值:在C語言中,左值最初指的是可出現在賦值語句左邊的實體,但現在,常規變數和const變數都視為左值,因為可以通過地址訪問它們。
左值例子:變數,數組元素,結構成員,引用,解除引用的指針等。
非左值例子:字面常量(用引號括起的字元串除外,因為它們是地址),包含多項的表達式等。
代碼例子如下:
double refcube (const double &ra){return ra * ra * ra;}
double side = 3.0;
long edge = 5L;
double x1 = refcube(edge);//實參類型不正確,但可以轉換為正確的類型
double x2 = refcube(7.0);//實參類型正確,但不是左值(字面常量)
double x3 = refcube(side+10.0);//實參類型正確,但不是左值(表達式)
const
全局變數
在C++中,const
限定符對預設存儲類型稍有影響。預設情況下,C++全局變數的鏈接性是外部的,但const
全局變數的鏈接性為內部的。也就是說,在c++
看來,全局const
定義就像使用了static
說明符一樣。
C++這樣做有著很多的好處,這意味著每個文件都有自己的一組常量,而不是所有文件共用一組常量。因此我們可以將常量定義到頭文件中,這樣只要在兩個源代碼文件中包括同一個頭文件,則它們將獲得同一組常量。當然,如果我們希望某個常量的鏈接性為外部的,那麼我們可以使用extern
關鍵字來覆蓋預設的內部連接線。extern const int states = 50;
cv-限定符
const
volatile
const
限定符表明,記憶體被初始化後,程式便不會再對它進行修改。
volatile
限定符表明,即使程式沒有對記憶體單元進行修改,但其中的值也可能發生變化。可能由於硬體的原因,也可能由其他程式修改,如共用數據。這個關鍵字的作用主要是為了改善編譯器的優化能力。
防止編譯器將該值用緩存的方式進行優化。
mutable
限定符也是需要瞭解的。當我們聲明一個數據結構體為常量,而其中某個成員卻需要修改時。我們可以利用mutable
限定符對需要修改的成員加以修飾。例子如下:
struct Data{
int x;
mutable int y;//聲明此成員是可被再次修改的。
};
const Data data = {1,2};//data實例是常量
data.y=3;//但data的y成員可以被再次修改!
上述代碼中,data
的const
禁止程式修改data
的成員,但由於y
成員的mutable
限定符說明瞭data
的y
成員不受這種限制,仍然可以被再次修改。
const
成員函數
請看如下的代碼片段:
class Data{
int x;
public:
void show(){std::cout <<x;};
}
//
const Data data = {1};
data.show();//這裡會報錯哦!
C++編譯器在data.show();
會報錯,因為show();
函數無法確保調用對象不被修改。很有可能show
函數修改了data
中的成員,因此C++編譯器為了保證data
不會被再次修改,禁止了這種調用行為。在C++中,解決這類問題的辦法是const
成員函數。
請看下麵的代碼:
class Data{
int x;
public:
void show() const {std::cout <<x;};//這裡的const!
}
//
const Data data = {1};
data.show();//這裡不會報錯了
對於show()
的聲明應該類似於這樣:void show() const;
對於show()
的定義應該類似於這樣:void Data::show() const {...}
這種方式聲明和定義的類函數被稱為const
成員函數,const
成員函數表示函數不會修改調用對象(類成員)。
最後,本人才疏學淺,可能會有很多錯誤,還望諸君見諒。