国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

這篇具有很好參考價值的文章主要介紹了【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

??作者:一只大喵咪1201
??專欄:《C++學(xué)習(xí)》
??格言:你只管努力,剩下的交給時間!
【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

C++的發(fā)展截至到目前為止,雖然版本有很多,但是C++11則帶來了數(shù)量可觀的變化,其中包含了約140個新特性,以及對C++03標(biāo)準(zhǔn)中約600個缺陷的修正,這使得C++11更像是從C++98/03中孕育出的一種新語言。相比較而言,C++11能更好地用于系統(tǒng)開發(fā)和庫開發(fā)、語法更加泛華和簡單化、更加穩(wěn)定和安全,不僅功能更強大,而且能提升程序員的開發(fā)效率,公司實際項目開發(fā)中也用得比較多,所以我們要作為一個重點去學(xué)習(xí)。

??列表初始化

  • 列表:{ }就被叫做列表。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
我們之前使用列表初始化都是這樣的,如上圖代碼所示,可以使用列表來初始化數(shù)組,初始化結(jié)構(gòu)體變量,初始化元素類型為結(jié)構(gòu)體變量的數(shù)組等等。

  • C++11擴大了用大括號括起的列表(初始化列表)的使用范圍,使其可用于所有的內(nèi)置類型和用戶自定義的類型,使用初始化列表時,可添加等號(=),也可不添加。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
可以不加等會進(jìn)行初始化,如上圖代碼所示,但是強烈不建議使用


不寫等號列表初始化的正確用法:

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

正確的用法應(yīng)該是在new一個對象,一個數(shù)組并且對它們進(jìn)行初始化的時候使用,如上圖所示。


【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

簡單寫一個日期類,在構(gòu)造函數(shù)中打印一句話表面構(gòu)造函數(shù)被調(diào)用過。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
我們一直使用的都是C++98中方式初始化,如上圖代碼中的第一種方式,C++11中提供的列表初始化如上圖后兩種方式,這些都是在調(diào)用構(gòu)造函數(shù)來初始化。

這其實也很雞肋,沒有什么價值,繼續(xù)使用C++98中的方式就挺好的,而且容易理解,C++11中的方式反而不太好理解了。


?? std::initializer_list

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
用初始化列表來初始化STL容器,如上圖所示初始化vector,list,map等,當(dāng)然等號可以去掉(強烈不建議)。
【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

  • vector和list以及map等STL中的容器也可以像普通數(shù)組一樣使用初始化列表來初始化了。

這是因為列表初始化本身就是一個類模板:

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

如上圖所示,這是C++11才有的一個類型,該類型叫做列表初始化,而且還有自己的成員函數(shù),包括構(gòu)造函數(shù),計算列表大小的接口,獲取列表起始和結(jié)束位置的接口(迭代器位置)。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
創(chuàng)建一個列表,使用typeid().name()將類型打印出來,如上圖所示。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
列表中不僅可以放內(nèi)置類型,還可以放自定義類型,如上圖所示。

  • 列表中的自定義類型會調(diào)用它的構(gòu)造函數(shù),構(gòu)造出的對象組成列表。

列表也相當(dāng)于一個容器。


那么為什么可以用列表來初始化vector,list等容器呢?

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
C++11為這些容器提供了新的構(gòu)造函數(shù),如上圖所示。

  • 該構(gòu)造函數(shù)是使用列表來初始化對象的,它的形參就是initializer_list。
vector(std::initializer_list<T> il)
{
	//列表也是存在迭代器的
	for(auto& e : il)
	{
		push_bakc(e);
	}
}

其代碼實現(xiàn)如上。list,map等其他容器也是這個道理,都提供了一個用列表初始化的構(gòu)造函數(shù)。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
賦值運算符重載函數(shù)也有一個列表的重載版本,如上圖所示。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
可以用列表直接給vector賦值,list等其他容器也一樣可以。

對于列表初始化:

  • 省略等號的用法只建議在new對象的時候使用,其他時候要加上等號。
  • 使用列表不僅能夠初始化數(shù)結(jié)構(gòu),還可以初始化STL中的容器。

??新語法

C++11提供了一些新語法,這一小結(jié)中本喵來介紹一下比較小的語法,很多我們都接觸過甚至是使用過。

??聲明

c++11提供了多種簡化聲明的方式,尤其是在使用模板時。

