學過c++的應該都知道c++中獨有的概念—引用。
int b=1;
int& a = b;
通俗理解上,左值就是可以放在等號左邊的值,右值就是放在等號右邊的值。
更專業的說法是:
左值:左值指定了乙個函式或物件,是乙個可以取位址的表示式(能取位址就行)
右值:不和物件相關聯的值(字面量)或者求其值結果是字面量或者乙個匿名的臨時物件
將亡值:是指進行右值引用操作之後的右值。它是乙個產生於右值引用的概念。
#include
#include
#include
using
namespace std;
int mctor =0;
int cctor =0;
int dtor =0;
class
mystring
else
cout<<
"mystring無參建構函式"
mystring
(const mystring &str)
~mystring()
private
:char
* m_data;};
intmain()
這是c++中string類的乙個實現,在c++標準中匿名物件是乙個純右值,也就是當語句執行完之後就會被析構。
mystring str1
(mystring
("hello"))
;
mystring returnmystring()
void
getmystring
(mystring str)
getmystring
(returnmystring()
);
print函式在呼叫的過程中呼叫了兩次建構函式:第一次就是mystring()生成乙個匿名的物件,第二次是呼叫拷貝建構函式建構函式的返回值內容為生成的匿名物件。就相當於**mystring(mystring())**這是函式的返回值。可能大家不理解為什麼:c++標準中匿名物件是乙個純右值,在執行完return mystring()這條語句之後就會自動呼叫析構函式銷毀記憶體空間,也就是說我們不能用它,這樣就只能在生成乙個和他一摸一樣的匿名物件去執行下一條語句。
當然這個現象你也看不到編譯器幫你優化好了hh.
大家都懂編譯器是個什麼東西啊,有時幫你優化有時就不優化。所以我們還是自己搞懂這個東西比較好。
說回正題我想生成乙個內容為"hello"的字串為什麼要連續申請兩個內容一摸一樣的記憶體位址呢,而且生成之後還要銷毀乙個。這就很拖執行速度。
鑑於此右值引用的第乙個意義——移動構造就誕生了
mystring
(mystring &&str)
:m_data
正如左值引用可以提高函式的呼叫效能一樣,我們也希望可以用右值引用來避免申請和銷毀乙個沒有實際意義的記憶體空間。
如果我們用右值引用將語句執行完就銷毀的右值變成將亡值也就是延長它的生存週期。
呼叫移動建構函式,我們就可以不用再申請乙份相同內容的記憶體空間,而是用乙個別名繼續使用上面提到的mystring()這個匿名物件所申請的記憶體空間。這樣就少呼叫了乙個拷貝建構函式和虛構函式。極大地提高了效能。
看到這裡你會發現這不是偷嗎:既然你不能用了,我就拿過來當我的空間了
這種方法好像很有意思,那我們可不可以用同樣的方式將一些生命周期短的左值裡的內容也偷過來呢。
c++11中提供了std::move()方法來將左值轉換為右值:它的作用是告訴編譯器我是個左值但是不呼叫拷貝建構函式而是呼叫移動建構函式。
mystring str1
("string");
mystring str2
(std::
move
(str1));
//呼叫移動建構函式
注意:
這裡str2偷取了str1的內容之後,str1還沒有析構,他仍然是離開作用於之後才呼叫析構函式。
我們不能繼續用str1來訪問"string"這個內容,會產生錯誤(雖然不會報錯)。
引用有這麼多好處,那能不能定義乙個方法左值和右值我都可以用引用:
c++中可以將引用與模板相結合:
模板裡的&&是乙個未定義的引用型別,被稱為通用引用必須被初始化,左值引用還是右值引用取決於初始化的物件左值還是右值。
template
<
typename t>
void
f( t&& param)f(
10);f
(x);
注意:只有當發生自動型別推斷時(如函式模板的型別自動推導,或auto關鍵字),&&才是乙個通用指標。
c++中右值引用還有乙個重要的意義就是完美**。
先說說什麼是**:
**是指函式將自己的引數**給其他函式進行處理也就是返回的物件是自己接受的引數。
什麼是完美**
完美**是指**之後引數的型別,特徵保持不變。
可能你會想保持特徵不變那我就用乙個模板加通用指標不就行了嗎。
#include
#include
#include
using
namespace std;
void
process
(int
& i)
void
process
(int
&& i)
template
<
typename t>
void
myforward
(t&& i)
intmain()
執行這段**會發現myforward並不能完美的將引數傳給process,因為當我們將乙個右值傳給myforward時經過右值引用得到了名字,變成了左值。
那怎樣才能做到完美**呢?
c++11提供了乙個方法
void
myforward
(t&& i)
用std::forward和通用引用就可以做到完美**。
1.值分為左值和右值,右值可以通過右值引用成為將亡值
2.右值引用過後的別名屬於左值
3.模板中的&&是通用引用,初始化為左值就是左值引用,初始化為右值就是右值引用。
4.右值引用兩個重要意義:
第一:通過移動語義(移動建構函式,移動賦值函式)避免了不必要的記憶體拷貝,提高了效能。
第二:配合std::forward和通用引用實現了完美**。
5.std::move可以將左值轉換為右值
部落格參考了
c 11 右值引用
右值引用 是一種復合型別,跟c 的傳統引用很類似。為更準確地區分兩種型別,我們把傳統的c 引用稱為 左值引用 而使用 引用 這一術語時,我們的意思同時包含兩種引用 左值引用和右值引用。右值引用的行為跟左值引用類似,不同之處在於 右值引用可以繫結到臨時量 右值 而 非const的 左值引用卻不能繫結到...
C 11 右值引用
消除兩個物件互動時不必要的物件拷貝,節省運算儲存資源,提高效率。能夠更簡潔明確地定義泛型函式。1.右值引用 int a a 1 here,a is an lvalue 上述的a就是乙個左值。c 11中左值的宣告符號為 為了和左值區分,右值的宣告符號為 printreference const str...
C 11右值引用
c 11中引入的乙個非常重要的概念就是右值引用。理解右值引用是學習 移動語義 move semantics 的基礎。而要理解右值引用,就必須先區分左值與右值。對左值和右值的乙個最常見的誤解是 等號左邊的就是左值,等號右邊的就是右值。左值和右值都是針對表示式而言的,左值是指表示式結束後依然存在的持久物...