當形參是const時,必需要注意關於頂層const的討論。如前所述,頂層const的做用於對象自己:函數
const int ci=42; //不能改變ci,const是頂層的spa
int i=ci; //正確:當拷貝ci時,忽略了它的頂層const指針
int *const p=&i; //const是頂層的,不能給p賦值對象
*p=0; //正確:經過p改變對象的內容是容許的,如今i變成了0ci
和其餘初始化過程同樣,當用實參初始化形參時會忽略掉頂層const。換句話說,形參的頂層const被忽略掉了。當形參有頂層const時,傳給它的常量對象或者很是量對象都是能夠的:字符串
void fcn(const int i){ /*fcn可以讀取i,可是不能向i寫值*/}string
調用fcn函數時,既能夠傳入const int也能夠傳入int。忽略掉形參的頂層const能夠產生意想不到的結果:變量
void fcn(const int i) {/*fcn可以讀取i,可是不能向i寫值*/}引用
void fcn(int i) {/*....*/}//錯誤:重複定義了fdn(int)語言
在C++語言中,容許咱們定義若干具備相同名字的函數,不過前提是不一樣函數的形參列表應該有明顯的區別。由於頂層const被忽略了,因此在上面的代碼中傳入兩個fcn函數的參數能夠徹底同樣。所以第二個fcn是錯誤的,儘管形式上由差別,但實際上它的形參和第一個fcn的形參沒什麼不一樣。
指針或引用形參與const
形參的初始化方式和變量的初始化方式是同樣的,因此回顧通用的初始化規則有助於理解下面的知識。咱們可使用很是量初始化一個底層的const對象,可是反過來不行;同時一個普通的引用必須用同類型的對象初始化。
int i=42;
const int *cp=&i; //正確:可是cp不能改變i
const int &r=i; //正確:可是r不能改變i
const int &r2=42; //正確
int *p=cp; //錯誤:p的類型和cp的類型不匹配
int &r3=r; //錯誤:r3的類型和r的類型不匹配
int &r4=42; //錯誤:不能用字面值初始化一個很是量引用
將一樣的初始化規則應用到參數傳遞上可得以下形式:
int i=0;
const int ci=i;
string::size_type ctr=0;
void reset(int &i);
reset(&i); //調用形參類型是int *的reset函數
reset(&ci); //錯誤:不能用指向const int對象的指針初始化int *
reset(i); //調用參數類型是int&的reset函數
reset(ci); //錯誤:不能把普通引用綁定到const對象ci上
reset(42); //錯誤:不能把普通引用綁定到字面值上
reset(ctr); //錯誤:類型不匹配,ctr是無符號類型
//find_char的第一個形參是對常量的引用
find_char("hello world",'o',ctr);//能夠綁定到字面值常量上
要想調用引用版本的reset,只能使用int類型的對象,而不能使用字面值、求值結果爲int的表達式、須要轉換的對象或者const int類型的對象。相似的,要想調用指針版本的reset只能使用int*。
另外一方面,咱們能傳遞一個字符串字面值做爲find_char的第一個實參,這是由於改函數的引用形參是常量引用,而C++容許咱們用字面值初始化常量引用。
儘可能使用常量引用
把函數不會改變的形參定義成(普通的)引用是一種比較常見的錯誤,這麼作帶來給函數的調用者一種誤導,即函數能夠修改它的實參的值。此外,使用引用而很是量引用也會極大地限制函數所能接受的實參類型。就像剛剛看到的,咱們不能把const對象、字面值或者須要類型轉換的對象傳遞給普通的引用形參。