const形參
當形參是const時,要注意頂層const:
const int ci = 42; //不能改變ci,const是頂層的
int i = ci; //正確,當拷貝ci時,忽略了它的頂層const
int *const p = &i; //const是頂層的,不能給p賦值
*p = 0; //正確,通過p改變對象的內容是允許的,現在i變成0
當使用實參初始化形參時會忽略掉頂層的econst,也就是說形參的頂層const被忽略掉。當形參是頂層const時,傳給它常量對象或者非常量對象都是可以的:
void fcn(const int i) {/*fcn能夠讀取i,但是不能向i寫值*/}
調用fcn函數時,既可以傳入const inr也可以傳入int。
忽略掉形參的頂層const可能產生意想不到的結果:
void fcn(const int i); void fcn(int i);
C++語言,允許定義若干具有相同名的函數,不過前提是不同函數的形參列表應該有明顯的區別。因為頂層const被忽略掉,所以在上面的代碼中傳入兩個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; reset(&i); //調用形參類型是int*的reset函數
reset(&ci); //錯誤:不能用指向const int對象的指針初始化int*
reset(i); //調用形參類型是int&的reset函數
reset(ci); //錯誤,不能把普通引用綁定到const對象的ci上
reset(42); //錯誤,不能把普通引用綁定到字面值上
reset(ctr); //錯誤,類型不匹配,ctr是無符號類型
數組形參
數組的兩個特殊性質對我們定義和使用作用在數組上的函數有影響,這兩個性質分別是:不允許拷貝數組以及使用數組時會將其轉換成指針。因為不能拷貝數組,所以無法以值傳遞的方式使用數組參數。因為數組會被轉換成指針,所以為函數傳遞一個數組時,實際上傳遞的是指向數組首元素的指針。
數組形參的定義
如果要編寫一個函數,輸出 int 型數組的內容,可用下面三種方式指定數組形參:
// three equivalent definitions of printValues
void printValues(const int*) { /* ... */ } void printValues(const int[]) { /* ... */ } void printValues(const int[10]) { /* ... */ }
雖然不能直接傳遞數組,但是函數的形參可以寫成數組的形式。雖然形參表示方式不同,但可將使用數組語法定義的形參看作指向數組元素類型的指針。上面的三種定義是等價的,形參類型都是const int*。
通常,將數組形參直接定義為指針要比使用數組語法定義更好。這樣就明確地表示,函數操縱的是指向數組元素的指針,而不是數組本身。由於忽略了數組長度,形參定義中如果包含了數組長度則特別容易引起誤解。
使用標記指定數組長度
管理數組實參的第一種方法是要求數組本身包含一個結束標記,使用這種方法的典型示例是C風格字符串,C風格字符串存儲在字符數組中,並且最后一個字符后面跟着一個空字符函數在處理C風格字符串時遇到空字符停止:
void print(const char*cp) { if (cp) while (*cp) cout << *cp++; }
這種方法適用於某些有明顯結束標記且該標記不會與普通數據混淆的情況,但是對於像int這樣所有值都是合法值得數據就無效了。
使用標准庫規范
管理數組實參的第二種技術是傳遞指向數組的首元素和尾后元素的指針:
void print(const int*beg, const int *end) { while (beg != end) cout << *beg++ << endl; }
為了調用這個函數,必須傳入兩個指針,一個指向要輸出的首元素,另一個指向尾巴元素的下一位置。
顯示傳遞一個表示數組大小的形參
第三種管理數組實參的方法就是專門定義一個表示數組大小的形參:
void print(const int ia[], size_t size) { for (size_t i = 0; i != size; ++i) cout << ia[i] << endl; }
數組形參和const
上面的三個print函數都把數組形參定義成了指向const的指針,當函數不需要對數組元素執行寫操作的時候,數組形參應該指向const的指針。
數組引用形參
和其他類型一樣,數組形參可聲明為數組的引用。如果形參是數組的引用,編譯器不會將數組實參轉化為指針,而是傳遞數組的引用本身。在這種情況下,數組大小成為形參和實參類型的一部分。編譯器檢查數組的實參的大小與形參的大小是否匹配:
// ok: parameter is a reference to an array; size of array is fixed
void printValues(int (&arr)[10]) { /* ... */ } int main() { int i = 0, j[2] = {0, 1}; int k[10] = {0,1,2,3,4,5,6,7,8,9}; printValues(&i); // error: argument is not an array of 10 ints
printValues(j); // error: argument is not an array of 10 ints
printValues(k); // ok: argument is an array of 10 ints
return 0; }
這個版本的 printValues 函數只嚴格地接受含有 10 個 int 型數值的數組,這限制了哪些數組可以傳遞。然而,由於形參是引用,在函數體中依賴數組的大小是安全的:
// ok: parameter is a reference to an array; size of array is fixed
void printValues(int (&arr)[10]) { for (size_t i = 0; i != 10; ++i) { cout << arr[i] << endl; } }
&arr 兩邊的圓括號是必需的,因為下標操作符具有更高的優先級:
f(int &arr[10]) // error: arr is an array of references
f(int (&arr)[10]) // ok: arr is a reference to an array of 10 ints
多維數組的傳遞
所謂多維數組實際是指數組的數組。
和其他數組一樣,多維數組以指向 0 號元素的指針方式傳遞。多維數組的元素本身就是數組。除了第一維以外的所有維的長度都是元素類型的一部分,必須明確指定:
// first parameter is an array whose elements are arrays of 10 ints
void printValues(int (matrix*)[10], int rowSize);
上面的語句將 matrix 聲明為指向含有 10 個 int 型元素的數組的指針。
*matrix 兩邊的圓括號是必需的:
int *matrix[10]; // array of 10 pointers
int (*matrix)[10]; // pointer to an array of 10 ints
我們也可以用數組語法定義多維數組。與一維數組一樣,編譯器忽略第一維的長度,所以最好不要把它包括在形參表內:
// first parameter is an array whose elements are arrays of 10 ints
void printValues(int matrix[][10], int rowSize);
這條語句把 matrix 聲明為二維數組的形式。實際上,形參是一個指針,指向數組的數組中的元素。數組中的每個元素本身就是含有 10 個 int 型對象的數組。