參考:https://blog.csdn.net/HR_Reborn/article/details/130363997
#pragma once class Array { public: Array() : size_(0), data_(nullptr){ } Array(int size) : size_(size) { data_ = new int[size_]; } // 複製構造函數 (深拷貝構造) Array(const Array& temp_array) { size_ = temp_array.size_; data_ = new int[size_]; for (int i = 0; i < size_; i++) { data_[i] = temp_array.data_[i]; } } // 賦值構造函數 (深拷貝賦值) Array& operator=(const Array& temp_array) { delete[] data_; size_ = temp_array.size_; data_ = new int[size_]; for (int i = 0; i < size_; i++) { data_[i] = temp_array.data_[i]; } //返回對象的引用,是爲了做鏈式處理: f1 = f2 = f3; return *this; } // 移動構造函數 Array(Array&& temp_array) { cout << "移動構造函數\n"; data_ = temp_array.data_; temp_array.data_ = nullptr;// 爲防止temp_array析構時delete data,提前置空其data_ } // 移動賦值構造函數 Array& operator=(Array&& temp_array) { cout << "移動賦值構造函數\n"; data_ = temp_array.data_; temp_array.data_ = nullptr;// 爲防止temp_array析構時delete data,提前置空其data_ return *this; } ~Array() { delete[] data_; } public: int *data_; int size_; }; // --> 測試 完美轉發 void B(int&& ref_r) { ref_r = 1; } // A、B的入參是右值引用 // 有名字的右值引用是左值,因此ref_r是左值 void A(int&& ref_r) { //B(ref_r); // 錯誤,B的入參是右值引用,需要接右值,ref_r是左值,編譯失敗 B(std::move(ref_r)); // ok,std::move把左值轉爲右值,編譯通過 B(std::forward<int>(ref_r)); // ok,std::forward的T是int類型,屬於條件b,因此會把ref_r轉爲右值 } void func1(int& i){ cout << "參數是左值," << i << endl; } void func1(int&& i){ cout << "參數是右值," << i << endl; } void func(int&& i){ //注意:形參 i是左值 func1(i);//這樣怎麼都是調用的 左值引用的 函數。爲解決,用完美轉發std::forward } // 1.如果模板中(包括類模板、函數模板)的參數爲 T&& 參數名,那麼函數既可以接受左值引用,也可以接受右值引用。 //2. std::forward<T>(參數), 用於轉發參數。若參數是左值,轉發後仍然是左值,若參數是右值,轉發後仍然是右值。 template<class T> void func(T&& t){ func1(std::forward<T>(t)); } // <-- 測試 完美轉發 class TestRightValueRef { public: TestRightValueRef() = delete; ~TestRightValueRef() = delete; static void testAll(){ //test02(); test03(); } static int foo(){ return 0; } static void test01(){ /* 左值,lvalue,就是賦值符號左邊的值,如a=5,a就是左值,但是準確的來說,左值是表達式(不一定是賦值表達式)後仍然存在的持久對象。 右值,rvalue,右邊的值,是指表達式結束就不存在的臨時對象。 純右值,prvalue,用於計算的或者用於初始化對象的的右值。例如 int a = 5; // 5是右值 注意 字符串 "abc" 這個不是右值,是指針 ( 在介紹將亡值之前對左右和右值做個總結,有地址的變量就是左值,沒有地址的字面值、臨時值就是右值。) 將亡值,xvalue, 是C++11爲了引入右值引用而提出的概念,與純右值的不同點在於,將亡值是即將被銷燬、卻能夠被移動的值。例如,函數中返回的對象。 有地址的變量就是左值,沒有地址的字面值或臨時值就是右值。 */ /* 左值引用:只能指向左值。不能指向右值的就是左值引用 右值引用:只能指向右值,不能指向左值,符號是&& */ int a = 5;// a 是左值, 5是右值 int b = foo();// b 是左值, foo()是右值 int& ra = a; //左值引用 int& rb = b; //左值引用 int&& rc = 5; //右值引用 int&& rd = foo(); //右值引用 //int& r = 5; //不能指向右值,編譯報錯 //int&& r = a; //不能指向左值,編譯報錯 //但const 左值引用 都可以 const int& rf1 = a; const int& rf2 = 3; const int& rf3 = foo(); //爲什麼?因爲const 左值引用不會修改指向值,因此可以指向右值,這也是爲什麼要使用const &作爲函數參數的原因之一,如std::vector的push_back: //void push_back(const value_type& val); //如果沒有const,vec.push_back(5)這樣的代碼就無法編譯通過了。 //左值引用默認只能指向左值,但是加了const的情況下可以指向右值,那麼右值引用有沒有類似的機制來指向左值呢?有的,就是std::move。 //std::move 唯一的功能是把左值強制轉換爲右值,可以讓右值引用指向左值,等於一個強制類型轉換。 int&& rg = std::move(a); //注意,被聲明出來的左、右值引用都是左值,因爲被聲明出的左右值引用是有地址的,也位於等號左邊。 // 所以,ra,rb,rc,rd 都是左值 /* 總結: 從性能上講,左右值引用沒有區別,傳參使用左右值引用都可以避免拷貝。 右值引用可以直接指向右值,也可以通過std::move指向左值;而左值引用只能指向左值(const左值引用也能指向右值)。 作爲函數形參時,右值引用更靈活。雖然const左值引用也可以做到左右值都接受,但它無法修改,有一定侷限性 void f(const int& n) { n += 1; // 編譯失敗,const左值引用不能修改指向變量 } void f2(int && n) { n += 1; // ok } int main() { f(5); f2(5); } ———————————————— 版權聲明:本文爲CSDN博主「HR_Reborn」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。 原文鏈接:https://blog.csdn.net/HR_Reborn/article/details/130363997 */ } //在實際場景中,右值引用和std::move被廣泛應用於在STL和自定義類中實現移動語義,避免拷貝,從而提升程序性能。 static void test02(){ // 例1:Array用法 Array a(2); // 做一些操作 //..... // 左值a,用std::move轉化爲右值 Array b(std::move(a));//會調用移動構造函數 //std::move(a) 後 a 不應再被使用,會報錯。因爲nullptr //b = a; //error Array c; Array d; c = d;//調用賦值構造函數 c = std::move(d);//調用移動賦值構造函數 } static void test03(){ //完美轉發 /* 完美轉發std::forward 和std::move一樣,std::forward本質上是進行類型轉換,即左值右值之間的轉換,與move相比,forward更強大,move只能轉出來右值,forward都可以。 std::forward<T>(u)有兩個參數:T與 u。 a. 當T爲左值引用類型時,u將被轉換爲T類型的左值; b. 否則u將被轉換爲T類型右值。 舉個例子,有main,A,B三個函數,調用關係爲:main->A->B。 */ int a = 5; A(std::move(a)); cout << "-------------\n"; func(a); func(5); } };