C++中函數的參數相比C語言中的函數參數要複雜的多,其中主要的緣由是C++中引入了引用以及const限定符。這兩個對象的引入,使得C++中的函數參數變得異常的複雜多變,每一種類型都具備比較適合的使用範圍。
本文詳細的分析const與指針、引用在一塊兒存在狀況下的狀況分析。
首先介紹引用
引用是對象的別名,必須在初始化的過程當中與一個具體的對象綁定起來,綁定完成之後就不再可以修改了,引用貌似和指針有很大的類似性,可是引用是引用,是一個別名,而指針是一個變量,只是變量中保存的是對象的地址,引用並不分配新的內存空間。所以一個引用與一塊內存區域綁定,是這塊內存區域的別名,就如同數組名同樣,數組是內存中的一塊區域,數組名是這塊區域的名字,可是在內存中並不存在額外的區域保存數組名。引用就是一塊內存區域的別名。C++中變量實質上就是一塊內存區域,咱們在用&i就能夠獲得變量i的地址,也就是說變量是這塊區域的名字。對變量的操做實質上就是對這塊內存中內容的操做。別名是這塊區域的另外一個名字。採用任何一個名字對這塊區域修改都會影響區域的內容。
- int vari = 10;
- int &refr = vari;
- vari = 20;
- cout << refr << " " << vari << endl;
- refr = 30;
- cout << refr << " " << vari << endl;
上面的
int &refr = vari實質上就是變量vari的別名,也就是保存vari變量地址的別名,這個地址中保存的是一個int類型的數據,數據能夠經過vari來操做,能夠修改。由於refr是一個別名,也能夠經過該別名對這塊內存區域進行操做。也就是說別名的操做就是針對一塊特定的內存區域,這個經過變量操做的效果是一致的。
其次說說const限定符
const的引入使得在函數操做中的一些問題複雜了,可是更加的合理了,const限定符是指上是指一個常量。這個常量在通常的狀況下就是限定變量不能修改,可是當這個限定符與引用、指針結合在一塊兒時,使得不少的問題不在明朗。
一、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的指針,若是不當心賦值也會出現上面出現的問題。下面參看一段小的代碼,說明其中的一些問題。
- #include <iostream>
- #include <string>
- #include <vector>
-
- using namespace std;
-
- int main()
- {
- int num = 20;
- const int array_size = 10;
-
-
- int *pnum = #
- const int * cpnum = #
- /*const int *指針能夠採用int *指針直接初始化*/
- const int *csize1 = pnum;
-
-
- /*可是int * 指針不能採用const int *製做初始化*/
- //int *psize = &array_size;
- /*const類型數據只能採用指向const的指針來指向*/
- const int *csize = &array_size;
-
- cout << "Before change..." << endl;
- cout << "The num of num = " << num << endl;
- cout << "*pnum = " << *pnum << " "
- << "*cpnum = " << *cpnum << " "
- << "csize1 = " << *csize1 << endl;
-
- num = 30;
-
- cout << "After changed..." << endl;
- cout << "The num of num = " << num << endl;
- cout << "*pnum = " << *pnum << " "
- << "*cpnum = " << *cpnum << " "
- << "csize1 = " << *csize1 << endl;
-
- return 0;
- }
從上面的結果咱們能夠知道指向const的指針能夠採用非const變量的地址進行初始化或者賦值操做,同時也能夠採用指向非const指針直接初始化指向const的指針。同時指向const的指針不能賦值給指向非const的指針,會出現const int *不能轉換到int *的問題。
三、const與引用
關於const和引用在一塊兒狀況下也存在一些相似於指針的狀況,可是畢竟引用相比指針要簡單,這時候狀況也比較簡單.可是我認爲分析引用應該與分析指針是同樣也存在相似的問題。可是引用就只有兩種,非const的引用和const的引用。
- int num = 10;
-
- int &newname = num;
- const int &othername = 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、甚至不一樣類型),這種差異在函數的參數中有較大的體現。
經過下面的例子來講明一下上面的分析:
- #include <iostream>
- #include <string>
- #include <vector>
-
- using namespace std;
-
- int main()
- {
- int num = 20;
- const int array_size = 10;
-
- int &pnum = num;
- const int &cpnum = num;
- /*採用引用直接初始化const類型的引用*/
- const int &csize1 = pnum;
-
- /*const的變量不能採用非const的引用*/
- //int &psize = array_size;
- /*const類型數據只能採用指向const的指針來指向*/
- const int &csize = array_size;
-
- cout << "Before change..." << endl;
- cout << "The num of num = " << num << endl;
- cout << "pnum = " << pnum << " "
- << "cpnum = " << cpnum << " "
- << "csize1 = " << csize1 << endl;
-
- num = 30;
- cout << "After the first changed..." << endl;
- cout << "The num of num = " << num << endl;
- cout << "pnum = " << pnum << " "
- << "cpnum = " << cpnum << " "
- << "csize1 = " << csize1 << endl;
-
- /*經過引用修改變量的值*/
- pnum = 40;
- cout << "After the second changed..." << endl;
- cout << "The num of num = " << num << endl;
- cout << "pnum = " << pnum << " "
- << "cpnum = " << cpnum << " "
- << "csize1 = " << csize1 << endl;
-
- /*不能採用const的引用修改對象,
- *這與指向const的指針特性的類似處*/
- /*
- csize1 = 50;
- cout << "After the second changed..." << endl;
- cout << "The num of num = " << num << endl;
- cout << "pnum = " << pnum << " "
- << "cpnum = " << cpnum << " "
- << "csize1 = " << csize1 << endl;
- */
-
- double dnum = 10.1;
- /*非const的引用只能綁定相同類型的對象*/
- //int &dname = dnum;
-
- /******************************************
- *const引用能夠綁定不一樣類型的對象,
- *所以const引用就能更加方便的做爲函數的形參
- *******************************************/
- const int &dothername = dnum;
-
- return 0;
- }
上面的實驗結果基本上符合分析的結論。
總結
const的使得引用與指針的變化更加複雜,整體而言,const主要是保證了經過指針或者引用不修改原始的數據,可是至於原始的數據是否能夠修改,這就須要參看數據的類型。
在存在const的對象中,只能採用包含限定符const的引用或者指向const的指針來操做。
const的引用比較強大,初始化的過程當中能夠採用任意的對象,const對象,非const對象,甚至其餘類型的數據。const引用支持隱式類型轉換。而指向const的指針則不能,只能指向同一類型的數據,可是能夠採用強制類型轉換,初始化或者賦值過程當中對數據類型沒有要求,能夠是const對象的地址,也能夠是非const對象的地址。
const引用和指向const對象的指針都是本身覺得本身指向的對象是不能修改的,採用const的指針或者引用就能避免原始數據修改。