C++中const與指標、引用的總結

csu_zhengzy~發表於2018-11-17

C++中函式的引數相比C語言中的函式引數要複雜的多,其中主要的原因是C++中引入了引用以及const限定符。這兩個物件的引入,使得C++中的函式引數變得異常的複雜多變,每一種型別都具有比較適合的使用範圍。

一,引用

引用是物件的別名,必須在初始化的過程中與一個具體的物件繫結起來,繫結完成以後就再也不能夠修改了,引用貌似和指標有很大的相似性,但是引用是引用,是一個別名,而指標是一個變數,只是變數中儲存的是物件的地址,引用並不分配新的記憶體空間。因此一個引用與一塊記憶體區域繫結,是這塊記憶體區域的別名,就如同陣列名一樣,陣列是記憶體中的一塊區域,陣列名是這塊區域的名字,但是在記憶體中並不存在額外的區域儲存陣列名。引用就是一塊記憶體區域的別名。C++中變數實質上就是一塊記憶體區域,我們在用&i就可以得到變數i的地址,也就是說變數是這塊區域的名字。對變數的操作實質上就是對這塊記憶體中內容的操作。別名是這塊區域的另一個名字。採用任何一個名字對這塊區域修改都會影響區域的內容。

int vari=10;
int &refr=vari;
vari=20;
cout<<refr<<endl;
refr=30;
cout<<vari;

上面的int &refr = vari實質上就是變數vari的別名,也就是儲存vari變數地址的別名,這個地址中儲存的是一個int型別的資料,資料可以通過vari來操作,可以修改。因為refr是一個別名,也可以通過該別名對這塊記憶體區域進行操作。也就是說別名的操作就是針對一塊特定的記憶體區域,這個通過變數操作的效果是一致的。

一.const修飾引用

關於const和引用在一起情況下也存在一些類似於指標的情況,但是畢竟引用相比指標要簡單,這時候情況也比較簡單.但是我認為分析引用應該與分析指標是一樣也存在類似的問題。但是引用就只有兩種,非const的引用和const的引用。

int num =10;
int &newname = num;
const int &newname = num;

引用主要是上面的兩種,這兩種的區別相對來說比較大,而且加入了const限定符以後,引用的能力往往變的更加的強大。

一般來說對於const物件而言,一般只能採用const引用,這與前面的const物件只能採用指向const物件的原因是一樣的,如果對引用沒有進行限定,可能會通過引用修改資料,這是不允許的。也就是說const引用與指向const物件的指標有一定的相似性,即不能通過這個引用或者指標來修改原來的資料。保證資料的const特性。也就是說非const引用不能引用const物件,如果不小心引用編譯器會出現下面的錯誤:

invalid initialization of reference of type ‘int&’ from expression of type ‘const int’

因此非const引用只能針對非const的同型別資料。這是需要注意的。比如string,和字串字面值都不能直接引用。因為型別不相同,這是在C++函式定義中經常出現的問題,在後期的博文中再分析。在引用中加入const的就是對於這個引用而言,不能通過自己來修改原始的資料,這與指向const的指標有很大的相似性.

但是往往const引用的初始化並不一定要去物件是const的,甚至可以是不同型別的物件,這種優越性是連指標(指標只能指向同一型別的資料,如果一定要指向需要強制型別轉換)都沒有的,也就是可以將不同型別的非const或者const物件來初始化一個const引用。但是這個const限定符就限定了該引用指向的物件是不能通過該引用來修改的。如果嘗試採用const的引用進行修改,編譯器會出現如下的錯誤:

error: assignment of read-only reference...

綜合上面的描述可知:非const引用只能繫結到該引用同型別(string和字串字面值(const char *)之間都不可以)的非const物件,而const引用則可以繫結到任意的一種物件上(非const、const、甚至不同型別),這種差別在函式的引數中有較大的體現。

二.const對指標的修飾

const對指標的修飾會更加複雜,對引用的修飾只有1種形式,二const對指標的修飾卻有很多種形式,但是實際上它們都有類似的效果。

