● 請問C++11有哪些新特性?
參考回答:
C++11 最常用的新特性如下:
auto關(guān)鍵字:編譯器可以根據(jù)初始值自動推導(dǎo)出類型。但是不能用于函數(shù)傳參以及數(shù)組類型的推導(dǎo)
nullptr關(guān)鍵字:nullptr是一種特殊類型的字面值,它可以被轉(zhuǎn)換成任意其它的指針類型;而NULL一般被宏定義為0,在遇到重載時可能會出現(xiàn)問題。
智能指針:C++11新增了std::shared_ptr、std::weak_ptr等類型的智能指針,用于解決內(nèi)存管理的問題。
初始化列表:使用初始化列表來對類進(jìn)行初始化
右值引用:基于右值引用可以實現(xiàn)移動語義和完美轉(zhuǎn)發(fā),消除兩個對象交互時不必要的對象拷貝,節(jié)省運算存儲資源,提高效率
atomic原子操作用于多線程資源互斥操作
新增STL容器array以及tuple
● 請你詳細(xì)介紹一下C++11中的可變參數(shù)模板、右值引用和lambda這幾個新特性。
參考回答:
可變參數(shù)模板:
C++11的可變參數(shù)模板,對參數(shù)進(jìn)行了高度泛化,可以表示任意數(shù)目、任意類型的參數(shù),其語法為:在class或typename后面帶上省略號”。
例如:
Template<class ... T>
void func(T ... args)
{
cout<<”num is”<<sizeof ...(args)<<endl;
}</pre>
func();//args不含任何參數(shù)
func(1);//args包含一個int類型的實參
func(1,2.0)//args包含一個int一個double類型的實參
其中T叫做模板參數(shù)包,args叫做函數(shù)參數(shù)包
省略號作用如下:
1)聲明一個包含0到任意個模板參數(shù)的參數(shù)包
2)在模板定義得右邊,可以將參數(shù)包展成一個個獨立的參數(shù)
C++11可以使用遞歸函數(shù)的方式展開參數(shù)包,獲得可變參數(shù)的每個值。通過遞歸函數(shù)展開參數(shù)包,需要提供一個參數(shù)包展開的函數(shù)和一個遞歸終止函數(shù)。例如:
include using namespace std;
// 最終遞歸函數(shù)
void print()
{
cout << "empty" << endl;
}
// 展開函數(shù)
template void print(T head, Args... args)
{
cout << head << ","; print(args...);
}
int main()
{
print(1, 2, 3, 4); return 0;
}</pre>
參數(shù)包Args ...在展開的過程中遞歸調(diào)用自己,沒調(diào)用一次參數(shù)包中的參數(shù)就會少一個,直到所有參數(shù)都展開為止。當(dāng)沒有參數(shù)時就會調(diào)用非模板函數(shù)printf終止遞歸過程。
右值引用:
C++中,左值通常指可以取地址,有名字的值就是左值,而不能取地址,沒有名字的就是右值。而在指C++11中,右值是由兩個概念構(gòu)成,將亡值和純右值。純右值是用于識別臨時變量和一些不跟對象關(guān)聯(lián)的值,比如1+3產(chǎn)生的臨時變量值,2、true等,而將亡值通常是指具有轉(zhuǎn)移語義的對象,比如返回右值引用T&&的函數(shù)返回值等。
C++11中,右值引用就是對一個右值進(jìn)行引用的類型。由于右值通常不具有名字,所以我們一般只能通過右值表達(dá)式獲得其引用,比如:
T && a=ReturnRvale();
假設(shè)ReturnRvalue()函數(shù)返回一個右值,那么上述語句聲明了一個名為a的右值引用,其值等于ReturnRvalue函數(shù)返回的臨時變量的值。
基于右值引用可以實現(xiàn)轉(zhuǎn)移語義和完美轉(zhuǎn)發(fā)新特性。
移動語義:
對于一個包含指針成員變量的類,由于編譯器默認(rèn)的拷貝構(gòu)造函數(shù)都是淺拷貝,所有我們一般需要通過實現(xiàn)深拷貝的拷貝構(gòu)造函數(shù),為指針成員分配新的內(nèi)存并進(jìn)行內(nèi)容拷貝,從而避免懸掛指針的問題。
但是如下列代碼所示:
當(dāng)類HasPtrMem包含一個成員函數(shù)GetTemp,其返回值類型是HasPtrMem,如果我們定義了深拷貝的拷貝構(gòu)造函數(shù),那么在調(diào)用該函數(shù)時需要調(diào)用兩次拷貝構(gòu)造函數(shù)。第一次是生成GetTemp函數(shù)返回時的臨時變量,第二次是將該返回值賦值給main函數(shù)中的變量a。與此對應(yīng)需要調(diào)用三次析構(gòu)函數(shù)來釋放內(nèi)存。
而在上述過程中,使用臨時變量構(gòu)造a時會調(diào)用拷貝構(gòu)造函數(shù)分配對內(nèi)存,而臨時對象在語句結(jié)束后會釋放它所使用的堆內(nèi)存。這樣重復(fù)申請和釋放內(nèi)存,在申請內(nèi)存較大時會嚴(yán)重影響性能。因此C++使用移動構(gòu)造函數(shù),從而保證使用臨時對象構(gòu)造a時不分配內(nèi)存,從而提高性能。
如下列代碼所示,移動構(gòu)造函數(shù)接收一個右值引用作為參數(shù),使用右值引用的參數(shù)初始化其指針成員變量。
其原理就是使用在構(gòu)造對象a時,使用h.d來初始化a,然后將臨時對象h的成員變量d指向nullptr,從而保證臨時變量析構(gòu)時不會釋放對內(nèi)存。
完美轉(zhuǎn)發(fā):
完美轉(zhuǎn)發(fā)是指在函數(shù)模板中,完全依照模板的參數(shù)的類型,將參數(shù)傳遞給函數(shù)模板中調(diào)用的另一個函數(shù),即傳入轉(zhuǎn)發(fā)函數(shù)的是左值對象,目標(biāo)函數(shù)就能獲得左值對象,轉(zhuǎn)發(fā)函數(shù)是右值對象,目標(biāo)函數(shù)就能獲得右值對象,而不產(chǎn)生額外的開銷。
因此轉(zhuǎn)發(fā)函數(shù)和目標(biāo)函數(shù)參數(shù)一般采用引用類型,從而避免拷貝的開銷。其次,由于目標(biāo)函數(shù)可能需要能夠既接受左值引用,又接受右值引用,所以考慮轉(zhuǎn)發(fā)也需要兼容這兩種類型。
C++11采用引用折疊的規(guī)則,結(jié)合新的模板推導(dǎo)規(guī)則實現(xiàn)完美轉(zhuǎn)發(fā)。其引用折疊規(guī)則如下:
因此,我們將轉(zhuǎn)發(fā)函數(shù)和目標(biāo)函數(shù)的參數(shù)都設(shè)置為右值引用類型,
當(dāng)傳入一個X類型的左值引用時,轉(zhuǎn)發(fā)函數(shù)將被實例為:
經(jīng)過引用折疊,變?yōu)椋?/p>
當(dāng)傳入一個X類型的右值引用時,轉(zhuǎn)發(fā)函數(shù)將被實例為:
經(jīng)過引用折疊,變?yōu)椋?/p>
除此之外,還可以使用forward()函數(shù)來完成左值引用到右值引用的轉(zhuǎn)換:
Lambda表達(dá)式:
Lambda表達(dá)式定義一個匿名函數(shù),并且可以捕獲一定范圍內(nèi)的變量,其定義如下:
capturemutable->return-type{statement}
其中,
[capture]:捕獲列表,捕獲上下文變量以供lambda使用。同時[]是lambda寅初復(fù),編譯器根據(jù)該符號來判斷接下來代碼是否是lambda函數(shù)。
(Params):參數(shù)列表,與普通函數(shù)的參數(shù)列表一致,如果不需要傳遞參數(shù),則可以連通括號一起省略。
mutable是修飾符,默認(rèn)情況下lambda函數(shù)總是一個const函數(shù),Mutable可以取消其常量性。在使用該修飾符時,參數(shù)列表不可省略。
->return-type:返回類型是返回值類型
{statement}:函數(shù)體,內(nèi)容與普通函數(shù)一樣,除了可以使用參數(shù)之外,還可以使用所捕獲的變量。
Lambda表達(dá)式與普通函數(shù)最大的區(qū)別就是其可以通過捕獲列表訪問一些上下文中的數(shù)據(jù)。其形式如下:
Lambda的類型被定義為“閉包”的類,其通常用于STL庫中,在某些場景下可用于簡化仿函數(shù)的使用,同時Lambda作為局部函數(shù),也會提高復(fù)雜代碼的開發(fā)加速,輕松在函數(shù)內(nèi)重用代碼,無須費心設(shè)計接口。