auto

  • auto能自動類型推斷,要求必須進(jìn)行顯示初始化,讓編譯器將定義對象的類型設(shè)置為初始化值的類型。

這個關(guān)鍵字我們已經(jīng)使用過很多了,這里就不再詳細(xì)解釋了,如:

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
可以自動推演出類型,使用起來非常方便。

decltype:

關(guān)鍵字decltype將變量的類型聲明為表達(dá)式指定的類型。

使用typeid().name()只能打印出類型的名稱,并不能用這個名稱繼續(xù)創(chuàng)建變量,而decltype可以:

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
使用decltype可以自動推演類型,并且可以用推演出的結(jié)果繼續(xù)創(chuàng)建變量,如上圖所示,對于一些不同類型直接的運算結(jié)果,decltype有奇效。

nullptr

由于C++中NULL被定義成字面量0,這樣就可能回帶來一些問題,因為0既能指針常量,又能表示整形常量。所以出于清晰和安全的角度考慮,C++11中新增了nullptr,用于表示空指針。

  • 在C語言中,NULL是(void*)0,仍然是一個指針。

在C++中存在條件編譯:

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL  ((void*)0)
#endif
#endif

說實在的,C++中對NULL定義為0本喵覺得沒有道理。

??范圍for循環(huán)

范圍for我們也一直都在使用,這是C++11提供的語法糖,使用起來非常方便,它的底層就是迭代器,只是編譯器給自動替換了,本喵曾經(jīng)講解過,這里就不再詳細(xì)解釋了。

??STL中的一些變化

新容器:

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
紅色框中的是C++11增加的新容器,個人覺得只有unordered_map和unordered_set有用,其他很雞肋。

容器array對標(biāo)的是靜態(tài)數(shù)組,array也是一個靜態(tài)的,也就是在棧區(qū)上的,大小是通過一個非類型模板參數(shù)確定的。

  • 唯一的優(yōu)勢:可以強制檢測越界。

具體的本喵在非類型模板參數(shù)一文中詳細(xì)講過。


容器forward_list是一個單鏈表,也很雞肋,因為絕大部分場景雙鏈表都可以滿足要求,而且更加方便,唯一使用到單鏈表的地方就是哈希桶中。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
它的接口后面有after,表示在指定元素的后面進(jìn)行操作,比如插入到指定元素的后面。

  • 楞要說單鏈表的優(yōu)勢,就是它比雙鏈表少一個節(jié)點指針變量。

至于unordered_map和unordered_set,這兩個容器的底層是哈希桶,雖然不能實現(xiàn)排序,但是可以降重。而且在查找時具有其他容器無法比擬的效率。這兩個容器是非常實用的,而且也是我們經(jīng)常使用的。

容器中的新方法:

  1. 使用列表構(gòu)造

這一點在前面本喵就講解過了,幾乎每個容器都增加了新的接口,使用std::initializer_list類型來構(gòu)造。

  1. 移動構(gòu)造和移動賦值

在下面本喵講解了右值引用就可以明白了。

  1. emplace_xxx插入接口或者右值引用版本的插入接口。

同樣在后面才能學(xué)習(xí)到。

??右值引用

什么是右值?什么是左值?

  • 左值:一個表示數(shù)據(jù)的表達(dá)式,如變量名或者指針解引用。
  • 特點:可以對左值取地址 + 可以對左值賦值。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
上圖代碼中所示的變量都屬于左值,要牢記左值可以取地址這一個特性。

  • 定義時const修飾符后的左值,不能給他賦值,但是可以取它的地址。
  • 左值可以出現(xiàn)在賦值符合的左邊,也可以出現(xiàn)在賦值符合的右邊。
  • 右值:也是一個表示數(shù)據(jù)的表達(dá)式。如:字面常量,表達(dá)式返回值,函數(shù)返回值,類型轉(zhuǎn)換時的臨時變量等等。
  • 特點:右值不可以取地址,不可以賦值。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

上面這些都是右值,要牢記右值特性–不能取地址不能賦值。

  • 右值可以出現(xiàn)在賦值符號的右邊,但是不能出現(xiàn)出現(xiàn)在賦值符號的左邊。

什么是右值引用?

左值引用是給左值取別名,右值引用顯而易見就是給右值取別名。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

  • 右值引用使用兩個&符號。

上圖代碼中的rr1,rr2,rr3就是三個右值的別名,也就是右值引用。