int *ptr;
const int *ciptr; 
int const *icptr; 
int * const cptr; 
const int * const cicptr;

上面是關於const與指標結合時的各種情況,這並不只是C++中經常遇到的問題,在C語言中也會有類似的討論,雖然const並不是C語言中的關鍵字。

int * ptr  是指定義一個指向int 型別的指標ptr。

const int *ciptr  是指定義一個指向const int 型別的指標ciptr,這是const 限定的是(* ciptr),也就是對指標解引用,即const限定的就是資料本身,而非指標。所以ciptr是一個指向常int型資料的指標。

int const * icptr其實和上面的const int *ciptr是一致的因為const只是一個限定符,在int前還是後都 沒有影響,他限定的仍然是(*icptr),並不是icptr,也就是icptr也是指向常int型資料的指標,也就是說在icptr這個指標看來,它指向的資料是常數,是不能改變的,但是是否真的不能改變,需要依據實際的型別的分析,只是不能通過這個指標來改變。也就是說該指標是一個自以為自己指向常量的指標。

int * const cptr 這時候我們還是結合const的位置分析,const限定的是cptr,cptr我們可以知道是一個指標,也就是說const限定的是一個指標,而不是指標指向的資料,也就是說這種定義實質上是定義一個常指標,也就是指標指向的地址是不變的,也就是cptr是不能被賦值的,不能採用這個指標指向其他的物件或者地址。但是這個地址中的資料並不是常數,是可以改變的,可以通過對*cptr進行修改。這種指標實質上也就使得指標的效果大大減小,並不常用。

const int * const cicptr 這種定義結合上面的分析我們知道是一個指向常量的常指標,也就說這個指標指向的地址是一個常地址,指標不能指向其他的物件或者地址。同時對指標cicptr來說,我指向的這個地址中的內容也是不能修改的,是一個常量,是不能修改的,但是該地址的資料能否修改還需要進行實際的分析。

對於const型別的資料,如果需要定義指標,這時候只能定義const型別的資料,這是為什麼呢?因為對於const物件來說,資料是肯定不能修改的,如果定義指向非const的指標,程式設計師可能就會通過指標來修改物件的值,但是const物件是不能被修改的,肯定會出現錯誤。因此const的變數只能採用指向常量的指標來指向。一般來說如果將一個非const指標指向了一個const物件,編譯的過程中就會丟擲如下的錯誤:

invalid conversion from ‘const int*’ to ‘int*’

但是對於指向常量的指標並不一定指向的資料就是常量,這是一個非常重要的技術點,指向常量的指標指向的資料只是針對這個指標而言,他認為自己指向的資料是常量,休想通過他來修改指向的物件。也就是說指向常量的指標在初始化、賦值的時可以初始化或者賦值為非const變數的地址,即可以指向非const的物件,只是不能通過該指標來修改物件罷了。

同時需要注意:對於指向const的指標,初始化過程比較方便,不要求是const物件的地址,可以採用非const物件的地址初始化,甚至還可以採用指向非const的指標直接賦值初始化,這時指標自己認為自己指向的物件是常量。但是不能將指向const的指標直接賦值給指向非const的指標,如果不小心賦值也會出現上面出現的問題。下面參看一段小的程式碼,說明其中的一些問題。

總結:

const的使得引用與指標的變化更加複雜,總體而言,const主要是保證了通過指標或者引用不修改原始的資料,但是至於原始的資料是否可以修改,這就需要參看資料的型別。  

在存在const的物件中,只能採用包含限定符const的引用或者指向const的指標來操作。  const的引用比較強大,初始化的過程中可以採用任意的物件,const物件,非const物件,甚至其他型別的資料。const引用支援隱式型別轉換。而指向const的指標則不能,只能指向同一型別的資料,但是可以採用強制型別轉換,初始化或者賦值過程中對資料型別沒有要求,可以是const物件的地址,也可以是非const物件的地址。

const引用和指向const物件的指標都是自己以為自己指向的物件是不能修改的,採用const的指標或者引用就能避免原始資料修改。

相關文章