(目錄占位)
1. 前言:
C++ 11 是在 C++ 98 之后又一個變化比較大的標準。為C++增加了很多東西,其中有一部分是有用的,有一部分是我自認為作用不是很大東西。這一章呢?我們就來說說C++11我,我認為對性能優化最有用的一部分 ---- 右值引用
2. 簡單回顧:左值引用
左值?我們現在說說什么是左值?
直接拋結論:能取地址的值或者表達式結果就是左值。左值可以出現在 = 左邊,也可以出現在 = 右邊,不能僅僅以 = 號來區分左右值。
最開始時候的引用,就是左值引用,在C++的語法層面上,引用是給一個變量起一個別名,是不開辟空間的,目的是為了:減少拷貝,提高效率。
我們通常在學習的時候與 指針 對照學習。
語法層面(指針對比引用):
引用在定義的時候必須初始化,指針在定義的時候是可以不初始化的。
引用是沒有空引用的,但是指針呢?他是有空指針的,比如C++11之前的NULL和C++11提供的nullptr。
引用是不開辟空間的,但是指針是需要開辟4個字節或者8個字節的空間的。
引用和指針都有一個概念:權限的方法和縮小,首先一說,只有引用和指針有權限的放大和縮小。其他的語法是沒有的,我們在學習的時候,千萬不要自己創造語法...
權限的放大:
#include <iostream>int main()
{const int a = 10;int &b = a; // 權限的放大const int c = 20;int *p = &c; // 權限的放大return 0;
}
變量 a 的權限是 可讀, 但是 變量 b 作為變量 a 的引用,權限卻是可讀可寫。?
變量 c 的權限是 可讀, 但是 變量 p 指向 變量 a,權限卻是可讀可寫。
上面這兩種情況都是編譯器默認不被允許的。?
權限的縮小:
#include <iostream>int main()
{int a = 10;const int &b = a; // 權限的縮小int c = 20;const int *p = &c; // 權限的縮小return 0;
}
變量 a 的權限是 可讀可寫, 但是 變量 b 作為變量 a 的引用,權限是可讀。?
變量 c 的權限是 可讀可寫, 但是 變量 p 指向 變量 c,權限是可讀。
上面的這兩種情況是編譯器默認被允許的。
小結:
權限的放大是不被允許的,但是權限的縮小是被允許的。
權限的放大和縮小都是語法層面的概念,都是被編譯器約束的,我們可以通過強制類型轉換來放大或者縮小權限(但是不推薦,因為有風險),都只是為了“騙”過編譯器。
匯編層面:
引用和指針是一個的,都是開辟可一塊空間,用來保存地址,指針變量就是用來保存指向的對象的地址,引用也是一樣的,開辟一塊空間,用來保存被起別名的對象的地址。
3. 本章主角:右值引用
相對于左值來說,右值就是不能取地址的值,并且右值是不能出現在 = 號的左邊的。
自定義類型的右值常見的兩種形式 : 臨時對象 匿名對象
C++11 對右值又做了細分:純右值(內置類型) 和將亡值(自定義類型),我們的主要研究對象就是:將亡值(自定義類型)
編譯器在進化過程中,將 拷貝構造?+ 構造 優化成 直接構造。
比如說在函數棧幀內返回一個比較大的對象的時候,右值引用的價值就極大了。
{
? ? ? ?原來: 跨行的:構造 + 拷貝構造
? ? ? ?現在: 跨行的:構造 + 移動構造
}
減少了一次拷貝,還是很可觀的。
移動構造,移動賦值
有了移動構造之后: 返回時先拷貝構造生成臨時對象,用臨時對象移動構造
如果 “析構函數”,“拷貝構造”,“拷貝賦值” 都沒有實現編譯器就會自動生成 “移動構造”
編譯器生成的移動構造:內置類型去值拷貝,自定義類型去調用它自己的移動構造
為什么?
需要顯式寫析構,說明有資源要釋放,說明需要顯式寫拷貝構造和重載
說明要顯式寫移動構造和移動賦值
要么都自己寫,要么都編譯器自己生成。
4. 左值引用和右值引用
這個部分我們主要討論一下,左值引用是否能引用右值,右值引用是否能引用左值,就是是否能交叉?
先看右值引用能否引用左值?
但是這樣:const 左值引用 可以給引用右值 是可以的
除了這樣,我們還可以通過強制類型轉換來讓左值引用引用右值,右值引用引用左值。
5. 小結
左值引用和右值引用的目的都是為了:減少拷貝,提高效率。但是他們都是編譯器層面的概念,是可以通過強制類型轉化來轉化的(騙編譯器,不建議)。