??右值引用類型的左值屬性

  • 右值是不能取地址的,但是給右值取別名后,會導(dǎo)致右值被存儲到特定位置,且可以取到該位置的地址。
  • 對于內(nèi)置類型的右值,如字面常量,一旦右值引用以后,就會被存儲到特定的位置,并且可以取到該地址,而且還可以修改。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

字面常量10原本是不可以被修改的,但是右值引用以后,在特定的位置開辟了變量來存放10,所以就可以被修改了。

表達(dá)式或者函數(shù)的返回值,會有一個臨時變量來存放返回值,我們知道這樣的臨時變量具有常性,也是右值。對于這種右值引用,編譯器會修改它的屬性,將常性修改,并且存儲在特定位置。

const類型的右值,即便開辟了變量存放該右值也是不可以被修改的,因為被const修飾了。

內(nèi)置類型的右值被稱為純右值

  • 對于自定義類型的右值,如容器的臨時變量,它確確實實會被銷毀,而不會被存放。

自定義類型的右值才能體現(xiàn)出右值存在的意義,后面本喵會詳細(xì)講解。

自定義類型的右值被稱為將亡值。

  • 右值引用是右值的別名,它所指向的右值是不可以被修改的。
  • 但是右值引用本身也是一種類型,并且它的屬性是左值,可以取地址,可以賦值。

左值引用總結(jié):

  • 左值引用只能引用左值,不能引用右值
  • const左值引用既可以引用左值,也可以引用右值

因為右值不可修改,只有加上const的左值引用去引用右值時才不會導(dǎo)致權(quán)限的放大,我們之前都是用const左值引用來引用右值的。

右值引用總結(jié):

  • 右值引用只能引用右值,不能引用左值
  • 右值引用可以引用move后的左值

左值經(jīng)過move以后就變成了右值,如:

int a = 10;
int&& rra = move(a);

??右值引用的場景和意義

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
先自己實現(xiàn)一個string,只有拷貝構(gòu)造函數(shù),賦值運算符重載函數(shù),析構(gòu)函數(shù),以及一個普通的構(gòu)造函數(shù)。無論是拷貝構(gòu)造還是賦值運算符重載,都會進(jìn)行深拷貝,采用現(xiàn)代寫法來實現(xiàn)。

左值引用的場景:

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

  • 使用普通傳值調(diào)用,存在一次深拷貝。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

  • 使用傳拷貝調(diào)用時,不存在深拷貝,func函數(shù)直接使用main函數(shù)中的s1對象。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

  • 傳值返回時,存在一次深拷貝。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

  • 傳左值引用返回時,不存在深拷貝。

要知道深拷貝的代價是比較大的,深拷貝次數(shù)減少可以很大程度上提高代碼的效率。


但是左值引用存在短板:

前面我們在調(diào)用to_string函數(shù)的時候,形參就是左值引用,然后再返回,main函數(shù)傳過去的string對象一直存在。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

此時需要拿到函數(shù)中的string對象,而且string對象是一個臨時變量,此時mian函數(shù)中拿到to_string中的string對象要進(jìn)行兩次深拷貝。

  • 第一次深拷貝,to_string函數(shù)返回時,會將string對象放在一個臨時變量中,此時發(fā)生的深拷貝。

函數(shù)返回時,如果是內(nèi)置類型等幾個字節(jié)的變量,會將函數(shù)中的臨時變量放在寄存器中返回,如果是自定義類型所占空間比較大,就會放在臨時變量中壓棧到上一級棧幀中。

  • 第二次深拷貝,main函數(shù)中,ret接收函數(shù)返回了的string對象時會再發(fā)生一次深拷貝。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
但是編譯器會進(jìn)行優(yōu)化,將兩次深拷貝優(yōu)化成一次。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
如上圖所示,可以看到發(fā)生了一次深拷貝,即使減少了一次,但是仍然存在代價。

  • 我們現(xiàn)在想讓它一次拷貝都沒有。

我們現(xiàn)在的深拷貝感受不到代價比較大,試想如果深拷貝的是一個vector<vectot<…>>,此時就代價相當(dāng)大了。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

  • 函數(shù)調(diào)用使用傳左值引用返回。

但是報錯了,因為to_string函數(shù)中的string對象出了作用域會消失,此時傳引用返回就會發(fā)生類似野指針的問題。

