參考(Reference)是物件的別名(Alias),也就是替代名稱,對參考名稱存取時該有什麼行為,都參考了來源物件該有的行為,在 C++ 中,「物件」這個名詞,不單只是指類別的實例,而是指記憶體中的一塊資料。
要定義參考,是在型態關鍵字後加上 &
運算子,例如:
int n = 10; // 定義變數
int *p = &n; // 定義指標,儲存 n 的位址
int &r = n; // 定義參考,是 n 的別名
上面的程式中,最後一行定義參考,參考一定要初始,例如以下定義無法通過編譯:
int &r; // error: 'r' declared as reference but not initialized
參考必須要有物件可以參考,因此一定要初始,初始後就是被參考物件的別名,對參考的任何存取,都是對物件的操作。例如:
#include <iostream>
using namespace std;
int main() {
int n = 10;
int &r = n;
cout << "n:" << n << endl
<< "r:" << r << endl;
r = 20;
cout << "n:" << n << endl
<< "r:" << r << endl;
return 0;
}
在上面的程式中,r
就是 n
,n
就是 r
,它們是同一物件的別名,也就是同一塊記憶體且有兩個名稱,改變 n
或 r
,都是改變該記憶體空間的資訊,執行結果如下:
n:10
r:10
n:20
r:20
參考的物件也可以是個指標,只要指定對應的型態,例如參考至 int*
型態的變數:
int n = 10;
int *p = &n;
int *&r = p; // 也就是 int* &r = p;
陣列呢?必須指定長度,例如參考長度為 1 的一維陣列:
int arr[] = {1, 2};
int (&r)[2] = arr;
參考的應用之一,是作為函式呼叫時傳遞引數的一種方式,這之後文件再來討論;回頭來看看〈二維(多維)陣列〉中 for range 的範例:
#include <iostream>
using namespace std;
int main() {
int maze[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
for(auto row : maze) {
for(int i = 0; i < 3; i++) {
cout << row[i] << "\t";
}
cout << endl;
}
return 0;
}
當時談到,row
的型態實際上是 int*
,row
儲存的只是位址,透過它只能依 int*
型態進行位址位移,然而它不帶有陣列的其他資訊,也就無法以 begin(row)
、end(row)
來操作,因此範例中內層 for
只能用索引來取得元素。
上面這個範例,展開來就像是以下:
#include <iostream>
using namespace std;
int main() {
int maze[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
for(int (*it)[3] = begin(maze); it < end(maze); it++) {
int *row = *it;
for(int i = 0; i < 3; i++) {
cout << row[i] << "\t";
}
cout << endl;
}
return 0;
}
也就是說,實際上 it
是陣列變數的位址,也就是 it
遞增過程會是 &maze[0]
、&maze[0]
,*it
的話,就會是 maze[0]
、maze[1]
,也就是一維陣列位址,若能直接參考至 *it
,就可以在內層迴圈也使用 begin(row)
、end(row)
:
#include <iostream>
using namespace std;
int main() {
int maze[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
for(int (*it)[3] = begin(maze); it < end(maze); it++) {
int (&row)[3] = *it; // 使用參考
for(auto offset = begin(row); offset < end(row); offset++) {
int n = *offset;
cout << n << "\t";
}
cout << endl;
}
return 0;
}
不過這程式碼難以閱讀,改為 for range 並搭配 auto
就好多了:
#include <iostream>
using namespace std;
int main() {
int maze[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
for(auto &row : maze) { // 使用參考
for(auto n : row) {
cout << n << "\t";
}
cout << endl;
}
return 0;
}