機器人slam相關(guān)ros其他cpp1x tutorial第二章語言可用性的強化_第1頁
機器人slam相關(guān)ros其他cpp1x tutorial第二章語言可用性的強化_第2頁
機器人slam相關(guān)ros其他cpp1x tutorial第二章語言可用性的強化_第3頁
機器人slam相關(guān)ros其他cpp1x tutorial第二章語言可用性的強化_第4頁
機器人slam相關(guān)ros其他cpp1x tutorial第二章語言可用性的強化_第5頁
已閱讀5頁,還剩83頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

引第一C++11/14簡第二語言可用性的強第三語言運行期第四對標(biāo)準(zhǔn)庫的擴充:新增容第五對標(biāo)準(zhǔn)庫的擴充:智能指針和計第六對標(biāo)準(zhǔn)庫的擴充:正則表達(dá)第七對標(biāo)準(zhǔn)庫的擴充:語言級線程支第八其他雜第九擴展:C++17簡引C++算是一個用戶群體比較大的語言了,從C++98到C++11經(jīng)歷了長達(dá)十年多之久的積累,C++14則是作為對C++11的重要補充和優(yōu)化,所有這些新標(biāo)準(zhǔn)中擴充的特性,給C++這門語言注入了新的。那些還在堅持使用傳統(tǒng)C++(本把C++98及其之前的C++特性均稱之為傳統(tǒng)C++)而未接觸過C++11/14C++程序員在見到諸如Lambda表達(dá)式這類全C++1x(本中指C++11/14,甚至C++17)為傳統(tǒng)C++注入的大量特性使得整個C++變得更加像一門現(xiàn)代化的語言。C++1x不僅僅增強了C++語言自身的可用性,auto關(guān)鍵字語義的修改使得我們更加有信心來操控極度復(fù)雜的模板類型。同時還對語言運行期進(jìn)行了大量的強化,Lambda表達(dá)式的出現(xiàn)讓C++具有了『匿 為自身的標(biāo)準(zhǔn)庫增加了非常多的工具和方法,諸如在語言層面上提了語言層面的跨平臺支持;std::regex提供了完整的正則表達(dá)式支持等等。C++98已經(jīng)被實踐證明了是一種非常成功的『范型』,而C++1x的出現(xiàn),則進(jìn)一步推動這種范型,讓C++成為系統(tǒng)程序設(shè)計和庫開發(fā)更好的語言。目標(biāo)本假定讀者已經(jīng)熟悉了傳統(tǒng)C++,至少在閱讀傳統(tǒng)C++代碼上不具備任何。換句話說,那些長期使用傳統(tǒng)C++進(jìn)行編碼的人、渴望在短時間內(nèi)迅速了ModernC++特性的人非常適合閱讀本書;本一定程度上介紹了一些C++1x的黑魔法,但這些魔法畢竟有限,不適合希望進(jìn)階學(xué)習(xí)C++1x的讀者,本的定位系C++1x的快速上手。當(dāng)然,希望進(jìn)階學(xué)習(xí)的讀者可以使用本書來回顧并檢驗自己對ModernC++的熟悉本書本雖號稱高速上程,但實際上對C++11/14的相關(guān)特性做了一個較為全面的介紹,讀者可以自行根據(jù)下面的選取感的內(nèi)容進(jìn)行學(xué)習(xí),快速熟悉需要這些特性并不需要全部掌握,只需針對特定的應(yīng)用場景,學(xué)習(xí)、查閱最適合自己的新特性即可。值得一提的是,本在介紹這些特性的過程中,盡可能簡單明了的介紹了這些特性產(chǎn)生的歷史背景和技術(shù)需求,這為理解這些特性、運用這些特性提供了很大的幫助。內(nèi)容與C的兼容第二章語言可用性nullptr類型尾返回類型、autodecltype配合尖括號類型別名繼承顯式虛函數(shù)第三章語言運行期lambda表達(dá)函數(shù)對象包右值右值和左值完美第四章對標(biāo)準(zhǔn)庫的擴充:新增容第五章對標(biāo)準(zhǔn)庫的擴充:智能指針和計RAII與計std::regex及其相第七章對標(biāo)準(zhǔn)庫的擴充:語言級線程longlongnoexcept的修飾和操作非類型模板參數(shù)的結(jié)構(gòu)化綁定(Structured變量的強化贊如果你認(rèn)為本對你起到了幫助并希望贊助作者,可以通過下面的 支付交本在每節(jié)的最下方提供了評論,如果讀者發(fā)現(xiàn)中內(nèi)容的錯誤,可以使用評論或者通過發(fā)issue來;本書依然有很多特性沒有參與介紹,例如algas內(nèi)存對齊、聯(lián)合等,主要是考慮到這些特性的使用頻次實在是太低,故沒有多做介紹,若對未提及的特性有需求,筆者可以考慮將其加入第八章;個做得更好;本有以 ,有的讀者可以加入,加群請注明gitbook本書中涉及的相關(guān)代碼可以在上查看/changkun/cpp1x-致筆者感謝以下讀者本書中出現(xiàn)的錯誤RecolicKeghart,sinomiko,WangZhenhua,asm性使用-演繹4.0國際協(xié)議進(jìn)行。地址 Copyright?2016ChangkunOuat allright ,poweredbyGitbook該文件修改時間:2017-11-1609:30:52一、被棄用在學(xué)習(xí)C++1x之前,我們先了解一下從C++11開始,被棄用的主要特性:失,應(yīng)該盡量避免使用。但是,已棄用的特性依然是標(biāo)準(zhǔn)庫的一部分,并且出于兼容性的考慮,這些特性其實會『永久』保留。如果一個類有析構(gòu)函數(shù),為其生成拷貝構(gòu)造函數(shù)和拷貝賦值運算符的特性被棄用了。 常量賦值和初始化一個char*,應(yīng)該使用constchar*或者autocharchar*str oworld!"C++98異常說明、unexcepted_handler、set_unexpected等相關(guān)特性被棄用,應(yīng)該使用noexcept。auto_ptr被棄用,應(yīng)使用unique_ptrregister關(guān)鍵字被棄用bool類型的操作被棄 static_cast、reinterpret_cast、const_cast來進(jìn)行類型轉(zhuǎn)換還有一些其他諸如參數(shù)綁定(C++11提供了std::bindstfncin)、expot等特性也均被棄用。前面提到的這些特性如果你從未使用或者聽,也請不要嘗試去了解他們,應(yīng)該向新標(biāo)準(zhǔn)靠攏,直接學(xué)習(xí)新特性。畢竟,技術(shù)是向前發(fā)展的。二、與C的兼容出于一些不可抗力、歷史原因,我們不得不在C++中使用一些C語言代碼(甚至古老的C語言代碼),例如Linux系統(tǒng)調(diào)用。在++1出現(xiàn)之前,大部分人當(dāng)談及『C與C++的區(qū)別是什么』時,普遍除了回答面向?qū)ο蟮念愄匦?、泛型編程的模板特性外,就沒有其他的看法了,甚至直接回答『差不多』,也是大有人在。下面的韋恩圖大致上回答了C和C++相關(guān)的兼容情況:一開始就不是,后面的進(jìn)一步閱讀的參考文獻(xiàn)中給出了C++98C99之間的區(qū)別)。在編寫C++時,也應(yīng)該盡可能的避免使用諸如void*之類的程序風(fēng)格。而在不得不使用C時,應(yīng)該注意使用extern"C"這種特性,將C語言的代碼與C++代碼進(jìn)行分離編譯,再統(tǒng)一這種做法,例如:////#ifdefcplusplusextern"C"{intadd(intx,inty);#ifdefcplusplus}//intadd(intx,int{reutrn}//main.cppintmain(){add(1,return}應(yīng)先使用gcc編譯C語言的代碼gccgcc-c編譯出foo.o文件,再使用g++將C++代碼和.o文件起來(或者都編譯為.o再統(tǒng)一):g++g++main.cppfoo.o-o進(jìn)一步閱讀C++1x特性在GCC/ClangC++98C99之間的區(qū)Copyright?2016ChangkunOuat allright,poweredbyGitbook該文件修改時間:2017-11-1609:30:52一、本節(jié)內(nèi)容包語言可用性的nullptr類型尾返回類型、autodecltype配合尖括號面向?qū)ο罄^承顯式虛函數(shù)總二、nullptr出現(xiàn)的目的是為了替代NULL。在某種意義上來說,傳統(tǒng)C++會NULL、0視為同一種東西,這取決于編譯器如何定義NULL,有些編譯器會將NULL定義為((void*)0),有些則會直接將其定義為0。C++不允許直接將void*隱式轉(zhuǎn)換到其他類型,但如果NULL被定義((void*)0,那么當(dāng)charchar*ch=時,NULL只好被定義為0。而這依然會產(chǎn)生問題,將導(dǎo)C中重載特性會發(fā)生,考慮:voidvoidfoo(char*);voidfoo(int);對于這兩個函數(shù)來說,如果NULL又被定義為了0那么foo(NULL這個語句將會去調(diào)用foo(int),從而導(dǎo)致代碼直觀。為了解決這個問題,C++11引入了nullptr關(guān)鍵字,專門用來區(qū)分空0。nullptr的類型為nullptr_t,能夠隱式的轉(zhuǎn)換為任何指針或成員指針的你可以嘗試使用gcc和g++兩個編譯器同時編譯下面的代碼voidfoo(char*);voidfoo(int);intmain()if(NULL==(void*)0)std::cout<<"NULL==0"<<std::endl;elsestd::cout<<"NULL!=0"<<std::endl;foo(NULLreturn}voidfoo(char*ch)std::cout<<"callfoo(char*)"<<}voidfoo(inti)std::cout<<"callfoo(int)"<<}將輸NULLNULL==callfoo(int)所以,當(dāng)需要使用NULL時候,請養(yǎng)成直接使用nullptr的習(xí)慣C++本身已經(jīng)具備了常數(shù)表達(dá)式的概念,比如1+2,3*4這種表達(dá)式總是會產(chǎn)生相同的結(jié)果并且沒有任何副作用。如果編譯器能夠在編譯時就把這些表達(dá)式直接優(yōu)化并植入到程序運行時,將能增加程序的性能。一個非常顯著的例子就是在數(shù)組的定義階段:#define#defineLENint{return}intmain()chararr_1[10];intlen=5;constintlen_2=10;chararr_4[len_2];charreturn}在C++11之前,可以在常量表達(dá)式中使用的變量必須被為const,在上面代碼中,len_2被定義成了常量,因此len_2是一個常量表達(dá)式,所以能夠合而對于arr_5來說,C++98之前的編譯器無法得知len_foo()在運行期實際C++11提供了constexpr讓用戶顯式的函數(shù)或?qū)ο髽?gòu)造函數(shù)在編譯器會成為常數(shù),這個關(guān)鍵字明確的告訴編譯器應(yīng)該去驗證len_foo在編譯器就應(yīng)該是一此外,constexpr的函數(shù)可以使用遞constexprconstexprintfibonacci(constintn)returnn==1||n==2?1:fibonacci(n-1)+fibonacci(n-}從C++14開始,constexptr函數(shù)可以在局部變量、循環(huán)和分支等簡單語句,例如下面的代碼在C++11的標(biāo)準(zhǔn)下是不能夠通過編譯的:constexprconstexprintfibonacci(constint{if(n==1)returnif(n==2)return}三、在傳統(tǒng)C和++中,參數(shù)的類型都必須明確定義,這其實對我們快速進(jìn)行編碼沒有任何幫助,尤其是當(dāng)我們面對一大堆復(fù)雜的模板類型時,必須明確的變量的類型才能進(jìn)行后續(xù)的編碼,這不僅拖慢我們的開發(fā)效率,也讓代碼變得又臭又長。C++11引入了auto和declype這兩個關(guān)鍵字實現(xiàn)了類型推導(dǎo),讓編譯器來操心變量的類型。這使得C++也具有了和其他現(xiàn)代編程語言一樣,某種意義上提auto在很早以前就已經(jīng)進(jìn)入了C++,但是他始終作為一個類型的指示符存在,與register并存。在傳統(tǒng)C++中,如果一個變量沒有為register變量,將自動被視為一個auto變量。而隨著register被棄用,對auto的使用auto進(jìn)行類型推導(dǎo)的一個最為常見而且顯著的例子就是迭代器。在以前我end();toritr=vec.cbegin);itr!=而有了auto之后可以////由于cbegin()將返回 //所以itr也應(yīng)該是 tor類for(autoitr=vec.cbegin();itr!=vec.cend();一些其他的常autoautoi=//i被推導(dǎo)為autoarrnewauto(10arrintintintadd(autox,auto此外,auto還不能用于推導(dǎo)數(shù)組類#include#includeint{autoi=intarr[10]={0};autoauto_arr=arr;autoauto_arr2[10]=return}decltype關(guān)鍵字是為了解決auto關(guān)鍵字只能對變量進(jìn)行類型推導(dǎo)的缺陷而出現(xiàn)的。它的用法和sizeof很相似:有時候,我們可能需要計算某個表達(dá)式的類型,例如:autoautox=1;autoy=2;尾返回類型、autodecltype你可能會思考,auto能不能用于推導(dǎo)函數(shù)的返回類型。考慮這樣一個例子加法函數(shù)的例子,在傳統(tǒng)C++中須這么寫:te<typenameR,typenameT,typenameRadd(Tx,Uy){returnx+y}typename和class在模板中沒有區(qū)別,在typename這個關(guān)鍵字出現(xiàn)之前,都是使用class來定義模板參數(shù)的這樣的代碼其實變得很,因為程序員在使用這個模板函數(shù)的時候,必須明確指出返回類型。但事實上我們并不知道add()這個函數(shù)會做什么樣的操作,獲得一個C++11中這個問題得到解決。雖然你可能馬上回反應(yīng)出decltypex+y的類型,寫出這樣的代decltypedecltype(x+y)add(Tx,U但事實上這樣的寫法并不能通過編譯。這是因為在編譯器讀 時,xy尚未被定義。為了解決這個問題,C++11還引入了一個叫做尾返回類型(trailingreturntype),利用auto關(guān)鍵字將返回類型后置:te<typenameT,typenameautoadd(Tx,Uy)->{return}te<typenameT,typenameautoadd(Tx,Uy){returnx+y}四、基于范圍forintintarray[]={1,2,3,4,5};for(auto&x:array){std::cout<<x<<}最常用的std::vector遍歷將從原來的樣子stdstd::vector<int>arr(5,++i)tori=arr.begin();i!=std::cout<<*i<<}變得非常的簡&如果沒有則對arrfor(auto&i:arr)std::cout<<i<<}五、初始化初始化是一個非常重要的語言特性,最常見的就是對對象進(jìn)行初始化。在傳統(tǒng)C++中,不同的對象有著不同的初始化方法,例如普通數(shù)組、POD(inolddata,沒有構(gòu)造、析構(gòu)和虛函數(shù)的類或結(jié)構(gòu)體)類型都可以使用{}進(jìn)行初始化,也就是需要使用()進(jìn)行。這些不同方法都針對各自對象,不能通用。intintarr[3]=class{Foo(int)Foo為了解決這個問題,C++11首先把初始化列表的概念綁定到了類型上,并將其稱之std::initializer_list,允許構(gòu)造函數(shù)或其他函數(shù)像參數(shù)一樣使用初始化列表,這就為類對象的初始化與普通數(shù)組和POD的初始化方法提供了統(tǒng)一的橋#include#includeclass{Magicmagic={1,2,3,4,5};std::vector<int>v={1,2,3,4};這種構(gòu)造函數(shù)被叫做初始化列表構(gòu)造函數(shù),具有這種構(gòu)造函數(shù)的類型將在初始化時被特殊關(guān)照。初始化列表除了用在對象構(gòu)造上,還能將其作為普通函數(shù)的形參,例如:voidvoidfoo(std::initializer_list<int>其次 提供了統(tǒng)一的語法來初始化任意的對象,例如structstructAintfloatstructBB(int_a,float_b):a(_a),b(_b)inta;floatb;Aa{1,Bb{2,六、傳統(tǒng)++中,模板只有在使用時才會被編譯器實例化。換句話說,只要在每個編譯單元(文件)中編譯的代碼中遇到了被完整定義的模板,都會實例化。這就產(chǎn)生了重復(fù)實例化而導(dǎo)致的編譯時間的增加。并且,我們沒有辦法通知編譯器不要出發(fā)模板實例化。C++11引入了外部模板,擴充了原來的強制編譯器在特定位置實例化模板的語法,teclassexternteclass尖括號在傳統(tǒng)C的編譯器中,一律被當(dāng)做右移運算符來進(jìn)行處理。但實際上我們這在傳統(tǒng)C++編譯器下是不能夠被編譯的,而C++11開始,連續(xù)的右尖括號將變得te<boolT>std::vector<SuckType<(1>2v;合法類型別名模板在了解類型別名模板之前,需要理解『模板』和『類型』之間的不同。仔細(xì)體會這句話:模板是用來產(chǎn)生類型的。在傳統(tǒng)C++中,typedf可以為類型定義一個新te<typenameT,typenameclasstypedefSuckType<std::vectorstd::stringNewTypeC++11使用using引入了下面這種形式的寫法,并且同時支持對typedef相同的功效通常我們使用typedf定義別名的語法是:typeef原名稱;,但是對函數(shù)指針等別名的定義語法卻不相同,這通常給直接閱讀造成了一定程度的。typedeftypedefint(*process)(void*);int,參數(shù)為void*的函數(shù)指針類型,名字叫做processusingprocessvoid(*)(void*同上usingNewType=SuckType<std::vector,默認(rèn)模板參數(shù)我們可能定義了一個加法函數(shù)te<typenameT,typenameautoadd(Tx,Uy)->{return}但在使用時發(fā)現(xiàn),要使用add,就必須每次都指定其模板參數(shù)的類型。在C++11中提供了一種便利,可以指定模板的默認(rèn)參數(shù):te<typenameT=int,typenameU=autoadd(Tx,Uy)->decltype(x+y){returnx+y}變長參數(shù)模板模板一直是C++所獨有的黑魔法(一起念:DarkMagic)之一。在C++11之前,數(shù);而C++11加入了新的表示方法,允許任意個數(shù)、任意類別的模板參數(shù),同時 te<typename...Ts>class模板類Magic的對象,能夠接受不受限制個數(shù)的typename作為模板的形式參數(shù),classclassstd::vector<int>>>既然是任意形式,所以個數(shù)為0的模板參數(shù)也是可以的:classMagic<>nothing;。如果不希望產(chǎn)生的模板參數(shù)個數(shù)為0,可以手動的定義至少一個模板參數(shù): te<typenameRequire,typename...Args>class變長參數(shù)模板也能被直接調(diào)整到到模板函數(shù)上。傳統(tǒng)C中的printf函數(shù),雖然也能達(dá)成不定個數(shù)的形參的調(diào)用,但其并非類別安全。而C++11除了能定義類別安全的變長參數(shù)函數(shù)外,還可以使類似printf的函數(shù)能自然地處理非自帶類別的對象。除了在模板參數(shù)中能使用...表示不定長模板參數(shù)外,函數(shù)參數(shù)也使用同樣的表te<typename...Args>voidprintf(conststd::string&str,rgs...那么我們定義了變長的模板參數(shù),如何對參數(shù)進(jìn)行解包呢首先,我們可以sizeof...來計算參數(shù)的個數(shù)voidmagic(Args...args)std::cout<<sizeof...(args)<<}我們可以傳遞任意個參數(shù)給magic函數(shù)magic(1,輸出輸出輸出其次,對參數(shù)進(jìn)行解包,到目前為止還沒有一種簡單的方法能夠處理參數(shù)包,但有兩種經(jīng)典的處理手法:遞歸模板遞歸是非常容易想到的一種,也是最經(jīng)典的處理方法。這種方法不斷遞歸地向函數(shù)傳遞模板參數(shù),進(jìn)而達(dá)到遞歸遍歷所有模板參數(shù)的目的:#include<iostream>#include<iostream> te<typenameT>voidprintf(Tvalue){std::cout<<value<<} te<typenameT,typename...Args>voidprintf(Tvalue,Args...args){std::cout<<value<<std::endl;}intmain()printf(1,2,"123",return}初始化列表這個方法需要之后介紹的知識,讀者可以簡單閱讀以下,將這個代碼段保存,在后面的內(nèi)容了解過了之后再回過頭來閱讀此處方大有收獲。遞歸模板函數(shù)是一種標(biāo)準(zhǔn)的做法,但缺點顯而易見的在于必須定義一個終止遞歸的函數(shù)。這里介紹一種使用初始化列表展開的- te<typenameT,typename...Args>autoprint(Tvalue,Args...args){std::cout<<value<<std::endl;returnstd::initializer_list<T>{([&]{std::cout<<args<<}(),}intmain()print(1,2.1,return}在這個代碼中,額外使用了C++11中提供的初始化列表以及Lambda表達(dá)式的特(下一節(jié)中將提到),而std::initializer_list也是C++11新引入的容器(以后會介紹通過初始化列表,(lambda表達(dá)式value).將會被展開。由于逗號表達(dá)式的出現(xiàn),首先會執(zhí)行前面的lambda表達(dá)式,完成參數(shù)的輸出。唯一不美觀的地方在于如果不使用return編譯器會給出未使用的變量作為警告。我們可以利用std::bind及完美轉(zhuǎn)發(fā)等特性實現(xiàn)對函數(shù)和參數(shù)的綁定,從而關(guān)于這方面的使用技巧,可以通過項目課:100行C++代碼實現(xiàn)線程池進(jìn)行七、面向?qū)intvalue1;intvalue2;Base(){value1=}Base(intvalueBaseBasevalue2=}intmain()Basestd::cout<<b.value1<<std::endl;std::cout<<b.value2<<}低下。C++11利用關(guān)鍵字using引入了繼承構(gòu)造函數(shù)的概念:{intvalue1;intvalue2;Base(){value1=}Base(intvalueBase()Basevalue2=}classSubclass:public{using intmain()Subclassstd::cout<<s.value1<<std::endl;std::cout<<s.value2<<}顯式虛函數(shù)在傳 C++中,經(jīng)常容易發(fā)生意外重載虛函數(shù)的事情。例如structstructBasevirtualvoidstructSubClass:{voidSubCass:foo可能并不是程序員嘗試重載虛函數(shù),只是恰好加入了一個具有相同名字的函數(shù)。另一個可能的情形是,當(dāng)基類的虛函數(shù)被刪除后,子類擁有舊的函數(shù)就不再重載該虛擬函數(shù)并搖身一變成為了一個普通的類方法,這將造成性的。C++11引入了overridefinal這兩個關(guān)鍵字來防止上述情形的發(fā)structstructBasevirtualvoidstructSubClass:Basevirtualvoidfoo(intoverridevirtualvoidfoo(floatoverride final則是為了防止類被繼續(xù)繼承以及終止虛函數(shù)繼續(xù)重載引入的。structstructBasevirtualvoidfoo()structSubClass1final:Base structSubClass2:SubClass1 SubClass1已structSubClass3:Basevoidfoo() ,foo已顯式禁用默認(rèn)構(gòu)造、賦值算符以及析構(gòu)函數(shù)。另外,C++也為所有類定義了諸如newdelete這樣的運算符。當(dāng)程序員有需要時,可以重載這部分函數(shù)。這就了一些需求:無法精確控制默認(rèn)函數(shù)的生成行為。例如類的拷貝時,必須將賦值構(gòu)造函數(shù)與賦值算符為privte。嘗試使用這些未定義的函數(shù)將導(dǎo)致編譯或錯誤,則是一種非常不優(yōu)雅的方式。并且,編譯器產(chǎn)生的默認(rèn)構(gòu)造函數(shù)與用戶定義的構(gòu)造函數(shù)無法同時存在。若用戶定義了任何構(gòu)造函數(shù),編譯器將不再生成默認(rèn)構(gòu)造函數(shù),但有時候我們卻希望同時擁有這兩種構(gòu)造函數(shù),這就造成了尷尬。C++11提供了上述需求的解決方案,允許顯式的采用或編譯器自帶的函classclass{Magic()default;Magic&operator=(constMagic&)=delete;//顯 Magic(int}八、強類型在傳統(tǒng)++中,枚舉類型并非類型安全,枚舉類型會被視作整數(shù),則會讓兩種完全不同的枚舉類型可以進(jìn)行直接的比較(雖然編譯器給出了檢查,但并非所有),甚至枚舉類型的枚舉值名字不能相同,這不是我們希望看到的結(jié)果。C++11引入了枚舉類(enumarationclass),并使用enumclass的語法進(jìn)行聲enumenumclassnew_enum:unsigned{value1,value3=100,value4=這樣定義的枚舉實現(xiàn)了類型安全,首先他不能夠被隱式的轉(zhuǎn)換為整數(shù),同時也不能夠?qū)⑵渑c整數(shù)數(shù)字進(jìn)行比較,更不可能對不同的枚舉類型的枚舉值進(jìn)行比較。但相同枚舉值之間如果指定的值相同,那么可以進(jìn)行比較:ifif(new_enum::value3==new_enum::value4)std::cout<<"new_enum::value3==new_enum::value4"<<std::}在這個語法中,枚舉類型后面使用了冒號及類型關(guān)鍵字來指定枚舉中枚舉值的類型,這使得我們能夠為枚舉賦值(未指定時將默認(rèn)使用int)。而我們希望獲得枚舉值的值時,將必須顯式的進(jìn)行類型轉(zhuǎn)換,不過我們可以通過重載這個算符來進(jìn)行輸出,可以收藏下面這個代碼段:#include<iostream>#include<iostream> std::ostream&operator<<(typename::value,std::ostream>::type&stream,constT&{returnstream<<static_cast<typename}這時,下面的代碼將能夠stdstd::cout<<new_enum::value3<<總auto類型推范圍for迭初始化列變參模進(jìn)一步閱讀深入理解C++11:C++11新特性解析與應(yīng)用.MichaelWong,IBMXL編譯器中深入應(yīng)用C++11:代碼優(yōu)化與工程級應(yīng)用.Copyright?2016ChangkunOuat allright ,poweredbyGitbook該文件修改時間:2017-11-1609:30:52一、本節(jié)內(nèi)容包語言運行期的lambda表達(dá)函數(shù)對象包右值右值和左值完美二、Lambda表達(dá)Lambda表達(dá)式是++1中最重要的新特性之一,而Lambda表達(dá)式,實際上就是提供了一個類似函數(shù)的特性,而函數(shù)則是在需要一個函數(shù),但是又不想費力去命名一個函數(shù)的情況下去使用的。這樣的場景其實有很多很多,所以函數(shù)幾乎是現(xiàn)代編程語言的標(biāo)配。Lambda表達(dá)式基礎(chǔ) 表達(dá)式的基本語[[捕獲列表](參數(shù)列表mutable(可選}上面的語則除了[捕獲列表]內(nèi)的東西外,其他部分都很好理解,只是一般函數(shù)的函數(shù)名被略去,返回值使用了一個的形式進(jìn)行(我們在上一節(jié)前面的尾所謂捕獲列表,其實可以理解為參數(shù)的一種類型,lambda表達(dá)式內(nèi)部函數(shù)體在默認(rèn)情況下是不能夠使用函數(shù)體外部的變量的,這時候捕獲列表可以起到傳遞外部數(shù)據(jù)的作用。根據(jù)傳遞的行為,捕獲列表也分為以下幾種:與參數(shù)傳值類似,值捕獲的前期是變量可以拷貝,不同之處則在于,獲的變量在lambda表達(dá)式被創(chuàng)建時拷貝,而非調(diào)用時才拷貝:voidvoid{intvalue_1=autocopy_value_1={returnvalue_1=autostored_value_1=這時stored_value_11而value_1因為copy_value_1value_1}捕獲 傳參類似 捕獲保存的 ,值會發(fā)生變化voidvoid{intvalue_2=autocopy_value_2={returnvalue_2=autostored_value_2=這時stored_value_2100value_1copy_value_2}隱式理,這時候可以在捕獲列表中寫一個&或=向編譯器采用捕獲或者[]空捕獲列[name1,name2,...]捕獲一系列[&]捕獲,讓編譯器自行推導(dǎo)捕獲列[=]值捕獲,讓編譯器執(zhí)行推導(dǎo)應(yīng)用列表表達(dá)式捕獲這部分內(nèi)容需要了解后面馬上要提到的右值以及智能指上面提到的值捕獲、捕獲都是已經(jīng)在外層作用域的變量,因此這些捕獲方式捕獲的均為左值,而不能捕獲右值。auto本質(zhì)上是相同的#include<utility>intmain()autoimportant=autoadd=[v1=1,v2=std::move(important)](intx,int->intreturnstd::cout<<add(3,4)<<std::endl;return0;}在上面的代碼中,important是一個獨占指針,是不能夠獲到的,這時候我上一節(jié)中我們提到了auto關(guān)鍵字不能夠用在參數(shù)表里,這是因為這樣的寫數(shù)可以使用auto關(guān)鍵字來產(chǎn)生意義上的泛型:autoautoadd=[](autox,auto{returnadd(1,add(1.1,二、函數(shù)對Lambda表達(dá)式的本質(zhì)是一個函數(shù)對象,當(dāng)Lambda表達(dá)式的捕獲列表為空時, 表達(dá)式還能夠作為一個函數(shù)指針進(jìn)行傳遞,例#include#includeusingfoovoid(int);定義函數(shù)指針using的使用見上一節(jié)中的別名voidfunctional(foo{}intmain()autof=[](intvalue)std::cout<<value<<functional(f);//函數(shù)指針調(diào)用 //lambda表達(dá)式調(diào)用return0;}上面的代碼給出了兩種不同的調(diào)用形式,一種是將Lambda作為函數(shù)指針傳遞進(jìn)行調(diào)用,而另一種則是直接調(diào)用Lambda表達(dá)式,在C++11中,統(tǒng)一了這些概念,std::function引入的C++11std::function是一種通用、多態(tài)的函數(shù)封裝,它的實例可以對任何可以調(diào)用的目標(biāo)實體進(jìn)行、和調(diào)用操作,它也是對C++中現(xiàn)有的可調(diào)用實體的#include<iostream>intfoo(int{return}intmain()std::functionint,intstd::function<int(int)>func=intimportant=std::function<int(int)>func2=[&](intvalue)->{returnstd::cout<<func(10)<<std::endl;std::cout<<func2(10)<<std::endl;}std::bind/std::而std:bid則是用來綁定函數(shù)調(diào)用的參數(shù)的,它解決的需求是我們有時候可能并不一定能夠獲得調(diào)用某個函數(shù)的全部參數(shù),通過這個函數(shù),我們可以將部分調(diào)用參數(shù)提前綁定到函數(shù)身上成為一個新的對象,然后在參數(shù)齊全后,完成調(diào)用。例intintfoo(inta,intb,intc);}intmain()將參數(shù)1,2fooceholders::_1autobindFoo=std::bind(foo, ceholders::_1,這時調(diào)用bindFoo}值類型,但是我們卻可以通過auto的使用來規(guī)避這一問題的出現(xiàn)。三、右值右值是C++11引入的與Lambda表達(dá)式齊名的重要特性之一。它的引入解決了C中大量的歷史遺留問題,消除了std::vector、std::string之類的額外開銷,也才使得函數(shù)對象容器std::function成為了可能。左值、右值的純右值、將亡值、右值要弄明白右值到底是怎么一回事,必須要對左值和右值做一個明確的理解右值 value),右邊的值,是指表達(dá)式結(jié)束后就不再存在的臨時對象純右值(prvalue,purervalue),純粹的右值,要么是純粹的字面量10true;要么是求值結(jié)果相當(dāng)于字面量或臨時對象,例如1+2。非返回的臨時變量、運算表達(dá)式產(chǎn)生的臨時變量、原始字面量、Lambda表達(dá)式都屬于純將亡值(xvalue,expiringvalue),是C++11為了引入右值而概念(因此在傳統(tǒng)C++中,純右值和右值是統(tǒng)一個概念),也就是即將被銷毀、卻能夠被移動將亡值可能稍有些難以理解,我們來看這樣的代碼:stdstd::vector<int>{std::vector<int>temp={1,2,4};return}std::vector<int>v=在這樣的代碼中foo的返回值temp在內(nèi)部創(chuàng)建然后被賦值給v,然而v獲得這個對象時,會將整個temp拷貝一份,然后把temp銷毀,如果這個temp非常大,這將造成大量額外的開銷(這也就是傳統(tǒng)C++一直被詬病的問題)。在最后一行中,v是左值、foo(返回的值就是右值(也是純右值)但是,v可以被別的變量捕獲到,而foo()產(chǎn)生的那個返回值作為一個臨時值,一旦被v后,將立即被銷毀,無法獲取、也不能修改。將亡值就定義了這樣一種行為:臨時的值能夠被識別、同時又能夠被移動。右值和左需要拿到一個將亡值,就需要用到右值的申明:T&&,其中T是類型。值的讓這個臨時值的生命周期得以延長、只要變量還活著,那么將亡值將繼續(xù)存活。#include<iostream>#include<string>voidreference(std::string&std::cout左值}voidreference(std::string&&std::cout右值}int{std::stringlv1=lv1//std::string&&r1=std::string&&rv1std::move(lv1合法std::move可以將左std::cout<<rv1<< //conststd::string&lv2=lv1+lv1;//合法,常量左值能夠延//lv2+= //,的右值無法被修std::cout<<lv2<<std::endl; //string,stringstd::string&&rv2=lv1+lv2; //合法,右值延長臨時對rv2+="Test"; //合法,非常量能夠修改std::cout<<rv2<<std::endl; //輸出左值return}注意:rv2雖然了一個右值,但由于它是一個,所以rv2依然是一個傳統(tǒng)++通過拷貝構(gòu)造函數(shù)和賦值操作符為類對象設(shè)計了拷貝/的概念,但了實現(xiàn)對資源的移動操作,調(diào)用者必須使用先、再析構(gòu)的方式,否則就需要自己實現(xiàn)移動對象的接口。試想,搬家的時候是把家里的東西直接搬到新家去,而不是將所有東西一份(重買)再放到新家、再把原來的東西全部扔掉(銷毀),這是非常的一件事情。傳統(tǒng)的C++沒有區(qū)分『移動』和『拷貝』的概念,造成了大量的數(shù)據(jù)移動,浪費時間和空間。右值的出現(xiàn)恰好就解決了這兩個概念的問題,例如:classA{intA():pointer(newint(1std::cout"構(gòu)造pointerstd::endl;}A(A&a):pointer(newint(*a.pointer)){std::cout<<"拷貝"<<pointer<<std::endl;} //無意義的對象拷貝A(A&&a):pointer(a.pointer){a.pointer="移動pointerstd::endl~A(std::cout"析構(gòu)pointerstd::endldeletepointer;}Areturn_rvalue(bool{Aif(test)returna;elsereturnb;}intmain()Aobj=return_rvalue(false);std::cout<<"obj:"<<std::endl;std::cout<<obj.pointer<<std::endl;std::cout<<*obj.pointer<<std::endl;return}在上面的代碼首先會在return_rvalue內(nèi)部構(gòu)造兩個A對象,于是獲得兩個構(gòu)造函數(shù)函數(shù)返回后,產(chǎn)生一個將亡值,被A的移動構(gòu)造(A(A&&)),從而延長生命周期,并將這個右值中的指針拿到,保存到了obj中,而將亡值的指針被設(shè)置為nullptr,防止了這塊內(nèi)存區(qū)域被銷毀。從而避免了無意義的拷貝構(gòu)造,加強了性能。再來看看涉及標(biāo)準(zhǔn)庫的例子:#include<iostream>//std::cout#include<utility>#include<iostream>//std::cout#include<utility>//std::move#include //#include //intmain()std::stringstr=ostd::vector<std::string>將使用push_back(constT&//將輸出 ostd::cout<<"str:"<<str<<將使用push_back(constT&&vectorstd::move會用來減少這步操作后str//將輸出"strstd::cout<<"str:"<<str<<return}前面我們提到了,一個的右值其實是一個左值。這就為我們進(jìn)行參數(shù)轉(zhuǎn)(傳遞)造成voidvoidreference(int&v)std::cout"左值}voidreference(int&&v)std::cout"右值} te<typenameT>voidpass(T&&v){std::cout"普通傳參 始終調(diào)用reference(int&}intmain()std::cout<<"傳遞右值:"<<std::endl; //1是右值,但輸出左值std::cout"傳遞左值std::endl;intv=1; //r是 ,輸出左return}對于pass(1)來說,雖然傳遞的是右值,但由 是一個,所以同時是左值。