所以現(xiàn)在要解決的就是讓局部的臨時對象出了作用域后不消失。


移動構(gòu)造

此時用右值引用就可以解決這個問題。

右值引用的價值之一:補齊臨時對象不能傳引用返回這個短板

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
在string類中增加一個移動構(gòu)造函數(shù),如上圖所示。

  • 移動構(gòu)造的形參是右值引用。

從to_string中返回的string對象是一個臨時變量,具有常性,也就是我們所說的右值。

  • 用右值來構(gòu)造string對象時,會自定匹配移動構(gòu)造函數(shù)。
  • 直接使用swap拿走將亡值。

返回的臨時變量是一個自定義類型的右值,也就是我們前面所說的將亡值。將亡值意味著馬上就要結(jié)束生命了,所以在移動構(gòu)造中,直接拿走將亡值,此時就不用發(fā)生深拷貝。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

  • 此時編譯器識別到了右值ret,然后匹配了移動構(gòu)造函數(shù)。

移動構(gòu)造減少了深拷貝的次數(shù),能夠更大程度上的提高效率,減少代價。

  • to_string返回的是一個右值,用這個將亡值構(gòu)建新對象時,如果沒有移動構(gòu)造就會匹配構(gòu)造函數(shù),因為cons 左值引用可以引用右值。
  • 如果有移動構(gòu)造就匹配移動構(gòu)造。

移動賦值

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
將賦值和創(chuàng)建string對象分開寫,此時編譯器就不會進(jìn)行優(yōu)化,在賦值的時候就會調(diào)用賦值運算符重載,而不是構(gòu)造函數(shù)。

可以看到,調(diào)用了一次移動構(gòu)造,一次深拷貝:

  • to_string返回的是一個臨時變量,是一個右值,所以這里匹配移動構(gòu)造函數(shù)。
  • 在賦值運算符重載中,現(xiàn)代寫法中會調(diào)用一次拷貝構(gòu)造的深拷貝,所以這兩會出現(xiàn)深拷貝。這里顯示的兩次深拷貝其實只有一次。

此時用一個右值進(jìn)行賦值仍然發(fā)生了深拷貝,可以采用同樣的思路,被賦值對象將右值直接拿過來使用。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

  • 將to_string返回的右值對象識別到以后,匹配移動賦值運算符重載函數(shù)。
  • 在函數(shù)中使用swap直接拿走右值去使用。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
此時只調(diào)用了一次移動構(gòu)造和一次移動賦值,都是直接使用右值對象,相比于深拷貝提高了很大的效率。

總結(jié):右值引用和左值引用減少拷貝的原理不太一樣。

  • 左值引用是別名,直接在原本的對象上起作用。
  • 右值引用是間接起作用,通過右值引用識別到右值,然后在移動構(gòu)造和移動賦值中進(jìn)行資源轉(zhuǎn)移。

使用移動構(gòu)造和移動賦值時,被轉(zhuǎn)移資源的對象必須是個將亡值,負(fù)責(zé)會被銷毀:

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
將左值對象s1通過move變成右值對象,用來構(gòu)建s2,匹配的是移動構(gòu)造函數(shù),通過調(diào)試窗口可以看到,當(dāng)s2被構(gòu)造好時,s1就被銷毀了,因為s1的資源被轉(zhuǎn)移了。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
可以看到,C++11的STL標(biāo)準(zhǔn)庫中也提供了移動構(gòu)造和移動賦值函數(shù)。


右值引用的價值之二:插入右值時可以減少深度拷貝

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

簡單實現(xiàn)一個鏈表,僅支持尾插。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
向我們寫的list中插入我們實現(xiàn)的string匿名對象,如上圖所示。在插入過程中,多次調(diào)用了string的深拷貝。

  • 每插入一個string對象時,就要new一個list中的節(jié)點,節(jié)點的構(gòu)造函數(shù)中對string對象進(jìn)行了深拷貝。

由于string對象都是匿名對象,都屬于右值,所以深拷貝是完全沒有必要的,最理想的狀況是用移動拷貝。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

  • 在list的尾插中增加右值引用類型的接口。
  • 在節(jié)點的構(gòu)造函數(shù)中增加右值引用的構(gòu)造函數(shù)。

