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

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

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

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

,這些元素。在插入素時候,會根據<操作符比較元素大小并判斷元素是否相同,并選擇合適的位置插入到容器中。當對這個容器中的元素進行遍歷時,輸出結果會按照<操作符的而無序容器中的元素是不進行排序的,內部通過Hash表實現,插入和搜索元素的平均復雜度為O(constant),在不關心容器內部元素順序時,能夠獲得顯著的性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";}最終的輸出結Key:[2]Value:[2]Key:[3]Key:[1]Key:[1]Key:[2]Key:[3]四、元組了解過Python的程序員應該知道元組的概念,縱觀傳統C++中的容器std::pair外,似乎沒有現成的結構能夠用來存放不同類型的數據(通常我們會自己定義結構)。但std::pair的缺陷是顯而易見的,只能保存兩個元素。元組基本操作關于元組的使用有三個的函數std::make_tuple構造元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會出現推斷錯誤,}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)<<如果你仔細思考一下可能就會發(fā)現上面代碼的問題,std::get依賴一個編譯intintindex=1;那么要怎么處理?答案是,標準庫做不到。這里介紹一個使用配合變長模板參數的黑魔#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ā)現,應該如何快速遍歷一個元組?但是我們剛才介紹了如何在運行期通過非常數索引一個tuple那么遍歷就變得簡單了,首先我們需要知道一個元組的te<typenameautotuple_len(T&tpl)return}這樣就能夠對元組進行迭代了for(inti=0;i!=tuple_len(new_tuple);std::cout<<tuple_index(i,new_tuple)<<總本節(jié)簡單介紹了C++11/14中新增的容器,它們的用法和傳統C++中已有的容器類似,std::tuple雖然有效,但是標準庫提供的功能有限,沒辦法滿足運行期索引和Copyright?2016ChangkunOuat allright,poweredbyGitbook該文件修改時間:2017-11-1609:30:52一、本節(jié)內容包對標準庫的擴充 智能指針和計RAII與計二、RAII與計了解 Swit的程序員應該知道計數的概念。計數這種計數是為了防止內存而產生的?;鞠敕ㄊ菍τ趧討B(tài)分配的對象,進行計數,每當增加一次對同一個對象的,那么對象的計數就會增加一次,每刪除一次,計數就會減一,當一個對象的計數減為零時,就自動刪除指向的堆內存。在傳統++中,『記得』手動釋放資源,總不是最佳實踐。因為我們很有可能就忘記了去釋放資源而導致。所以通常的做法是對于一個對象而言,我們在構造函數的時候申請空間,而在析構函數(在離開作用域時調用)的時候釋放空間,也就是我們常說的RAII資源獲取即初始化技術。凡事都有例外,我們總會有需要將對象在自由上分配的需求,在傳統C++里我們只好使用new和delte去『記得』對資源進行釋放。而++1引入了智能指針的概念,使用了計數的想法,讓程序員不再需要關心手動釋放內存。這些智能指針就包括std::shared_ptr/std::unique_ptr/std::weak_ptr,使用它們需要包含頭文件<memory>。注意:計數不是回收,技術能夠盡快收回不再被使用的對象,同時在回收的過程中也不會造成長時間的等待,更能夠清晰明確的表明資源的生命周期。三、std::shared_ptr是一種智能指針,它能夠記錄多少個shared_ptr共同指向一個對象,從而消除顯示的調用delete,當計數變?yōu)榱愕臅r候就會將對象但還不夠,因為使用std::shared_ptr仍然需要使用new來調用,這使得代的std::shared_ptr指針。例如:#includevoidfoo(std::shared_ptr<int>{}int{//autopointer=newint(10);//構造了一個autopointer=std::make_shared<int>(10);std::cout<<*pointer<<std::endl;//離開作用域前,shared_ptrreturn}std::shared_ptr可以通過get方法來獲取原始指針,通過reset()來減少一個計數,并通過get_count()來查看一個對象的計數。例如: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++標準HerbSutter在他的博客中提到原既然是獨占,換句話說就是不可。但是,我們可以利用std::move將其轉移給其他的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}五、如果你仔細思考std::shared_ptr就會發(fā)現依然存在著資源無法釋放的問題。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=}運行結果是A,B都不會被銷毀,這是因為a,b內部的pointer同時又a,b,這使得a,b的計數均變?yōu)榱?,而離開作用域時,a,b智能指針被析構,卻智能造成這塊區(qū)域的計數減一,這樣就導致了a,b對象指向的內解決這個問題的辦法就是使用弱指一種弱(相比較而言std::shared_ptr就是一種

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

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

std::string對象

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

溫馨提示

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

評論

0/150

提交評論