C++11右值引用
文章目錄
- 左值和右值
- 左值,右值與引用的關(guān)系
- 右值的特殊點
- 左值引用的應用場景和短板
- 移動語義
- 關(guān)于move
- 完美轉(zhuǎn)發(fā)(&& 和 forward)
- 總結(jié)
左值和右值
一般沒有明確的定義。
左值有以下性質(zhì):
右值有以下性質(zhì):
最重要的性質(zhì)是左值可以取地址,右值不可以取地址。
常量,表達式的返回值,非左值的函數(shù)返回值是右值
如下:
10 x + y; f(10);可以參考下圖的定義:比較好理解和記憶
C++11對右值進行了嚴格的區(qū)分:
1.C語言中的純右值,比如:a+b, 100
2.將亡值。比如:表達式的中間結(jié)果、函數(shù)按照值的方式進行返回。
總結(jié)一下, 知道以下幾點就足夠了
1.右值分為純右值和將亡值(中間狀態(tài))
2.左值可以取地址,右值不可以取地址。
左值,右值與引用的關(guān)系
下面代碼對4種情況都舉了例子:
右值的特殊點
右值是不能被修改的,10 = 20這種操作是不允許的。
但是加了右值引用之后,右值可以被修改。并且右值引用是可以取地址的。效果如下:
可能會有這種想法,那么加了右值引用的右值不就變得和左值一樣了。
答案確實是這樣的,后面會講一個叫完美轉(zhuǎn)換的東西,就和這個性質(zhì)有關(guān)系。
左值引用的應用場景和短板
左值引用一般都是用來做函數(shù)傳參和函數(shù)返回值。這樣可以減少拷貝次數(shù),提高效率。
但是有一種情況,左值引用沒有辦法減少拷貝次數(shù)。
要返回局部對象的時候,沒有辦法將函數(shù)返回值變成左值引用。 必須拷貝。如果不拷貝,就相當于訪問已經(jīng)析構(gòu)了的空間了。
右值引用的其中一個應用場景就在這里
ps:并不是說把返回值變成右值引用,右值引用不會做函數(shù)返回值!!!
移動語義
移動語義又分為了移動構(gòu)造和移動賦值。在C++11里面,6個默認成員函數(shù)已經(jīng)變到8個了,加多了移動構(gòu)造函數(shù)和移動賦值函數(shù)。
移動語義需要使用右值引用。
測試代碼:如果沒有寫移動構(gòu)造的時候,operator+返回的局部對象調(diào)用的是深拷貝來返回的。
#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); }原理如下圖:
-----------------------------------手動分割線----------------------------------------
上面講的是沒有移動構(gòu)造函數(shù)情況下:現(xiàn)在我們加上移動構(gòu)造函數(shù)。
移動構(gòu)造函數(shù)寫法:
1.參數(shù)是右值引用
2.交換當前對象和右值引用的所有資源
移動構(gòu)造和拷貝構(gòu)造的區(qū)別在于:拷貝構(gòu)造要多開一塊空間,移動構(gòu)造不需要。
移動這個詞很形象,就是把資源不斷的移動到最終的對象上
再次運行
原理:
總結(jié)一下:
關(guān)于move
move就是把左值變成右值的一個函數(shù)。它并不搬移任何東西,唯一的功能就是將一個左值強制轉(zhuǎn)化為右值引用,然后實現(xiàn)移動語義。
也就是說:只要你不調(diào)用移動構(gòu)造或者移動賦值,左值的值就不會被移動。
一旦發(fā)生了移動拷貝,a的字符串就沒有了。
完美轉(zhuǎn)發(fā)(&& 和 forward)
完美轉(zhuǎn)發(fā)是指一個模板函數(shù),接收到不同的引用參數(shù),可以調(diào)用不同的函數(shù)去執(zhí)行。
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)這個函數(shù)。我們上面說過,右值在被引用的瞬間就變成左值了,如果不加forward,右值引用在繼續(xù)傳參的時候右值引用的類型就會變成左值引用
因此只要用右值引用傳參,就一定要用forward< T >,否則傳的就是左值引用。
總結(jié)
C++11中右值引用主要有以下作用:
總結(jié)
- 上一篇: html标签 对word2vec,基于W
- 下一篇: c++入门必学库函数 memset