當(dāng)向list中插入string的匿名對象時,會匹配list的右值引用類型的尾插接口,在尾插接口中new一個新節(jié)點時,會匹配list_node的移動構(gòu)造函數(shù),又會使用string的移動構(gòu)造函數(shù)來初始化節(jié)點。

  • 尾插接口以及節(jié)點的構(gòu)造函數(shù)中,需要使用move將右值引用的左值屬性改成右值,負(fù)責(zé)會匹配普通構(gòu)造函數(shù),而不是移動構(gòu)造函數(shù)。

因為右值引用類型本身是一個左值,所以需要使用move改變其屬性,使其始終保持右值屬性。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

此時所有的插入都是使用的移動構(gòu)造,相比于深拷貝,效率提升了很多。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
C++11為STL庫中添加了右值引用版本的尾插。


這里右值引用類的接口不能使用const修飾右值引用:

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
僅將尾插接口的形參改成const修飾的右值引用以后,在插入匿名對象時,調(diào)用的是string的深拷貝函數(shù),沒有匹配到移動構(gòu)造。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
僅將節(jié)點構(gòu)移動構(gòu)造函數(shù)的右值引用用const修飾,同樣會導(dǎo)致無法匹配string的移動構(gòu)造函數(shù)。

  • 右值引用會在特定位置開辟空間來存儲右值,所以右值引用本身的屬性是左值。
  • 是左值就可以修改,尤其是在移動構(gòu)造函數(shù)中,會將右值引用里的右值轉(zhuǎn)移過來。
  • 如果使用const修飾了右值引用,此時右值引用就不可以修改了,同樣就不可以進(jìn)行轉(zhuǎn)移了。
  • 所以編譯器就去匹配深拷貝去了。

雖然右值不可以被修改,但是右值引用以后具有了左值屬性,才能被轉(zhuǎn)移,一旦被const修飾以后就無法轉(zhuǎn)移了。所以我們在使用右值引用的時候,不要使用const來修飾。


??完美轉(zhuǎn)發(fā)

??萬能引用

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
寫多個重載函數(shù),根據(jù)實參類型調(diào)用不同函數(shù)。

  • 形參類型分別是左值引用,const左值引用,右值引用,const右值引用

上圖代碼中的perfectForward函數(shù)模板被叫做萬能引用模板,無論調(diào)用該函數(shù)時傳的是什么類型,它都能推演出來。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

  • 左值:調(diào)用模板函數(shù)時,實參是左值,推演出來的t就是左值引用。實參是const左值,推演出來的t就是const左值引用。
  • 右值:調(diào)用模板函數(shù)時,實參是右值,推演出來的t就是右值引用。實參是const右值,推演出來的t就是const右值引用。

由于右值引用本身也是左值,所以需要通過move將其轉(zhuǎn)換成右值才能看到推演效果。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

實參 T t
int a=10 int int&(左值引用)
const int b=20 const int const int&(const左值引用)
30 int int&&(右值引用)
move(int c=40) const int const int&&(const 右值引用)

在函數(shù)模板推演的過程中會發(fā)生引用折疊:模板參數(shù)T&&中的兩個&符號折疊成一個。

當(dāng)傳入的實參是左值時,就會發(fā)生引用折疊,是右值時就不會發(fā)生引用折疊。

  • 無論傳的實參是什么,都不用改變模板參數(shù)T&&,編譯器都能夠自己推演。

這就是萬能引用,只需要一個模板就可以搞定,不需要分類去寫。


上面萬能模板中,雖然推演出來了各自實參類型,但是由于右值引用本身是左值屬性,所以需要使用move改變屬性后才能調(diào)用對應(yīng)的重載函數(shù)。

有沒有辦法不用move改變左值屬性,讓模板函數(shù)中的t保持它推演出來的類型。答案是有的,完美轉(zhuǎn)發(fā)就能夠保持形參的屬性不變。

  • 完美轉(zhuǎn)發(fā):完美轉(zhuǎn)發(fā)在傳參的過程中保留對象原生類型屬性。
  • 實參傳遞過來后,推演出的形參是什么類型就保持什么類型繼續(xù)使用。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
完美轉(zhuǎn)發(fā)同樣是C++11提供的,它也是一個模板。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
此時再使用萬能引用的時候,在函數(shù)模板中調(diào)用重載函數(shù)時只需要使用完美轉(zhuǎn)發(fā)就可以保持推演出來的屬性不變,右值引用仍然是右值,const右值引用也仍然是右值。


