語言雜談深入基礎(chǔ)new運(yùn)算符_第1頁
語言雜談深入基礎(chǔ)new運(yùn)算符_第2頁
語言雜談深入基礎(chǔ)new運(yùn)算符_第3頁
語言雜談深入基礎(chǔ)new運(yùn)算符_第4頁
語言雜談深入基礎(chǔ)new運(yùn)算符_第5頁
免費(fèi)預(yù)覽已結(jié)束,剩余16頁可下載查看

下載本文檔

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

文檔簡介

深入Cnew是C++的一個(gè)關(guān)鍵字,同時(shí)也是操作符關(guān)于new的話題非常new有關(guān)的內(nèi)容做一個(gè)總結(jié)new過當(dāng)我們使用關(guān)鍵字new上動(dòng)態(tài)創(chuàng)建一個(gè)對象時(shí),它實(shí)際上做定義了如下一個(gè)類A:class{inti;A(int_i):i(_i*_i)void {printf("i=%dn",i);//調(diào)用A*pa=new那么上述動(dòng)態(tài)創(chuàng)建一個(gè)對象的過程大致相當(dāng)于以下三句話(大致上):雖然從效果上看,這三句話也得到了一個(gè)有效的指向堆上的A對象的指針pa,但區(qū)別在于,當(dāng)malloc它不會調(diào)用分配內(nèi)存失敗處理程序new_handler,而使用new會的因此我們還是要盡可能的使用new,除非有一些特殊的需求new三種到目前為止,本文所提到的new都是指的newoperator或稱為newexpression,但事實(shí)上在C++中一提到new,至少可能代表以下三種含義:newoperatoroperatornew cementnewnwopator就是我們平時(shí)所使用的nw,其行為就是前面所說就有些內(nèi)容了newoperator第一步分配內(nèi)存實(shí)際上是通過調(diào)用operatornew完成的,這里的new實(shí)際上是像加減乘除一樣的操作符,因此也是可以重載的operatornew默認(rèn)情況下首先調(diào)用分配內(nèi)存的代碼,嘗用一個(gè)new_hander,然后繼續(xù)重復(fù)前面過程如果我們對這個(gè)過程不滿意,就可以重載operatornew,來設(shè)置我們希望的行為例如:class{void*operatornew(size_t{printf("operatornewcalledn");return::operatornew(size);}A*a=new這里通過::operatornew用了原有的全局的new,實(shí)現(xiàn)了在分配內(nèi)存之前輸出一句話全局的operatornew也是可以重載的,但這樣一來就不能再遞歸的使用new配內(nèi)存,而只能使用malloc:void*operatornew(size_tsize){printf("globalnewn");returnmalloc(size);}相應(yīng)的,delete有deleteoperatoroperatordelete后也是可以重載的并且,如果重載了operatornew,就應(yīng)該也相應(yīng)的重載operatordelete,這是良好的編程習(xí)慣new的第三種形態(tài) cementnew是用來實(shí)現(xiàn)定位構(gòu)造的,因此可以實(shí)現(xiàn)newoperator三步操作中的第二步,也就是在取得了一有點(diǎn)類似于前面代碼中的p>::(3);這句話,但這并不是一個(gè)標(biāo)準(zhǔn)的寫法,正確的寫法是使用mntnw:#includevoid{chars[sizeof(A)];A*p=(A*)s;new(p)A(3);//p-p-}對頭文件<new>或<new.h>的是必須的,這樣才可以使cementnew這里new(p)A(3)這種奇怪的寫法便 cement對象的功能,后面A(3)就是對構(gòu)造函數(shù)的顯式調(diào)用這里不難發(fā)現(xiàn), cement對此不加區(qū)分 cementnew,這畢竟不是用來構(gòu)造對象的正式寫法,只不過是newoperator的一個(gè)步驟而已使用newoperator地編譯器會自動(dòng)生成對 cementnew的調(diào)用的代碼,因此也會相應(yīng)的生成使用delete時(shí)調(diào)用析構(gòu)函數(shù)的代 cementnew,則必須手工調(diào)用析構(gòu)函p-當(dāng)我們覺得默認(rèn)的newoperator不能滿足我們的需要,而希望自己手工的管理內(nèi)存時(shí),cementnew用了STL的allocator用了這種方式,借助cementnew來實(shí)現(xiàn)更靈活有處理內(nèi)存分配異正如前面所說,operatornew的默認(rèn)行為是請求分配內(nèi)存,如果成功則返回此內(nèi)存地址,如果失敗則調(diào)用一個(gè)new_handler,然后再重復(fù)此過程于是,想要從operatornew的執(zhí)行過程中返回,則必然于是,我們可以假設(shè)默認(rèn)情況下operatornew的行為是這樣的void*operatornew(size_t{void*p=while(!(p={if(null==new_handler)throwbad_alloc();{}catch(bad_alloc{throw}}return}在默認(rèn)情況下,new_handler的行為是拋出一個(gè)bad_alloc異常自定義一個(gè)new_handler,并使用std::set_new_handler函數(shù)使其生效在自定義的new_handler,我們可以拋出異常,可以結(jié)束程序,也時(shí)也許會成功,也可以通過set_new_handler來安裝另一個(gè)可能更有效的new_handler例如:void{printf(Newhandlercalled!n);throwstd::bad_alloc();}這里new_handler程序在拋出異常之前會輸出一句話應(yīng)該注意,在new_handler代碼里應(yīng)該注意避免再嵌套有對new調(diào)用,因?yàn)槿绻@里調(diào)用new再失敗的話,可能會再導(dǎo)致對new_handler的調(diào)在編程時(shí)我們應(yīng)該注意到對new的調(diào)用是有可能有異常被拋出的,因此在new代碼周圍應(yīng)該注意保持其事務(wù)性,即不能因?yàn)檎{(diào)new異常來導(dǎo)致不正確的程序邏輯或數(shù)據(jù)結(jié)構(gòu)的出現(xiàn)例class{staticintSomeClass()staticSomeClass*{returnnew}靜態(tài)變量count記錄此類型生成的實(shí)例的個(gè)數(shù),在上述代碼中,如果因new分配內(nèi)存失敗而拋出異常,那么其實(shí)例個(gè)數(shù)并沒有增加,但count變量的值卻已經(jīng)多了一個(gè),從而數(shù)據(jù)結(jié)構(gòu)被破壞正staticSomeClass*{SomeClass*p=newSomeClass();return}這樣一來,如果new失敗則直接拋出異常,count的值不會增類似的,在處理線程同步時(shí),也要注意類似的問題void{lock(someMutexdeletep;p=newSomeClass();}STL分配與traitsSTL析一書中詳細(xì)分析了SGISTL分配器的行為與直接使用newoperator同的是,SGISTL依賴C++默的內(nèi)存分配方式,而是使用一套自行實(shí)現(xiàn)的方案首先SGISTL將可用內(nèi)存整塊的分配,使之成為當(dāng)前進(jìn)程可用的內(nèi)存,當(dāng)程序中確實(shí)需要分配內(nèi)存時(shí),先從這些已請求好的大內(nèi)存塊中嘗試取得內(nèi)存,如果失敗的話再嘗試整塊的分配大內(nèi)存這種做法有效的避免了大量內(nèi)存碎片的出現(xiàn),提高了內(nèi)存管理效率為了實(shí)現(xiàn)這種方式,STL使用了 cementnew,通過在自己管理 cementnew來構(gòu)造對象,以達(dá)到原有newoperator所具有的功能 te<classT1,classinlinevoidconstruct(T1*p,constT2&{存地址p上構(gòu)造一個(gè)新對象,代碼中后半截T(val)便是cmntnw語法中調(diào)用構(gòu)造函數(shù)的寫法,如果傳入的對象ale正是所要求的類型T,那么這里就相當(dāng)于調(diào)用拷貝構(gòu)造函數(shù)類似的,因使用了cmntnw,編譯器不會自動(dòng)產(chǎn)生調(diào)用析構(gòu)函數(shù)的代碼,需要手工的實(shí)現(xiàn): te<classinlinevoiddestory(T*{與此同時(shí),STL中還有一個(gè)接收兩個(gè)迭代器的destory版本,可將某容器上指定范圍內(nèi)的對象全部銷毀典型的實(shí)現(xiàn)方式就本沒有必要調(diào)用析構(gòu)函數(shù)的自定義類型(例如只包含數(shù)個(gè)int為此,STL使用了一種稱為typetraits的技巧,在編譯器就判斷出所 te<class inlinevoid torfirst, tor{ destory(first,last,}其中value_type()用于取出迭代器所指向的對象的類型信息是 te<class tor,classinlinevoid torfirst, torlast,{typedeftypename destory_aux(first,last,} te<classForwardI inlinevoid torfirst, tor {for(;first<last;destory(&*first);first*first其真正內(nèi)容,}tempalte<classForwardI inlinevoid torfirst, tor 因上述函數(shù)全都是inline的,所以多層的函數(shù)調(diào)用并不會對性能造成影響,最終編譯的結(jié)果根據(jù)具體的類型就只是一個(gè)for循環(huán)或 據(jù)不同的T類型定義出不同的has_trivial_destructor的結(jié)果,如果T true_type類型,否則就定義為 false_type只不過是兩個(gè)沒有任何內(nèi)容的類, type_traits<T>也是特化了的一系列模板類struct true_type{};struct false_type{}; te<classT>struct {typedef false_type te<板特struct //int{typedef true_type//其他簡單類型的如果要把一個(gè)自定義的類型MyClass也定義為不調(diào)用析構(gòu)函數(shù), struct {typedef true_type模板是比較高級的C++編程技巧,模板特化模板偏特化就技巧性很強(qiáng)的東西,STL的type_traits助模板特化的功能,率更詳細(xì)的內(nèi)容可參考STL源碼剖析第二三章中的相關(guān)內(nèi)容new我們經(jīng)常會通過new來動(dòng)態(tài)創(chuàng)建一個(gè)數(shù)組,例如是nw[],而并不是簡單的nw,但釋放內(nèi)存時(shí)卻用的是dlte正確的寫法是使用det[]:delete[]事實(shí)上,nw與nw[]dlte與dete[]是有區(qū)別的,特別是當(dāng)用來操作復(fù)雜類型時(shí)假如針對一個(gè)我們自定義的類Cass使用nw[]:MCas*p=nwCas[];上述代碼的結(jié)果是在堆上分配了10個(gè)連續(xù)的MyClass實(shí)例,并且已經(jīng)對它們依次調(diào)用了構(gòu)造函數(shù),于是我們得到了10個(gè)可象,這一點(diǎn)與JavaC#有區(qū)別的,JavaC#中這樣的結(jié)果只是得到了個(gè)nul換句話說,使用這種寫法時(shí)Cass必須擁有不帶參數(shù)的構(gòu)造函數(shù)當(dāng)這樣構(gòu)造成功后,我們可以再將其釋放,釋放時(shí)使用delete[]:delete[]p;當(dāng)我們對動(dòng)態(tài)分配的數(shù)組調(diào)用dete[]時(shí),其行為根據(jù)所申請的變量類型會有所不同如果p指向簡單類型,如ntchar等,其結(jié)果只不過是這塊內(nèi)存被回收,此時(shí)使用dt[]與dte沒有區(qū)別,但如果p指向的是復(fù)雜類型,dlte[]會針對動(dòng)態(tài)分配得到的每個(gè)對象調(diào)用析構(gòu)函數(shù),然后再釋放內(nèi)存因此,如果我們對上述分配得到的p指針直接使用dlte來回收,雖然編譯期不報(bào)什么錯(cuò)誤(因?yàn)榫幾g器根本看不出來這個(gè)指針p是如何分配的),但在運(yùn)行時(shí)(DEBUG情況下)會給出一個(gè)Dbugasetonfad提示到這里,我們很容易提出一個(gè)問題dlte[]是如何知道要為多少個(gè)對象調(diào)用析構(gòu)函數(shù)的?要回答這個(gè)問題,我們可以首先看一看nw[]的重載class{inta;MyClass(){printf("ctorn");~MyClass(){printf("dtorn");void*operatornew[](size_t{void*p=operatorprintf("callingnew[]withsize=%daddress=%pn",size,p);returnp;}//主函數(shù)MyClass*mc=newMyClass[3];printf("addressofmc=%pn",mc);delete[]mc;運(yùn)行此段代碼,得的內(nèi)存空間大小以及地址的數(shù)值卻出現(xiàn)了問題我們的類Cass的大小顯然是4節(jié),并且申請的數(shù)組中有3素,那么應(yīng)共申請12字節(jié)才對,但事實(shí)上系統(tǒng)卻為我們申請了16節(jié),并且在operatornew[]返后我們得到的內(nèi)存地址是實(shí)際申請得到的內(nèi)存地址值加4的結(jié)果也就是說,當(dāng)為復(fù)雜類型動(dòng)態(tài)分配數(shù)組自動(dòng)在最終得到的內(nèi)存地址前空出了4個(gè)字節(jié),我們有理4個(gè)字節(jié)的內(nèi)容與動(dòng)態(tài)分配數(shù)組的長度有關(guān)通過單步,很容易發(fā)現(xiàn)這4個(gè)字節(jié)對應(yīng)的int值為0x 我的想法于是,我們也有理由認(rèn)為new[]operator的行為相當(dāng)于下面 te<classT>T*New[](int{intsize=sizeof(T)*count+4;void*p=T::operatornew[](size);*(int*)p=T*pt=(T*)((int)p+4);for(inti=0;i<count;i++)new(&pt[i])T();returnpt;}上述示意性的代碼省略了異常處理的部分,只是展示當(dāng)我們對個(gè)復(fù)雜類型使用nw[]來動(dòng)態(tài)分配數(shù)組時(shí)其真正的行為是什么,從中可以看到它分配了比預(yù)期多4個(gè)字節(jié)的內(nèi)存并用它來保存對象的個(gè)數(shù),然后對于后面每一塊空間使用 mntnw來調(diào)用無參構(gòu)造函數(shù),這也就解釋了為什么這種情況下類必須有無參構(gòu)造函數(shù),最后再將首地址返回類似的,我們很容易寫出相應(yīng)的dlte[]的實(shí)現(xiàn)代碼 te<classT>voidDelete[](T*{intcount=((int*)pt)[-1];for(inti=0;i<count;i++)void*p=(void*)((int)pt4);T::operatordelete[](p);}由此可見,在默認(rèn)情況下operatornewoperatornew行為相同的,operatordelete[]與operatordelete也是,不同的是newoperatornewoperatordeleteoperatordeleteoperator我們可以根據(jù)不同的需要來選擇重載帶有和不帶有[]的operatornew和把前面類MyClass做修改注釋掉析構(gòu)函數(shù),然后再來看callingnew[]withsize=12address=003A5A58addressof這一次,new[]老老實(shí)實(shí)的申請了12個(gè)字節(jié)的內(nèi)存,并且申請的結(jié)果與new[]operator返回的結(jié)果也是相同的,看來,是否面添加4個(gè)字節(jié),只取決于這個(gè)類有沒有析構(gòu)函數(shù),當(dāng)然,這么說并不情況下雖然這個(gè)類沒析構(gòu)函數(shù),但還是多申請了4個(gè)字節(jié):一顯式的了析構(gòu)函數(shù)擁有需要調(diào)用析構(gòu)函數(shù)的類的成繼承自需要調(diào)用析構(gòu)函數(shù)的類類似的,動(dòng)態(tài)申請簡單類型的數(shù)組時(shí),也不會多申請4字節(jié)于是在這兩種情況下,釋放內(nèi)存時(shí)使用delete或delete[]都可以,但為時(shí)就使用delete[]釋放內(nèi)存時(shí)如何知道長但這同時(shí)又帶來了新問題,既然申請無需調(diào)用析構(gòu)函數(shù)的類或單類型的數(shù)組時(shí)并沒有記錄個(gè)數(shù)信息,那么opeatordete,或更直接的說 ()是如何來回收這塊內(nèi)存的呢?這就要研究mao()返回的內(nèi)存的結(jié)構(gòu)了與nw[]類似的是,實(shí)際上在mao()申請內(nèi)存時(shí)也多申請了數(shù)個(gè)字節(jié)的內(nèi)容,只不過這與所申請的變量的類型沒有任何關(guān)系,我們從調(diào)用maloc時(shí)所傳入的參數(shù)也可以理解這一點(diǎn)它只接收了要申請的內(nèi)存的長度,并不關(guān)系這塊內(nèi)存用來保存什么類型下面運(yùn)行這樣一段代碼做個(gè)實(shí)驗(yàn):char*p=for(inti=0;i<40;i+={char*s=newprintf("alloc%2dbytes,address=%pdistance=%dn",i,s,s-我們直接來看VC2005ReleaseDEBUG因allo

溫馨提示

  • 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)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論