左值參照(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 的值不受影響, 是因為宣告成一般的變數, 這是需要特別注意的地方.
沒有留言:
張貼留言