前面在向list中插入string匿名對象的時候,同樣面臨這個問題,當(dāng)時我們都是使用的move解決的,此時就可以使用完美轉(zhuǎn)發(fā)(forward)了。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
可以看到,使用完美轉(zhuǎn)發(fā)同樣可以實現(xiàn)目的。

??新的類功能

在原來的C++類中,有6大默認(rèn)成員函數(shù):

構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),賦值運算符重載,析構(gòu)函數(shù),取地址重載,const取地址重載。

最后重要的是前4個,后兩個用處不大。默認(rèn)成員函數(shù)就是我們不寫編譯器會生成一個默認(rèn)的,而且完全符號我們使用的需求。

在C++11中新增了兩個:移動構(gòu)造和移動賦值運算符重載。

這兩個成員函數(shù)在前面本喵已經(jīng)介紹過它是什么了,這里站在默認(rèn)成員函數(shù)的角度繼續(xù)談?wù)劇?/p>

滿足下列條件,編譯器會自定生成移動構(gòu)造函數(shù):

  • 沒有自己顯示定義移動構(gòu)造函數(shù)
  • 且沒有實現(xiàn)析構(gòu)函數(shù),拷貝構(gòu)造函數(shù),拷貝賦值重載中的任何一個。

此時編譯器會自定生成一個默認(rèn)的移動構(gòu)造函數(shù)。

  • 默認(rèn)生成的移動構(gòu)造函數(shù),對于內(nèi)置類型會逐字節(jié)進(jìn)行拷貝。
  • 對于自定義類型,如果實現(xiàn)了移動構(gòu)造就調(diào)用移動構(gòu)造,沒有實現(xiàn)就調(diào)用拷貝構(gòu)造。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
創(chuàng)建一個類,屏蔽掉拷貝構(gòu)造,拷貝賦值,以及析構(gòu)函數(shù),成員變量有一個是我們自己實現(xiàn)的string,里面有移動構(gòu)造。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
此時Person就自動生成了移動構(gòu)造函數(shù),并且調(diào)用了string中的移動構(gòu)造函數(shù)來構(gòu)造string對象。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

  • 將Person中的拷貝構(gòu)造,拷貝賦值,析構(gòu)函數(shù)任意放出一個來。
  • 使用右值構(gòu)建string對象時,都會調(diào)用string的拷貝構(gòu)造函數(shù)。

滿足下列條件,編譯器會自動生成移動賦值重載函數(shù)

  • 自己沒有顯示定義移動賦值重載函數(shù)。
  • 且且沒有實現(xiàn)析構(gòu)函數(shù),拷貝構(gòu)造函數(shù),拷貝賦值重載中的任何一個。

此時編譯器會自動生成一個默認(rèn)移動賦值函數(shù)。

  • 對于內(nèi)置類型會按字節(jié)拷貝。
  • 對于自定義類型,如果實現(xiàn)了移動賦值就調(diào)用移動賦值,如果沒有實現(xiàn)就調(diào)用拷貝賦值。

和上面的移動構(gòu)造完全類型。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
同樣將Person中的拷貝構(gòu)造,拷貝賦值,析構(gòu)函數(shù)屏蔽,給s4賦值右值對象。此時編譯器自動生成移動賦值,調(diào)用string的移動賦值函數(shù)。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)

同樣將上面的三個成員函數(shù)任意放出一個,編譯器都不會自動生成默認(rèn)移動賦值,而是會調(diào)用string的拷貝賦值函數(shù)。

  • 編譯器默認(rèn)生成的移動賦值和移動構(gòu)造非常類型。
  • 如果符合條件就生成,內(nèi)置內(nèi)心按字節(jié)處理,自定義類型調(diào)用自定義類型的移動賦值或者移動構(gòu)造,如果沒有的化就調(diào)用它們的拷貝賦值或者拷貝構(gòu)造。
  • 如果不符合條件,就直接調(diào)用自定義類型的拷貝復(fù)制或者拷貝構(gòu)造。

??新的關(guān)鍵字

default:

這個default并不是switch中的default,而是C++11的新用法。

  • 假設(shè)類中的某個默認(rèn)成員函數(shù)沒有自動生成,但是我們需要它,就可以用default,強制讓編譯器自動生成默認(rèn)函數(shù)。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
