参考


参考(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就是nn就是r,它们是同一对象的别名,也就是同一块内存且有两个名称,改变nr,都是改变该内存空间的信息,执行结果如下:

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; 
}




展开阅读全文