[C++ Primer Plus] 引用

認識引用

Declares a named variable as a reference, that is, an alias to an already-existing object or function. 來自: C++參考手冊

先從一個簡單的例子開始: 交換函數 swap()c++

// 代碼片斷01
void swap01(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

// 代碼片斷02
void swap02(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main(int argc, const char * argv[]) {

    int a= 10, b = 20;
    
    swap01(a, b); // 不會交換
    
    swap02(&a, &b); // 會交換

    return 0;
}

上述代碼彷佛不用解釋都知道它所要說明的問題。彷佛與今天的主題無關,可是不着急,看下 代碼片斷02 。內部充斥着 *a*b 這樣的操做,代碼彷佛不夠簡潔。有沒有什麼方式可使其更簡潔一些呢?有! 引用!!程序員

有了引用之後,代碼是這樣的:數組

void swap03(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}

int main(int argc, const char * argv[]) {

    int a= 10, b = 20;
    
    swap03(a, b); // 會交換
    
    return 0;
}

代碼看起來和上面沒有什麼大的差異。&a&b 彷佛是!在取地址。實際上不是的。它表示的 a 是某個 int 類型變量的引用(或者說某個 int 類型變量的別名)。函數

那麼如何建立一個 引用 呢?spa

int main(int argc, const char * argv[]) {

    int num = 10;
    
    int & ref = num; // 建立一個引用。
    
    int & refref = ref; // 建立一個引用

    return 0;
}

如上,建立的是 num 的引用,名字叫 ret既然是引用,就是說,使用 numret 沒有任何區別。 設計

沒有任何區別是什麼意思?意思就是說:numret 都是同一個內存地址的名稱。舉個小例子,如圖:
image-20190526182118680指針

num: 周樟壽
ref: 周樹人
refref: 魯迅

只是名字變了,但它都是同一個內存!!code

說到這裏,有沒有發現其實引用是什麼?像不像下面這樣:對象

int &ref = num;

// 等價於
int *ref = #

另外有一個須要注意的地方是: 引用一旦建立,便不能更改(只讀)!!既然不能修改,那就說明必須在建立時就要給引用初始化!!!。blog

int one = 10;
int two = 20;

// 錯誤01(ref建立時就要被初始化)
int &ref;
ref = one;
// 正確寫法:int &ref = one;

// 異常 02(引用一旦建立,便不能修改)
int &ref = one;
ref = two; // 這句話不會報錯,可是它表達的意思不是讓ref變成two的引用。而是將 two 的值,賦值給 ref。即,ref = 20;

至此,對引用有了一個基本的認識。總結一下:

  1. 引用是某個變量的別名。
  2. 引用一旦建立,便不能夠修改。
  3. 引用建立時,必需要初始化。

基於以上的觀點。咱們能夠對引用有一個本質上的認識,即指針常量

int & ref = num;

// 等價於
int * const ref = #

引用的做用

引用難道只是爲了簡化 *a 這樣的代碼,使代碼更簡潔麼? 不是的。引用來源於指針,但不一樣於指針。

引用最主要的用途是:用做函數的參數(尤爲是結構體或對象參數)!!

  • 做爲函數參數

    struct Student {
        std::string id;
        std::string name;
        int age;
    };
    
    void func01(Student student) { 
        // do something 
    }
    
    void func02(Student &student) { 
        // do something 
    }
    • func01 中會執行建立臨時的Student對象,並執行Student的數據拷貝(拷貝構造函數)。這是耗時和耗空間的。
    • func02 中使用的是Student的引用,所以操做的是同一塊內存區域,不會建立臨時對象,不耗時,也不佔用空間。
    • 【注意】 使用引用做爲參數時,常加上 const 修飾。理由以下:

      1. 避免無心中修改被引用的數據,如:

        int i = 5;
        
        int cube(int &a) { 
            a *= a * a; 
            return a; 
        }
        
        int sum(int &a) {
            a = a + 5;
            return a;
        }
        
        int tmp1 = cube(i); // 輸入時i= 5,輸出時i = 125
        int tmp2 = sum(i); // 輸入時i = 125,輸出時i = 130
      2. 可以處理 const非const 實參,不然只能接受 非const 數據,如:

        const int NUM = 100;
        
        int sum01(int &a) {
          // do something
          return 0;
        }
        
        int sum02(const int &a) {
            // do something
            return 0;
        }
        
        sum01(NUM);// ERROR!!!!
        sum02(NUM);// OK
      3. 使函數可以正確生成並使用臨時變量。(這種在早期C++較寬鬆的規則下才會出現)
  • 做爲函數返回值

    返回值是引用的函數,實際上返回的是被引用變量的「別名」

    有一個須要注意的地方是:不要返回臨時變量。以下:

    Student &clone(const Student &s) {
        Student student; // 局部變量,函數執行完成時會被銷燬。
        student.id = s.id;
        student.name = s.name;
        student.age = s.age;
        return student;
    }

    如上,會返回 student。可是 student 是局部的臨時變量,在 clone 函數執行完成後,將被銷燬,致使程序出現異常。避免這種問題有一些作法:

    1. 返回一個做爲參數傳遞給函數的引用。如:

      Student &clone(const Student &s) {
          return s;
      }
    2. new 來分配新的內存空間, 如:

      Student &clone(const Student &s) {
          Student *student = new Student();
          student->id = s.id;
          student->name = s.name;
          student->age = s.age;
          return *student;
      }

引用的使用建議

使用引用參數的主要緣由有兩個:

  • 程序員可以修改調用函數中的數據對象。
  • (最主要)經過傳遞引用而不是整個數據對象(如:結構體和類對象這種數據對象較大的對象),能夠提升程序的運行速度。

正如前面所說,引用來源於指針,但不一樣於指針。甚至引用像是指針的 "語法糖" 。那麼問題來了,何時使用引用,何時使用指針,何時使用按值傳遞呢???

對於使用傳遞的值而不做修改的函數

  • 若是數據對象比較小(如:基本數據類型或者小型的結構),選擇按值傳遞
  • 若是數據對象是數組,則使用指針,由於這是惟一的選擇,並將指針聲明爲指向 const 的指針
  • 若是數據對象是較大的結構,則使用指針或者引用,以提升程序的效率。這樣能夠節省複製結構所需的時間和空間
  • 若是數據對象是類對象,則使用 const 修飾的引用。類設計的語義經常要求使用語義,這是C++新增這項特性的主要緣由。所以傳遞類對象參數的標準方式是按引用傳遞。

對於修改調用函數中數據的函數

  • 若是數據對象是基本數據類型,則使用指針。
  • 若是數據對象是數組,則只能使用指針。
  • 若是數據對象是結構,則使用指針或者引用。
  • 若是數據對象是類對象,則使用引用。
相關文章
相關標籤/搜索