將Person中的拷貝構(gòu)造,拷貝復(fù)制,析構(gòu)函數(shù)都顯示定義,此時就破壞了自動生成移動構(gòu)造的條件。

  • 使用default強制生成默認(rèn)的移動構(gòu)造函數(shù),如上圖紅色框中所示。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
從結(jié)果中可以看到,仍然調(diào)用了string中的移動構(gòu)造函數(shù),而不是調(diào)用的拷貝構(gòu)造。

  • 說明Person中仍然生成了默認(rèn)的移動構(gòu)造函數(shù)。

delete

  • 要求不生成默認(rèn)成員函數(shù)。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
在Person類中不顯示定義拷貝構(gòu)造函數(shù),拷貝復(fù)制函數(shù),析構(gòu)函數(shù),此時符合自動生成默認(rèn)移動構(gòu)造的條件。

  • 聲明移動構(gòu)造函數(shù),但是沒有定義。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
此時在編譯的時候就會報錯,這是C++98中的方式,利用鏈接時找不到函數(shù)的定義報錯。

  • C++11中,使用delete同樣可以實現(xiàn)不讓自動生成默認(rèn)成員函數(shù)。

【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)
同樣在編譯時報錯了。

編譯器會自動生成移動構(gòu)造函數(shù),但是此時使用了delete,編譯器就會報錯,告訴我們這里生成了移動構(gòu)造。

這是為了在編譯階段就報錯,而不是運行時再報錯,實話講,這個很雞肋。

final與override

這兩個關(guān)鍵字在繼承和多態(tài)部分本喵詳細(xì)講解過。

final

  • 在繼承中,被final修飾的類叫做最終類,是無法繼承的。
  • 在多態(tài)中,被final修飾的虛函數(shù)是無法進(jìn)行重寫的。

override

  • 在多態(tài)中,用來檢查虛函數(shù)是否完成了重寫。

??總結(jié)

C++11中的很多東西雖然讓C++越來越不像C++,比如列表初始化等內(nèi)容,但是還是有一些非常有用的東西的,比如今天講到的右值引用,可以大大提高效率。文章來源地址http://www.zghlxwxcb.cn/news/detail-456233.html