reference(v會調(diào)用reference(int&),輸出『左值』。于pass(v而言,v是一個左值,為什么會成功傳遞給pass(T&&呢這是基于坍縮規(guī)則的:在傳統(tǒng)C++中,我們不能夠?qū)σ粋€類型繼續(xù)進(jìn)行,但C++由于右值的出現(xiàn)而放寬了這一做法,從而產(chǎn)生了坍縮規(guī) 。但是卻遵循如下規(guī)則:函數(shù)形參實參參數(shù)推導(dǎo)后函數(shù)形左右左右因此,模板函數(shù)中使用T&&不一定能進(jìn)行右值,當(dāng)傳入左值時,此函數(shù)的將被推導(dǎo)為左值。更準(zhǔn)確的講,無論模板參數(shù)是什么類型的,當(dāng)且僅當(dāng)實參類型為右時,模板參數(shù)才能被推導(dǎo)為右類型。這才使得v作為左值的成功完美轉(zhuǎn)發(fā)就是基于上述規(guī)律產(chǎn)生的。所謂完美轉(zhuǎn)發(fā),就是為了讓我們在傳遞參數(shù)的時候,保持原來的參數(shù)類型(左保持左,右保持右)。為了解決這個問題,我們應(yīng)該使用std::owrd來進(jìn)行參數(shù)的轉(zhuǎn)發(fā)(傳遞):#include<utility>voidreference(int&v)std::cout<<"左 "<<}voidreference(int&&v)std::cout<<"右 "<<} te<typenameT>voidpass(T&&v){std::cout"普通傳參:";std::cout<<"std::move傳參:";std::cout"std::forward傳參:";}intmain()std::cout"傳遞右值std::endl;std::cout"傳遞左值std::endl;intv=1;return}輸出結(jié)果傳遞右值傳遞右值std::move傳參:右值std::move傳參:右值無論傳遞參數(shù)為左值還是右值,普通傳參都會將參數(shù)作為左值進(jìn)行轉(zhuǎn)發(fā),所以std::move總會接受到一個左值,從而轉(zhuǎn)發(fā)調(diào)用了reference(int&&輸出右std::forwardstd::move一樣,沒有做任何事情,std::move單純的將左值轉(zhuǎn)化為右值,std::forward也只是單純的將參數(shù)做了一個類型的轉(zhuǎn)換,從是線上來看,std::forward<T>(v)static_cast<T&&>(v)總Lambda表達(dá)函數(shù)對象容器右Copyright?2016ChangkunOuat allright,poweredbyGitbook該文件修改時間:2017-11-1609:30:52一、本節(jié)內(nèi)容包對標(biāo)準(zhǔn)庫的擴充:新增二、std::array看到這個容器的時候肯定會出現(xiàn)這樣為什么要引入std::array而不是直接使用std::vector已經(jīng)有了傳統(tǒng)數(shù)組,為什么要用std::array先回答第一個問題,std::vecotr太強大了,以至于我們沒有必要為了去敲碎一個雞蛋而用一個釘錘。使用std::array保存在棧內(nèi)存中,相比堆內(nèi)存中的std::vector,我們就能夠靈活的這里面的元素,從而獲得更高的性能;同而第二個問題就更加簡單,使用std::array能夠讓代碼變得更加現(xiàn)代,且封裝std::sortstd::array會在編譯時創(chuàng)建一個固定大小的數(shù)組,std::array不能夠被隱式的轉(zhuǎn)換成指針,使用std::array很簡單,只需指定其類型和大小即可:stdstd::array<int,4>arr=intlen=std::array<int,len>arr={1,2,3,4};當(dāng)我們開始用上了std::array時,難免會遇到要將其兼容C風(fēng)格的接口,這里voidvoidfoo(int*p,int{}std::array<int,4>arr=C//foo(arr,foo(&arr[0],foo(arr.data(),//使用std::forward_list是一個列表容器,使用方法和std::list基本類似,因需要知道的是,和std::list的雙向鏈表的實現(xiàn)不同,使用單向鏈表進(jìn)行實現(xiàn)O(1)復(fù)雜度的元素插入,不支持快速隨機(這也是鏈表的特點),也是標(biāo)準(zhǔn)庫容器中唯一一個不提供size()方法的容器。當(dāng)不需要雙向迭代時,具有比std::list更高的空間利用率。三、我們已經(jīng)熟知了傳統(tǒng)C++中的有序通 樹進(jìn)行實現(xiàn),插入和搜索的平均復(fù)雜度均

,這些元素。在插入素時候,會根據(jù)<操作符比較元素大小并判斷元素是否相同,并選擇合適的位置插入到容器中。當(dāng)對這個容器中的元素進(jìn)行遍歷時,輸出結(jié)果會按照<操作符的而無序容器中的元素是不進(jìn)行排序的,內(nèi)部通過Hash表實現(xiàn),插入和搜索元素的平均復(fù)雜度為O(constant),在不關(guān)心容器內(nèi)部元素順序時,能夠獲得顯著的性C++11引入了兩組無器:std::unordered_map/std::unordered_multimapstd::unordered_set/std::unordered_multiset它們的用法和std::map/std::multimap/std::set/set::multiset基本類似,由于這下std::map和std::multimap#include<iostream>#include<string>#include<iostream>#include<string>#include<map>intmain()std::unordered_map<int,std::string>u={1,{3,{2,std::map<int,std::string>v={1,{3,{2,std::cout<<"std::unordered_map"<<std::endl;for(constauto&n:u)std::cout<<"Key:["<<n.first<<"]Value:["<<n.second<<"]\n";std::cout<<std::cout<<"std::map"<<std::endl;for(constauto&n:v)std::cout<<"Key:["<<n.first<<"]Value:["<<n.second<<"]\n";}最終的輸出結(jié)Key:[2]Value:[2]Key:[3]Key:[1]Key:[1]Key:[2]Key:[3]四、元組了解過Python的程序員應(yīng)該知道元組的概念,縱觀傳統(tǒng)C++中的容器std::pair外,似乎沒有現(xiàn)成的結(jié)構(gòu)能夠用來存放不同類型的數(shù)據(jù)(通常我們會自己定義結(jié)構(gòu))。但std::pair的缺陷是顯而易見的,只能保存兩個元素。元組基本操作關(guān)于元組的使用有三個的函數(shù)std::make_tuple構(gòu)造元std::get獲得元組某個位置std::tie元組拆#include<tuple>#include<iostream>autoget_student(int{返回類型被推斷為std::tuple<doublechar,if(id==returnstd::make_tuple(3.8A張三");if(id==1)returnstd::make_tuple(2.9C李四");if(id==2)returnstd::make_tuple(1.7D王五returnstd::make_tuple(0.0,'D',如果只寫0會出現(xiàn)推斷錯誤,}int{autostudent=get_student(0);std::cout<<"ID:0,"<<"GPA:"<<std::get<0>(student)<<",成績std::get<1>(student<< :"<<std::get<2>(student)<<doublegpa;charstd::stringstd::tie(gpa,grade,name)=get_student(1);std::cout<<"ID:1,"<<"GPA:"<<gpa<<",成績grade<< :"<<name<<}std::get除了使用常量獲取元組對象外,C++14增加了使用類型來獲取元組中,std::cout<<std::get<double>(t)<<std::cout<<std::get<3>(t)<<std::tuple<std::string,double,double,int>t("123",4.5,6.7,std::cout<<std::get<std::string>(t)<<如果你仔細(xì)思考一下可能就會發(fā)現(xiàn)上面代碼的問題,std::get依賴一個編譯intintindex=1;那么要怎么處理?答案是,標(biāo)準(zhǔn)庫做不到。這里介紹一個使用配合變長模板參數(shù)的黑魔#include#include te<size_tn,typename...boost::variant<T...>_tuple_index(size_ti,const.>&tpl)if(i==returnstd::get<n>(tpl);elseif(n==sizeof...(T)-1)return_tuple_index<(n<sizeof...(T)-1?n+1:0)>(i,} te<typename...boost::variant<T...>tuple_index(size_ti,const>&tpl)return_tuple_index<0>(i,}這樣我們就intinti=std::cout<<tuple_index(i,t)<<元組合并與還有一個常見的需求就是合并兩個元組,這可以通過std::tuple_cat來實autoautonew_tuple=std::tuple_cat(get_student(1),馬上就能夠發(fā)現(xiàn),應(yīng)該如何快速遍歷一個元組?但是我們剛才介紹了如何在運行期通過非常數(shù)索引一個tuple那么遍歷就變得簡單了,首先我們需要知道一個元組的te<typenameautotuple_len(T&tpl)return}這樣就能夠?qū)υM進(jìn)行迭代了for(inti=0;i!=tuple_len(new_tuple);std::cout<<tuple_index(i,new_tuple)<<總本節(jié)簡單介紹了C++11/14中新增的容器,它們的用法和傳統(tǒng)C++中已有的容器類似,std::tuple雖然有效,但是標(biāo)準(zhǔn)庫提供的功能有限,沒辦法滿足運行期索引和Copyright?2016ChangkunOuat allright,poweredbyGitbook該文件修改時間:2017-11-1609:30:52一、本節(jié)內(nèi)容包對標(biāo)準(zhǔn)庫的擴充 智能指針和計RAII與計二、RAII與計了解 Swit的程序員應(yīng)該知道計數(shù)的概念。計數(shù)這種計數(shù)是為了防止內(nèi)存而產(chǎn)生的?;鞠敕ㄊ菍τ趧討B(tài)分配的對象,進(jìn)行計數(shù),每當(dāng)增加一次對同一個對象的,那么對象的計數(shù)就會增加一次,每刪除一次,計數(shù)就會減一,當(dāng)一個對象的計數(shù)減為零時,就自動刪除指向的堆內(nèi)存。在傳統(tǒng)++中,『記得』手動釋放資源,總不是最佳實踐。因為我們很有可能就忘記了去釋放資源而導(dǎo)致。所以通常的做法是對于一個對象而言,我們在構(gòu)造函數(shù)的時候申請空間,而在析構(gòu)函數(shù)(在離開作用域時調(diào)用)的時候釋放空間,也就是我們常說的RAII資源獲取即初始化技術(shù)。凡事都有例外,我們總會有需要將對象在自由上分配的需求,在傳統(tǒng)C++里我們只好使用new和delte去『記得』對資源進(jìn)行釋放。而++1引入了智能指針的概念,使用了計數(shù)的想法,讓程序員不再需要關(guān)心手動釋放內(nèi)存。這些智能指針就包括std::shared_ptr/std::unique_ptr/std::weak_ptr,使用它們需要包含頭文件<memory>。注意:計數(shù)不是回收,技術(shù)能夠盡快收回不再被使用的對象,同時在回收的過程中也不會造成長時間的等待,更能夠清晰明確的表明資源的生命周期。三、std::shared_ptr是一種智能指針,它能夠記錄多少個shared_ptr共同指向一個對象,從而消除顯示的調(diào)用delete,當(dāng)計數(shù)變?yōu)榱愕臅r候就會將對象但還不夠,因為使用std::shared_ptr仍然需要使用new來調(diào)用,這使得代的std::shared_ptr指針。例如:#includevoidfoo(std::shared_ptr<int>{}int{//autopointer=newint(10);//構(gòu)造了一個autopointer=std::make_shared<int>(10);std::cout<<*pointer<<std::endl;//離開作用域前,shared_ptrreturn}std::shared_ptr可以通過get方法來獲取原始指針,通過reset()來減少一個計數(shù),并通過get_count()來查看一個對象的計數(shù)。例如:autopointer=std::make_shared<int>(10);autopointer2=pointer; autopointer3=pointer; int*p= //這樣不會增 計std::cout<<"pointer.use_count()="<<pointer.use_count()<< //3std::cout<<"pointer2.use_count()="<<pointer2.use_count()< //std::cout<<"pointer3.use_count()="<<pointer3.use_count()< //std::cout<<"resetpointer2:"<<std::cout<<"pointer.use_count()="<<pointer.use_count()<< //2std::cout<<"pointer2.use_count()="<<pointer2.use_count()< 0pointer2已std::cout<<"pointer3.use_count()="<<pointer3.use_count()< //std::cout<<"resetpointer3:"<<std::cout<<"pointer.use_count()="<<pointer.use_count()<< //1std::cout<<"pointer2.use_count()="<<pointer2.use_count()< //std::cout<<"pointer3.use_count()="<<pointer3.use_count()< 0pointer3已四、std::unique_ptr是一種獨占的智能指針,它其他智能指針與其共享同一個stdstd::unique_ptr<int>pointer=ake_unique從C++14//std::unique_ptr<int>pointer2= te<typenameT,typenamestd::unique_ptr<T>make_unique(Args&&...args)returnstd::unique_ptr<T>(newT(std::forward<Args>(args)...));}至于為什么沒有提供,C++標(biāo)準(zhǔn)HerbSutter在他的博客中提到原既然是獨占,換句話說就是不可。但是,我們可以利用std::move將其轉(zhuǎn)移給其他的unique_ptr,例如:#include#include#includestruct{{std::cout<<"Foo::Foo"<<std::endl;{std::cout<<"Foo::~Foo"<<std::endl;voidfoo(){std::cout<<"Foo::foo"<<std::endl;voidf(constFoo&)std::cout<<"f(constFoo&)"<<}intmain()std::unique_ptr<Foo>p1不空if(p1)p1-{stdstd::unique_ptr<Foo>p2不空p2不空if(p2)p2-p1為空if(p1)p1->foo();p1=std::move(p2);p2為空if(p2)p2-std::cout"p2被銷毀}p1不空if(p1)p1-Foo}五、如果你仔細(xì)思考std::shared_ptr就會發(fā)現(xiàn)依然存在著資源無法釋放的問題。structstructA;structstructAstd::shared_ptr<B>~A()std::cout"A被銷毀}structBstd::shared_ptr<A>~B()std::cout"B被銷毀}intmain()autoa=std::make_shared<A>();autob=std::make_shared<B>();a.pointer=b;b.pointer=}運行結(jié)果是A,B都不會被銷毀,這是因為a,b內(nèi)部的pointer同時又a,b,這使得a,b的計數(shù)均變?yōu)榱?,而離開作用域時,a,b智能指針被析構(gòu),卻智能造成這塊區(qū)域的計數(shù)減一,這樣就導(dǎo)致了a,b對象指向的內(nèi)解決這個問題的辦法就是使用弱指一種弱(相比較而言std::shared_ptr就是一種

,std::weak_ptr)。弱不會起計數(shù)增加,當(dāng)換用弱時候,最終的釋放流程如下圖所示std::weak_ptr沒有*運算符和->運算符,所以不能夠?qū)Y源進(jìn)行操作,它的唯一作用就是用于檢查std::shared_ptr是否存在,expired()方法在資源未被釋放時,會返回true,否則false。總術(shù)引進(jìn),在一定程度上消除了new/delete的,是一種更加成編程范進(jìn)一步閱讀 上關(guān)于『C++11為什么沒有make_unique』的討Copyright?2016ChangkunOuat allright,poweredbyGitbook該文件修改時間:2017-11-1609:30:52一、本節(jié)內(nèi)容包對標(biāo)準(zhǔn)庫的擴充正則表達(dá)式庫std::regex及其相二、正則表正則表達(dá)式不是 語言的一部分,這里僅做簡單的介紹正則表達(dá)式描述了一種字符串匹配的模式。一般使用正則表達(dá)式主要是實現(xiàn)下面三個需求:正則表達(dá)式是由普通字符(例如a到z)以及特殊字符組成的文字模式。模式描述在搜索文本時要匹配的一個或多個字符串。正則表達(dá)式作為一個模板,將某個字符模式與所搜索的字符串進(jìn)行匹配。普通字符包括沒有顯式指定為元字符的所有可打印和不可打印字符。這包括所有大寫和小寫字母、所有數(shù)字、所有標(biāo)點符號和一些其他符號。特殊字符是正則表達(dá)式里有特殊含義的字符,也是正則表達(dá)式的匹配語法。參見下表:描匹配輸入字符串的結(jié)尾位(,標(biāo)記一個子表達(dá)式的開始和結(jié)束位置。子表達(dá)式可以獲取供以后使用。匹配前面的子表達(dá)式零次+匹配前面的子表達(dá)式一次匹配除換行符\n之外的任何單字符[標(biāo)記一個中括號表達(dá)式的開始匹配前面的子表達(dá)式零次或一次,或指明一個非貪婪限定符。\將下一個字符標(biāo)記為或特殊字符、或原義字符、或向后、或八進(jìn)制轉(zhuǎn)義符。例如,n匹配字符n。\n匹配換行符。序列\(zhòng)\匹配'\'字符,而\(則匹配'('字符匹配輸入字符串的開始位置,除非在方括號表達(dá)式中使用,此時它表示不接受該字符集合。{標(biāo)記限定符表達(dá)式的開指明兩項之間的一個選限定符用來指定正則表達(dá)式的一個給定的組件必須要出現(xiàn)多少次才能滿足匹配。見下表:字描匹配前面的子表達(dá)式零次或多次。例如,foo*能匹配fo以foooo。*等價于{0+匹配前面的子表達(dá)式一次或多次foo+能匹配foo以及foooo,但不能匹配fo。+等價于{1,}。匹配前面的子表達(dá)式零次或一次Your(s)?可以匹YourYours中的Your。{0,1}n是一個非負(fù)整數(shù)。匹配確定的n次。例如f{2}不能匹for中的o,但是能匹配foo中的兩個on是一個非負(fù)整數(shù)。至少匹配n次。例如,f{2不能匹配for中的o,但能匹配foooooo中的所有o。o{1,}等價于o+。o{0,}則等價于o*。m和n均為非負(fù)整數(shù),其中n小于等于m。最少匹配n次且最多匹配m次。例如o{1,3}將匹配foooooo中的前三個oo{0,1}等價于o?。注意,在逗號和兩個數(shù)之間不有了這三張表,我們通常就能夠讀懂幾乎所有的正則表達(dá)式了。三、std::regex及其對字符串內(nèi)容進(jìn)行匹配的最常見就是使用正則表達(dá)式。可惜在傳統(tǒng)C++中正則表達(dá)式一直沒有得到語言層面的支持,沒有納入標(biāo)準(zhǔn)庫,而C++作為一門高性能語言,在服務(wù)的開發(fā)中,對URL資源進(jìn)行判斷時,使用正則表達(dá)式也是工業(yè)界最為成普遍做法。一般的解決方案就是

boost的正則表達(dá)式庫。而C++11正式將正則的的處理方法納入標(biāo)準(zhǔn)庫的行列,從語言級上提供了標(biāo)準(zhǔn)的支持,不再依賴第三方。C++11提供的正則表達(dá)式

std::string對象

std::regex(本std::basic_regex)進(jìn)行初始化,通過std::regex_match進(jìn)行匹配產(chǎn)生std::smatch(本質(zhì)是std::match_results對象)我們通過一個簡單的例子來簡單介紹這個庫的使用??紤]下面的正則表達(dá)式[a-z]+\.txt:在這個正則表達(dá)式中,[a-z]表示匹配一個小寫字母 +可以使前面的表達(dá)式匹配多次,因此[a-z]+能夠匹配一個小寫字母組字符串。在正則表達(dá)式中一個.表示匹配任意字符,而\.則表示匹配字符,最后的txt表示嚴(yán)格匹配txt則三個字母。因此這個正則表達(dá)式std::regex_match用于匹配字符串和正則表達(dá)式,有很多不同的重載形式。最簡單的一個形式就是傳入std::string以及一個std::regex進(jìn)行匹配,當(dāng)匹配成功時,會返回true,否則返回false。例如:#include<string>#include<regex>intmain()std::stringfnames[]={"foo.txt","bar.txt","test","a0.txt","AAA.txt"};在C中`\``\.``\``\\.`std::regextxt_regex("[a-z]+\\.txt");for(constauto&fname:fnames)std::cout<<fname<<":"<<std::regex_match(fname,txt_regex)<<std::endl;}另一種常用的形式就是依次傳入std::string/std::smatch/std::regex三個參數(shù),其中std::smatch的本質(zhì)其實是std::match_results,在標(biāo)準(zhǔn)庫中std::smatch被定義為了std::match_results<std::string::const_itor,也就是一個子串迭代器類型的match_results。使用std::smatch可以方便的對匹配的結(jié)果進(jìn)行std::smatchbase_match;for(constauto&fname:fnames)if(std::regex_match(fname,base_match,base_regex))sub_matchif(base_match.size()==2)std::stringbase=base_match[1].strstd::cout<<"sub-match[0]:"<<<<std::cout<<fname<<"sub-match[1]"<<base<<}}}以上兩個代碼段的輸出結(jié)果為foo.txt:foo.txt:bar.txt:test:a0.txt:AAA.txt:sub-match[0]:foo.txtfoo.txtsub-match[1]:foosub-match[0]:bar.txtbar.txtsub-match[1]:bar總本節(jié)簡單介紹了正則表達(dá)式本身,然后根據(jù)使用正則表達(dá)式的主要需求,通過一個實際的例子介紹了正則表達(dá)式庫的使用。進(jìn)一步閱讀知乎『如何評價GCC的C++11正則表達(dá)式?』中原庫作者TimShen正則表達(dá)式庫C++開發(fā)Web服務(wù)框Copyright?2016ChangkunOuat allright,poweredbyGitbook該文件修改時間:2017-11-1609:30:52一、本節(jié)內(nèi)容包對標(biāo)準(zhǔn)庫的擴充:語言級線程支持二、std::thread用于創(chuàng)建一個執(zhí)行的線程實例,所以它并發(fā)編程的基礎(chǔ),使用時需要包含<thread>頭文件,它提供了很多基本的線程操作,例如get_id來獲取所創(chuàng)建線程的線程ID,例如使用join來加入一個線程#include<thread>voidfoo(){std::cout<<}intmain()oworld"<<return}三、std::mutex其中的之一。C++11引入了mutex相關(guān)的類,其所有相關(guān)的函數(shù)都放在<mutex頭文件中std::mutexC++11中最基本的mutex類,通過實例化std::mutex可以創(chuàng)建互斥量,而通過其成員函數(shù)lock()可以僅此能上鎖,unlock()可以進(jìn)行。但是在在實際編寫代碼的過程中,最好不去直接調(diào)用成員函數(shù),因為調(diào)用成員函數(shù)就需要在每個臨界區(qū)的出口處unlock(),當(dāng)然,還包括異常。這時候C++11還為互斥量提供了一個RAII語法的模板類std::lock_gurad。RAII在 用法下,對于臨界區(qū)的互斥量的創(chuàng)建只需要在作用域的開始部分,例如voidvoidsome_operation(conststd::string{staticstd::mutexmutex;std::lock_guard<std::mutex>lock(mutex);當(dāng)離開這個作用域的時候,互斥鎖會被析構(gòu),同時unlock}由于++保證了所有棧對象在周期結(jié)束時會被銷毀,所以這樣的代碼也是異常安全的。無論someoeato()正常返回

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論