C++ 右值引用和移動語義
阿新 • • 發佈:2022-03-06
左值
左值:一個表示資料的表示式(如變數名或者解除引用的指標),程式可從堆疊上獲取其地址。最初,左值可出現在賦值語句的左邊,但是在有了const修飾符後,可以宣告一個沒有賦值的識別符號並獲取其地址。
//左值的例子 int n; int *pt = new int; const int b = 101; int & rn = n; int & rt = *pt; const int & rb = b; int &var = 10; // error const int &var = 10; // right (const修飾符後,宣告一個沒有賦值的識別符號並獲取其地址) int func(){ int a = 10; return a; } int &var1 = func(); // error cosnt int &var1 = func(); // right (但我們想要之後修改var1的值就無法修改了)
左值引用要求右邊的值必須能夠取地址,如果無法取地址,可以用常引用;如最後兩個例子所示;
但使用常引用後,我們只能通過引用來讀取資料,無法去修改資料,因為其被const修飾成常量引用了。
故C++11新增了右值引用;
右值
右值包括字面常量(c-風格字串除外,他表示地址)、諸如x+y等表示式以及函式的返回值(函式的返回值不是引用);
int x = 10; int y = 23; int &&r1 = 13; int &&r2 = x+y; // (r2關聯到的是23,即使後來修改了x、y,也不會使r2變化) double &&r3 = std::sqrt(2.0);
移動語義
vector<int> getVector(int num){
return vector<int>(num,0);
}
int main(){
vector<int> res = getVector(1000); // 移動語義
}
在上面一個例子,其實已經使用到了移動語義vector<int>(vector<int> &&v)
;
但如果我們只考慮普通的拷貝建構函式,那麼就需要將這個陣列在返回值(指的是返回的臨時物件而非res)的基礎上在拷貝一份,然後再將返回值拋棄,做了大量的無用功;那麼我們為何不直接把返回值中的資料的所有權直接移動到res中。
要實現移動語義,需要採取某種方式,讓編譯器知道什麼時候需要賦值,什麼時候不需要,而右值引用此時發揮了作用。
class U{
public:
U(const U& f); // 拷貝建構函式
U(U && f); // 移動建構函式
U &operator=(const U& f)const; // 拷貝賦值
U &operator=(U &&f); // 移動賦值
U operator+(const U&f)const;
};
int main(){
U u1;
U u2 = u1; //拷貝建構函式
U u3(u1+u2); //移動建構函式
}
強制移動
通常移動建構函式和移動運算子使用右值,但一定要他們使用左值的時候,可以使用std::move()
// 如我們需要在一個候選陣列中挑一個物件使用,並丟棄陣列;
U choices[10];
U best;
ini pick;
// ... 進行選擇, 將pick設定為目標索引
best = std::move(choices[pick]);
特殊的成員函式
C++11中特殊的成員函式有6個:
- 預設建構函式
- 拷貝建構函式
- 拷貝賦值運算子
- 移動建構函式
- 移動複製運算子
- 解構函式
可以使用default設定預設方法和使用delete禁用方法
class Someclass{
public:
Someclass()=default; // 使用編譯器給定的預設構造
Someclass(const Someclass &)=delete; // 禁止複製物件
Someclass & operator=(const Somelcass &)=delete; // 禁止複製物件
...
};