C++11右值引用
文章目錄
- 左值和右值
- 左值,右值與引用的關系
- 右值的特殊點
- 左值引用的應用場景和短板
- 移動語義
- 關于move
- 完美轉發(&& 和 forward)
- 總結
左值和右值
一般沒有明確的定義。
左值有以下性質:
右值有以下性質:
最重要的性質是左值可以取地址,右值不可以取地址。
常量,表達式的返回值,非左值的函數返回值是右值
如下:
10 x + y; f(10);可以參考下圖的定義:比較好理解和記憶
C++11對右值進行了嚴格的區分:
1.C語言中的純右值,比如:a+b, 100
2.將亡值。比如:表達式的中間結果、函數按照值的方式進行返回。
總結一下, 知道以下幾點就足夠了
1.右值分為純右值和將亡值(中間狀態)
2.左值可以取地址,右值不可以取地址。
左值,右值與引用的關系
下面代碼對4種情況都舉了例子:
右值的特殊點
右值是不能被修改的,10 = 20這種操作是不允許的。
但是加了右值引用之后,右值可以被修改。并且右值引用是可以取地址的。效果如下:
可能會有這種想法,那么加了右值引用的右值不就變得和左值一樣了。
答案確實是這樣的,后面會講一個叫完美轉換的東西,就和這個性質有關系。
左值引用的應用場景和短板
左值引用一般都是用來做函數傳參和函數返回值。這樣可以減少拷貝次數,提高效率。
但是有一種情況,左值引用沒有辦法減少拷貝次數。
要返回局部對象的時候,沒有辦法將函數返回值變成左值引用。 必須拷貝。如果不拷貝,就相當于訪問已經析構了的空間了。
右值引用的其中一個應用場景就在這里
ps:并不是說把返回值變成右值引用,右值引用不會做函數返回值!!!
移動語義
移動語義又分為了移動構造和移動賦值。在C++11里面,6個默認成員函數已經變到8個了,加多了移動構造函數和移動賦值函數。
移動語義需要使用右值引用。
測試代碼:如果沒有寫移動構造的時候,operator+返回的局部對象調用的是深拷貝來返回的。
#include <iostream>using namespace std; class String { public:String(char* str = (char*)""){if (nullptr == str)str = (char*)"";_str = new char[strlen(str) + 1];strcpy(_str, str);}String(const String& s): _str(new char[strlen(s._str) + 1]){cout << "String(const String& s)" << ' ' << "深拷貝" << endl;strcpy(_str, s._str);}void swap(String& s){std::swap(_str, s._str);}/*String(String&& s){cout << "String(String&& s) " << "移動語義" << endl;_str = (char*)"";swap(s);}*/String& operator=(String&& s){cout << "String& operator=(String&& s) " << "移動語義" << endl;_str = (char*)"";swap(s);}String& operator=(const String& s){if (this != &s){cout << "String& operator=(const String& s) " << "深拷貝" << endl;char* pTemp = new char[strlen(s._str) + 1];strcpy(pTemp, s._str);delete[] _str;_str = pTemp;}return *this;}String operator+(const String& s){char* pTemp = new char[strlen(_str) + strlen(s._str) + 1];strcpy(pTemp, _str);strcpy(pTemp + strlen(_str), s._str);String strRet(pTemp);return strRet;}~String(){if (_str) delete[] _str;} private:char* _str; };int main() {String s1((char*)"hello");String s2((char*)"world");String s3(s1 + s2); }原理如下圖:
-----------------------------------手動分割線----------------------------------------
上面講的是沒有移動構造函數情況下:現在我們加上移動構造函數。
移動構造函數寫法:
1.參數是右值引用
2.交換當前對象和右值引用的所有資源
移動構造和拷貝構造的區別在于:拷貝構造要多開一塊空間,移動構造不需要。
移動這個詞很形象,就是把資源不斷的移動到最終的對象上
再次運行
原理:
總結一下:
關于move
move就是把左值變成右值的一個函數。它并不搬移任何東西,唯一的功能就是將一個左值強制轉化為右值引用,然后實現移動語義。
也就是說:只要你不調用移動構造或者移動賦值,左值的值就不會被移動。
一旦發生了移動拷貝,a的字符串就沒有了。
完美轉發(&& 和 forward)
完美轉發是指一個模板函數,接收到不同的引用參數,可以調用不同的函數去執行。
void f(int&& t) {cout << "右值引用" << endl; }void f(int& t) {cout << "左值引用" << endl; }void f(const int& t) {cout << "const左值引用" << endl; }template<class T> void perfectForward(T&& t) {//f(forward<T>(t));f(t); }int main() {int&& a = 10;int& b = a;const int& c = a;perfectForward(10);perfectForward(b);perfectForward(c); }要說兩點:
template<class T> void perfectForward(T&& t)上面代碼里面的模板T&& t并不是說接收的是右值引用,在模板后面加上**&&代表的是萬能引用,既能接收左值引用,也可以接收右值引用。**
必須要加上forward< T >(t)這個函數。我們上面說過,右值在被引用的瞬間就變成左值了,如果不加forward,右值引用在繼續傳參的時候右值引用的類型就會變成左值引用
因此只要用右值引用傳參,就一定要用forward< T >,否則傳的就是左值引用。
總結
C++11中右值引用主要有以下作用:
總結
- 上一篇: html标签 对word2vec,基于W
- 下一篇: MIT 开源协议