# 技術|C++ |C++ rvalue 和 std::move ## lvalue - 具名的、可定位的對象 ```c++= int x = 5; // x 是 lvalue,因為它是一個具名的變量 x = 10; // 可以對 x 賦值,修改它的值 ``` - lvalue reference: 可以綁定到一個lvalue ```c++= int a = 5; int& ref = a; // ref 是 a 的 lvalue 引用 ``` ## rvalue 臨時的、不可取址的`值` ```c++= int y = 5; // 5 是一個 rvalue,因為它是臨時的常量,無法通過地址修改 y = x + 3; // (x + 3) 是 rvalue,它的結果不再持續存在 ``` - rvalue reference: 允許綁定到rvalue ```c++= int&& temp = 10; // temp 是一個 rvalue 引用 ``` ## 移動語意 - 為了減少不必要的拷貝操作,C++11 引入了移動語意,讓我們可以移動資源而不是拷貝它們。 - 為了支持移動語意,你需要在類中實現`移動構造函數`和`移動賦值運算符` ```c++= class MyClass { int* data; int size; // 需要記住 size,方便複製操作 public: // 構造函數 MyClass(int size) : size(size) { data = new int[size]; // 分配內存 } // 複製構造函數 : 深拷貝 MyClass(const MyClass& other) : size(other.size) { data = new int[other.size]; for (int i = 0; i < size; ++i) { data[i] = other.data[i]; } } // 複製賦值運算符 : 深拷貝 MyClass& operator=(const MyClass& other) { if (this != &other) { // 釋放現有的內存 delete[] data; // 分配新內存並進行深拷貝 size = other.size; data = new int[size]; for (int i = 0; i < size; ++i) { data[i] = other.data[i]; } } return *this; } // 移動構造函數 : 避免 copy MyClass(MyClass&& other) noexcept : data(other.data), size(other.size) { other.data = nullptr; other.size = 0; } // 移動賦值運算符 : 避免 copy MyClass& operator=(MyClass&& other) noexcept { if (this != &other) { // 釋放現有的內存 delete[] data; // 移動數據 data = other.data; size = other.size; other.data = nullptr; other.size = 0; } return *this; } // 析構函數 ~MyClass() { delete[] data; // 釋放資源 } }; ``` - 移動語意主要應用於: - 容器類別(如 vector、string):當容器中元素是臨時對象時,移動語意可避免不必要的深拷貝。 - 函數返回值優化(RVO):通過移動語意返回臨時對象,直接轉移資源。 - 資源管理:如動態內存、文件句柄、網絡連接等資源,通過移動語意可以避免資源的重複分配和釋放。 - 當呼叫移動函數 `std::move` 後 - caller 不應該再繼續使用該物件 - 實作應該把 rhs 設定成即使 caller 呼叫 destructor 也不會有問題的狀態 (如設成 nullptr) ## std::move - 在 C++ 中,默認情況下變量是 lvalue - `移動語意只會作用於 rvalue(臨時對象)`,std::move 可以顯式地將該對象轉換為 rvalue reference。 - 使用者透過是否呼叫 `std::move()` 決定要帶入左值或右值,以呼叫正確的`複製 or 移動函數` ```c++= int main() { MyClass obj1(10); // 複製構造 MyClass obj2 = obj1; // 複製賦值 obj2 = obj1 // 移動構造 MyClass obj3 = std::move(obj1); // 移動賦值 obj3 = std::move(obj1); } ``` - 以 STL 為例,他實作兩個版本,使用 move 可以帶來效能提升 ```c++= vec.push_back(str1); // 傳統方法,copy vec.push_back(std::move(str1)); // 使用移動以提升性能 ``` - [一文讀懂C++右值引用和std::move](https://zhuanlan.zhihu.com/p/335994370) - [移動語意 std::move() 的用途與原則](https://charlottehong.blogspot.com/2017/03/stdmove.html)