到了這里,關(guān)于【C++學(xué)習(xí)】C++11——新特性 | 右值引用 | 完美轉(zhuǎn)發(fā)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 【重學(xué)C++】04 | 說透C++右值引用、移動語義、完美轉(zhuǎn)發(fā)(上)

    【重學(xué)C++】04 | 說透C++右值引用、移動語義、完美轉(zhuǎn)發(fā)(上) 大家好,我是只講技術(shù)干貨的會玩code,今天是【重學(xué)C++】的第四講,在前面《03 | 手?jǐn)]C++智能指針實戰(zhàn)教程》中,我們或多或少接觸了右值引用和移動的一些用法。 右值引用是 C++11 標(biāo)準(zhǔn)中一個很重要的特性。第一

    2024年02月06日
    瀏覽(22)
  • 【重學(xué)C++】05 | 說透右值引用、移動語義、完美轉(zhuǎn)發(fā)(下)

    【重學(xué)C++】05 | 說透右值引用、移動語義、完美轉(zhuǎn)發(fā)(下) 大家好,我是只講技術(shù)干貨的會玩code,今天是【重學(xué)C++】的第五講,在第四講《【重學(xué)C++】04 | 說透右值引用、移動語義、完美轉(zhuǎn)發(fā)(上)》中,我們解釋了右值和右值引用的相關(guān)概念,并介紹了C++的移動語義以及如

    2024年02月06日
    瀏覽(16)
  • C++右值引用(左值表達(dá)式、右值表達(dá)式)(移動語義、完美轉(zhuǎn)發(fā)(右值引用+std::forward))(有問題懸而未決)

    C++右值引用(左值表達(dá)式、右值表達(dá)式)(移動語義、完美轉(zhuǎn)發(fā)(右值引用+std::forward))(有問題懸而未決)

    在 C++ 中,表達(dá)式可以分為左值表達(dá)式和右值表達(dá)式。左值表達(dá)式指的是可以出現(xiàn)在賦值語句左邊的表達(dá)式,例如變量、數(shù)組元素、結(jié)構(gòu)體成員等;右值表達(dá)式指的是不能出現(xiàn)在賦值語句左邊的表達(dá)式,例如常量、臨時對象、函數(shù)返回值等。 右值是指將要被銷毀的臨時對象或

    2024年02月04日
    瀏覽(23)
  • 從C語言到C++_33(C++11_上)initializer_list+右值引用+完美轉(zhuǎn)發(fā)+移動構(gòu)造/賦值

    從C語言到C++_33(C++11_上)initializer_list+右值引用+完美轉(zhuǎn)發(fā)+移動構(gòu)造/賦值

    目錄 1.?列表初始化initializer_list 2. 前面提到的一些知識點 2.1 小語法 2.2 STL中的一些變化 3. 右值和右值引用 3.1?右值和右值引用概念 3.2?右值引用類型的左值屬性 3.3?左值引用與右值引用比較 3.4?右值引用的使用場景 3.4.1 左值引用的功能和短板 3.4.2?移動構(gòu)造 3.4.3?移動賦值

    2024年02月12日
    瀏覽(36)
  • 【C++】—— C++11新特性之 “右值引用和移動語義”

    【C++】—— C++11新特性之 “右值引用和移動語義”

    前言: 本期,我們將要的介紹有關(guān) C++右值引用 的相關(guān)知識。對于本期知識內(nèi)容,大家是必須要能夠掌握的,在面試中是屬于重點考察對象。 目錄 (一)左值引用和右值引用 1、什么是左值?什么是左值引用? 2、什么是右值?什么是右值引用? (二)左值引用與右值引用比

    2024年02月11日
    瀏覽(21)
  • C++左值右值完美轉(zhuǎn)發(fā)轉(zhuǎn)移

    英文含義: 左值(Lvalue) : Locator value ,意味著它指向一個具體的內(nèi)存位置。 右值(Rvalue) : Read value ,指的是可以讀取的數(shù)據(jù),但不一定指向一個固定的內(nèi)存位置。 定義 左值 :指的是一個持久的內(nèi)存地址。左值可以出現(xiàn)在賦值操作的左側(cè)或右側(cè)。例如,變量、數(shù)組的元

    2024年03月10日
    瀏覽(35)
  • 【C++】C++11右值引用

    【C++】C++11右值引用

    ?? 樊梓慕: 個人主頁 ??? 個人專欄: 《C語言》 《數(shù)據(jù)結(jié)構(gòu)》 《藍(lán)橋杯試題》 《LeetCode刷題筆記》 《實訓(xùn)項目》 《C++》 《Linux》 《算法》 ?? 每一個不曾起舞的日子,都是對生命的辜負(fù) 目錄 前言 1.什么是左值什么是右值 左值 右值 2.什么是左值引用什么是右值引用 左

    2024年04月22日
    瀏覽(29)
  • 【C++】萬能引用、完美轉(zhuǎn)發(fā)

    萬能引用的格式如下: 雖然寫的是和右值引用類似,但是它可以接收左值引用和右值引用 當(dāng)傳過來的是左值,那么 T 會折疊為 T 。 引用折疊有以下幾種情況: 實參 形參 結(jié)果 (左值) (左值) (左值) (左值) (右值) (左值) (右值) (右值) (右值) (右值)

    2024年02月14日
    瀏覽(15)
  • 【C++】 C++11(右值引用,移動語義,bind,包裝器,lambda,線程庫)

    【C++】 C++11(右值引用,移動語義,bind,包裝器,lambda,線程庫)

    C++11是C++語言的一次重大更新版本,于2011年發(fā)布,它包含了一些非常有用的新特性,為開發(fā)人員提供了更好的編程工具和更好的編程體驗,使得編寫高效、可維護(hù)、安全的代碼更加容易。 一些C++11的新增特性包括: 強制類型枚舉,使得枚舉類型的通常行為更加可靠和容易控制

    2024年02月10日
    瀏覽(20)
  • 【C++】C++11右值引用|新增默認(rèn)成員函數(shù)|可變參數(shù)模版|lambda表達(dá)式

    【C++】C++11右值引用|新增默認(rèn)成員函數(shù)|可變參數(shù)模版|lambda表達(dá)式

    在C++11之前,我們只有引用的概念,沒有接觸到所謂的左值引用或者是右值引用這種概念,從C++11開始,增加了右值引用的概念,那么現(xiàn)在我們將對引用進(jìn)行一個概念上的區(qū)分。在此之前我們所說的引用都是左值引用,對于左值引用相關(guān)的內(nèi)容,可以去看一看博主之前寫的文章

    2024年02月15日
    瀏覽(34)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包