2018年9月30日 星期日

C++ 左值參照(&)及右值參照(&&)

左值參照(lvalue reference  &)可以當成變數的別名, 右值參照(rvalue reference &&)一般用在記憶暫存值, 兩者都需事先指定好記憶空間才能參照, 參照一詞隱函了指標的語意, 參考下面程式碼:
// a.cpp
#include <stdio.h>
int main( ) {
       int a = 3;     // normal variable
       int &b = a;  // lvalue reference of a
       int &&c = a + 1; // rvalue reference of a+1
       printf("a=%d b=%d c=%d\n", a, b, c);
       b = 1;   
       printf("a=%d b=%d c=%d\n", a, b, c);
       c = 6;
       printf("a=%d b=%d c=%d\n", a, b, c);
}
執行 g++   a.cpp && ./a.out
a=3 b=3 c=4
a=1 b=1 c=4
a= 1 b=1 c=6
初始化變數 a 等於 3, b 是 a 的左值參照, 因此 b 是 a 的別名, 其值等於 a, 而 c 是 a+1 的右值參照, 記憶空間位在暫存區, 其值等於 4 ,  a 及 b 參考到相同位置, 因此改變 b 相當於改變 a 的值, 但 c 參照的是 a + 1 的暫存空間, 雖然 a 已被改變, 但不影響到 c 的初始值 4,  而且 c 還可以利用 a + 1 的暫存空間直接改變值成為 6, 省下重新分配記憶空間的程序, 同時也不影響到 a 或 b 的值, 這是右值參照特殊的地方. 一個物件可以經由標準程式庫的 move( ) 程序(move semantics), 將物件強制轉化為右值參照的型別, 也就是轉化物件讓右值參照也變成別名使用
// b.cpp
#include <stdio.h>
#include <iostream>
using namespace std;
int main( ) {
    int a = 3;
    int &b = a;
    int &&c = move(a);
    printf("a=%d b=%d c=%d\n", a, b, c);
    b = 1;       
    printf("a=%d b=%d c=%d\n", a, b, c);
    c = 6;
    printf("a= %d b=%d c=%d\n",a,b,c);
}
執行 g++  b.cpp && ./a.out
a=3 b=3 c=3
a=1 b=1 c=1
a= 6 b=6 c=6
上述可以看到不管是改變 b 或 c, 同時 a 也跟著改變, 通常 move semantics 是用在函式輸入參數不想複製整個物件而宣告成右值參照時, 因右值參照(視為暫存)宣告導致傳進左值參照會不合法, 於是可以利用 move semantics 將物件轉化成右值參照再傳給函式, 兼顧了無需複製的效能也完成了函式該有的功能
// c.cpp
#include <stdio.h>
#include <iostream>
using namespace std;
void ss(int &&temp) { // using rvalue reference
    temp = temp*2;
}
int main() {
    int a = 3;
    int &b = a;
    int &&c = move(a);
    int d   = move(a);
    printf("a= %d b=%d c=%d d=%d\n",a,b,c, d);
    b = 1;      
    printf("a= %d b=%d c=%d d=%d\n",a,b,c, d);
    c = 6;
    printf("a= %d b=%d c=%d d=%d\n",a,b,c, d);
    ss(move(c));
    printf("a= %d b=%d c=%d d=%d\n",a,b,c, d);
}執行 g++  c.cpp && ./a.out
a= 3 b=3 c=3 d=3
a= 1 b=1 c=1 d=3
a= 6 b=6 c=6 d=3
a= 12 b=12 c=12 d=3
上述使用 move semantics 強制將 c 轉化為右值參照的型別傳給函式, 在函式內部改變了右值參照的內容, 因為是指標的關係, 因此它同時也更動到主體(a, b, c)的內容, 而 d 的值不受影響, 是因為宣告成一般的變數, 這是需要特別注意的地方.

沒有留言: