欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
用C++寫(xiě)JavaStyle程序
前言
故事的起因源自于一項“翻譯”工作,工作內容是將門(mén)戶(hù)Java版自動(dòng)切換客戶(hù)端改寫(xiě)成C++版。然而起始階段“翻譯”過(guò)程并不順暢,原因是雖然兩種語(yǔ)言語(yǔ)法類(lèi)似,但仍有一些本質(zhì)上的區別很難“直譯”。就如同我們在翻譯英文文章的時(shí)候總會(huì )發(fā)現有些單詞很難直譯成中文對應物,于是要么生造一個(gè)詞、要么就得繞個(gè)圈子才能解釋清楚。除此之外,我,一個(gè)用了很長(cháng)時(shí)間Java后來(lái)又轉為C++開(kāi)發(fā)的人來(lái)說(shuō),始終割舍不下Java那優(yōu)雅的線(xiàn)程模型、所有變量(除了基本數值變量)都是引用的編程理念、只管new不需要delete的傻瓜式內存管理、實(shí)用的靜態(tài)初始化區塊……,我一直想不明白為何C++不內置線(xiàn)程支持、垃圾回收這些現代編程語(yǔ)言特征,類(lèi)似google的GO語(yǔ)言那樣。所以我在開(kāi)發(fā)過(guò)程中一直在C++世界尋找可以讓我在寫(xiě)代碼時(shí)更貼近Java習慣的替代品。經(jīng)過(guò)一段時(shí)間的摸索實(shí)踐,我總結了一些經(jīng)驗和范例,使我可以在享受Java語(yǔ)法功能方面簡(jiǎn)潔優(yōu)雅的同時(shí)不失C++的強大控制力和高效性。
1 線(xiàn)程模型與鎖
1.1 線(xiàn)程模型Java的線(xiàn)程模型是在語(yǔ)言層面就支持的。通常我們可以通過(guò)兩種方法來(lái)定義一個(gè)線(xiàn)程:直接繼承Thread類(lèi)并覆寫(xiě)run()方法或實(shí)現Runnable接口并實(shí)現run()方法。run()里面存放的是邏輯相關(guān)代碼,調用start()即可使線(xiàn)程啟動(dòng)。而C++在語(yǔ)言層面并未支持線(xiàn)程模型,而需要使用額外的庫來(lái)實(shí)現線(xiàn)程功能(比如pthread)。于是你就會(huì )很煩躁地看到pthread_create()、pthread_exit(),、pthread_join()等一堆函數和一些更令人煩躁的屬性設置(pthread_attr_t)。Oh My God!還我簡(jiǎn)潔的線(xiàn)程模型??!
上帝和罵街解決不了問(wèn)題,我們自己動(dòng)手實(shí)現一套類(lèi)java的線(xiàn)程模型。首先定義接口IRunnable,純虛函數run()用于實(shí)現類(lèi)填充邏輯。
 
第二步是定義線(xiàn)程基類(lèi),里面含有我們熟悉的run()和start()。我們自定義的線(xiàn)程類(lèi)只需要繼承自AbstractThread,實(shí)現run()方法就可以了。于是煩躁去無(wú)蹤,簡(jiǎn)潔清爽的感覺(jué)又回來(lái)了。不過(guò)這樣一來(lái),我們自定義線(xiàn)程類(lèi)還想繼承其他類(lèi)的話(huà)就得使用多重繼承,這是C++中很容易誤用和導致錯誤的部分,后續的代碼需要特別注意。這里還有一個(gè)需要注意的點(diǎn)是代碼最后的那個(gè)互斥鎖,每個(gè)線(xiàn)程內部都應該有一把鎖作為線(xiàn)程內部的同步控制之用,java的synchronized關(guān)鍵字實(shí)際上是使用了基類(lèi)Object中的互斥鎖實(shí)現的,我們需要額外寫(xiě)一個(gè)。
 
1.2 鎖Java語(yǔ)言在jdk1.5以前用的是一種簡(jiǎn)單的同步模型,即整個(gè)Java世界的基類(lèi)Object內定義了一把互斥鎖,于是所有Java類(lèi)就都可以用這把鎖進(jìn)行同步控制。具體操作上是用synchronized關(guān)鍵字修飾的類(lèi)的成員函數、{}括起來(lái)的代碼塊,于是被修飾的函數和代碼塊就可以隱式利用Object的互斥鎖進(jìn)行線(xiàn)程同步。劃時(shí)代的jdk1.5引入了單獨的線(xiàn)程包java.util.concurrent,里面包含了多種新的線(xiàn)程模型、線(xiàn)程安全容器、鎖等等工具,極大地提高了java線(xiàn)程工具的可用性。
同線(xiàn)程模型一樣,C++語(yǔ)言本身并不包含鎖的功能,我們只能借助于外部庫(如pthread)來(lái)實(shí)現鎖的功能。仍然是一堆煩人的函數,我們簡(jiǎn)單的包裝一下,希望把它變得更好用一些。
1.2.1 讀寫(xiě)鎖

簡(jiǎn)單包裝了一下,避開(kāi)了pthread_XXX系列函數并適時(shí)一些異常,登時(shí)感覺(jué)好用了很多(或者是心理作用……)。
1.2.2 互斥鎖

類(lèi)似的封裝思路,看起來(lái)很上流,不過(guò)總覺(jué)得還有點(diǎn)什么事讓我們放不下心來(lái),我們下一節討論。
1.2.3 鎖的安全釋放
java里面有一個(gè)很好用的異常處理范式:try…catch…finally,其中java保證無(wú)論try塊中是否拋出異常,都會(huì )執行finally塊中代碼,這就給了我們一個(gè)機會(huì )在異常出現之后進(jìn)行一些常規的清理動(dòng)作,如關(guān)閉數據庫連接、釋放鎖等等。然而C++沒(méi)有finally塊,所以如果我們在函數中加了鎖,一旦發(fā)生異常,我們必須花很大力氣+把代碼搞的面目全非才能保證把鎖安全的釋放掉。如何才能更優(yōu)雅的完成這項艱巨的任務(wù)?
首先我們復習一下C++異常處理部分的一個(gè)特性:C++保證,在函數拋出異常的時(shí)候,異常拋出點(diǎn)之前聲明的所有臨時(shí)變量都將被析構。利用這一性質(zhì),我們只要把要同步的代碼用{}包裹起來(lái),在代碼塊的起始部分聲明一個(gè)鎖的Wrapper對象(我們定義為L(cháng)ockWrapper),在程序執行完這段代碼或拋出異常的時(shí)候,LockWrapper的析構函數將被調用,這時(shí)我們有機會(huì )在析構函數中把鎖釋放掉。
 
于是,我們在執行某一互斥操作的時(shí)候就可以像下面這樣寫(xiě)。簡(jiǎn)潔,優(yōu)雅,安全……
 
1.3 原子計數器很多時(shí)候(如生成協(xié)議的sequence)我們都需要一個(gè)線(xiàn)程安全的計數器,但如果為了線(xiàn)程安全在一個(gè)不斷++的int變量前加個(gè)互斥鎖就感覺(jué)有些太重了,但是不加有時(shí)候會(huì )引起很多煩惱。java.util.concurrent包里包含了各種類(lèi)型線(xiàn)程安全的計數器(AtomicInteger)和數組(AtomicArray),著(zhù)實(shí)令人眼饞哪。C++就沒(méi)那么幸運了,曾經(jīng)有人說(shuō)++i在很多平臺上是原子操作,但實(shí)際測試了一下發(fā)現并非如此,所以我們沒(méi)法偷懶還得自己動(dòng)手豐衣足食。
基本思路是仿照l(shuí)inux內核的同步方式,用嵌套匯編的方式來(lái)解決問(wèn)題。代碼很簡(jiǎn)單應該無(wú)須解釋?zhuān)?+、--和清零操作都有了,作為一個(gè)線(xiàn)程安全的計數器足矣。這里需要注意的是,由于用了80x86的匯編,我們這段代碼無(wú)法移植到其他平臺上,但是鑒于公司的服務(wù)器從硬件到系統都是統一部署配置的,這個(gè)問(wèn)題應該不用太過(guò)擔心。
 
2 引用與內存管理
在Java的世界中,除了基本數值變量之外,所有的類(lèi)變量都是引用。Java的引用概念和C++的有本質(zhì)的不同,C++的引用是指變量的別名,而Java中的引用我們可以理解為指針的Wrapper,而且是線(xiàn)程安全的。在內存管理方面,眾所周知Java的一個(gè)最大賣(mài)點(diǎn)就是垃圾回收機制,并且隨著(zhù)虛擬機垃圾回收算法的不斷改進(jìn),垃圾回收對于系統性能的影響越來(lái)越小。沒(méi)用過(guò)Java的人也可以想像得到,只管new不管delete還是相當爽的,同時(shí)我們不必擔心忘了delete某些資源而造成的內存泄漏,也不會(huì )因為對Raw指針的錯誤操作而把內存寫(xiě)壞。在C++里我們也可以擁有自動(dòng)化的內存管理嗎?答案是boost::shared_ptr<T>。
2.1 shared_ptrshared_ptr實(shí)際上也是一個(gè)Raw指針的wrapper,它通過(guò)引用計數的方式來(lái)管理所指資源的生命周期。即每當shared_ptr被copy的時(shí)候,所指資源對象的引用計數就加1;當shared_ptr對象析構的時(shí)候,所指資源對象的引用計數就減1,當引用計數為0的時(shí)候,shared_ptr將調用刪除器(缺省直接delete指針)將所指資源對象刪除。于是我們就可以放心大膽地new出對象,塞入到shared_ptr里,然后讓shared_ptr幫我們管理資源對象的生命周期。我們從此再不用擔心因為只顧new忘了delete或者拋異常沒(méi)來(lái)得及delete而造成的問(wèn)題,大大降低程序出現內存泄漏的機會(huì )。這也正是《effective C++》的作者對boost庫中只能指針推崇備至的原因。正因為它是如此有用,我們更有必要深入了解一下它的局限性、潛規則,避免因為誤用而導致的問(wèn)題。
2.1.1 循環(huán)引用
其實(shí)引用計數也是一種最簡(jiǎn)單的垃圾回收算法,但是它的一個(gè)很大的缺陷在于可能存在循環(huán)引用。而一旦形成循環(huán)引用,環(huán)上的所有資源對象的引用計數永遠不會(huì )是0,shared_ptr也就沒(méi)法幫我們正確刪除那些已經(jīng)沒(méi)用了的資源對象,于是內存泄漏再次發(fā)生。不過(guò)幸好我們可以用weak_ptr來(lái)幫助我們解決部分問(wèn)題,看下面的例子。
 
本例中shared_from_this()是獲取this指針的shared_ptr,我們后面再說(shuō)??紤]parent_,如果把weak_ptr改為shared_ptr的話(huà)會(huì )有什么問(wèn)題?對,答案是循環(huán)引用。weak_ptr是shared_ptr的觀(guān)察者,在把shared_ptr賦給weak_ptr的時(shí)候引用計數是不會(huì )加1的。所以shared_ptr配合weak_ptr可以幫助我們解決循環(huán)引用的困擾。但必須強調的是,我們首先還是要能看出程序中有類(lèi)似上述例子中的問(wèn)題,才能對癥下藥用shared_ptr配合weak_ptr加以解決,但如果我們沒(méi)看出來(lái)呢?還是要自己小心些才行……
Java在應付循環(huán)引用的一堆對象的時(shí)候就會(huì )比較智能,垃圾回收器會(huì )根據某種算法找到一些“跟對象”,然后根據這些跟對象順藤摸瓜找到所有正在被使用的對象。而剩下的那些“對象孤島”自然就是可以被回收掉的。這就避免了循環(huán)引用帶來(lái)的問(wèn)題。當然這是題外話(huà),與shared_ptr無(wú)關(guān)。
2.1.2 混用Raw指針和shared_ptr帶來(lái)的問(wèn)題
第一個(gè)例子如下圖所示,先new了一個(gè)指針出來(lái),然后賦給一個(gè)在括號作用域內的shared_ptr變量。在作用域結束之后shared_ptr析構,引用計數為0,p所指向的內存被清空,于是在最后一行再使用p的時(shí)候將會(huì )core掉。
 
第二個(gè)例子稍微復雜一些,主要是p4在析構時(shí)刪掉了資源。導致后面p1,p2析構之后又再次刪除已經(jīng)析構過(guò)的指針導致異常。
 
這里總結一點(diǎn)就是既然用了shared_ptr,那就信任它,把資源對象的生命周期管理完全交給它。我們不應再對Raw指針進(jìn)行額外的操作,既不要把它取出來(lái)用也不要把它再賦給其他shared_ptr。如果是因為此原因導致問(wèn)題,那不是shared_ptr的錯,而是我們確實(shí)誤用了。
2.1.3 資源對象獲取this指針的shared_ptr
this指針是一個(gè)比較特殊的指針,由于shared_ptr是一種非侵入式(不知google之)的管理方案,資源對象本身對于自己的引用計數毫不知情,所以如果資源對象的方法中要獲取一個(gè)指向自己的shared_ptr,就需要做一些額外的處理。如下所示,CResouce就可以在成員函數中用enable_shared_from_this::shared_from_this()獲取指向自己的shared_ptr了。
 
2.1.4 用臨時(shí)的shared_ptr當參數帶來(lái)的問(wèn)題
考慮下面的代碼有啥問(wèn)題:
void test()
{
foo(boost::shared_ptr<MyObj>(new MyObj()),g());
}
由于C++并不保證函數中參數表達式的執行順序,所以如果例子中執行順序是new MyObj、g()、構造shared_ptr,則g()拋異常的時(shí)候MyObj就會(huì )泄漏?!秂ffective C++》作者建議這樣寫(xiě):
void test()
{
boost::shared_ptr<implementation> sp (new MyObj());
foo(sp,g());
}
2.1.5 shared_ptr與多態(tài)
多態(tài)是面向對象的三個(gè)基本概念之一,我們可以采用多態(tài)技術(shù)實(shí)現接口與實(shí)現的分離。但是shared_ptr并不直接支持多態(tài)。比如有類(lèi)Father和Child,Child繼承自Father。我們不能直接這樣寫(xiě):
shared_ptr<Father> p1(new Father);
shared_ptr<Child> p2 = p1;// 編譯錯誤。
為了實(shí)現多態(tài)的目的,我們必須進(jìn)行一次顯示的類(lèi)型轉換:
shared_ptr<Father> p1(new Father);
shared_ptr<Child> p2 = static_pointer_cast<Child>(p1);// 編譯錯誤。
這是一次有代價(jià)的轉換,但是換來(lái)了靈活性。
2.1.6 執行效率
我們可以想象得到,既然用了資源對象外部的引用計數,就不可避免地要進(jìn)行同步操作以保證引用計數的準確性。雖然在新版本中采用了lock-free的原子整數操作一定程度上降低了線(xiàn)程同步開(kāi)銷(xiāo),但是有人壓測實(shí)際維護引用計數帶來(lái)的開(kāi)銷(xiāo)大約占了5%的CPU,并非全無(wú)代價(jià)。如果真的在乎這5%還是要另想辦法才行。
2.1.7 auto_ptr與shared_ptr
auto_ptr是stl里面自帶的只能指針,它沒(méi)有采用引用計數的方式,它管理對象的方式為:如果兩個(gè)auto_ptr進(jìn)行賦值操作,賦值一方將會(huì )把指針的控制權“轉移”給被賦值一方。從源碼上看是賦值一方把指針交給了被賦值一方,然后自己內部賦一個(gè)NULL。如此一來(lái),auto_ptr就完全沒(méi)辦法放到stl容器中了。以vector<auto_ptr<T>>為例,如果把其中的一個(gè)值賦給外面的一個(gè)auto_ptr,則指針控制權隨之轉移,vector里面的值就變成了NULL,這是隨時(shí)可能導致程序崩潰的事情?!秂ffective C++》作者的說(shuō)法是:遇到這種情況如果編譯器能報錯的話(huà)算你走運,如果沒(méi)報錯的話(huà)你才更應該小心。
2.2 NullPointer for shared_ptr在Java中,如果引用為空我們可以返回null,這和C++中的指針為空我們可以用NULL一樣。但如果shared_ptr為空我們應該如何表達?這是很常見(jiàn)的需求,例如我們有個(gè)函數返回某一cache中資源的shared_ptr,有時(shí)需要給用戶(hù)返回一個(gè)空智能指針表示cache中不存在改資源。直接用默認構造函數生成的只能指針里面是一個(gè)NULL,不過(guò)為了讓看的人更明白,我選擇這樣做:
shared_ptr<MyObj> pNullInfo(static_cast<MyObj*>(0));
如果要判斷pNullInfo是否為空,只需要像下面這樣就行,和Raw指針用法是一樣的。
if( !pNullInfo)
……
else ……
3 靜態(tài)初始化代碼塊
Java類(lèi)里面可以定義一個(gè)static代碼塊來(lái)進(jìn)行一些必要的初始化動(dòng)作。像這樣:
class Foo{
static{
……//一些初始化動(dòng)作
}
…….//其他操作
}
static塊內的代碼將在該類(lèi)的對象第一次被用到的時(shí)候執行(lazy loading),很是方便。但是C++不支持這種這種寫(xiě)法,根據C++的初始化方法,于是我們很難對類(lèi)里定義的static成員進(jìn)行較為復雜一些的初始化動(dòng)作。簡(jiǎn)單賦個(gè)初值還行,復雜了就沒(méi)辦法。通常的解決方案是寫(xiě)一個(gè)static的init()方法,并要求類(lèi)的使用者在使用類(lèi)之前一定要實(shí)現調用一下這個(gè)init來(lái)進(jìn)行必要的初始化。這種方式既麻煩又不安全(多線(xiàn)程情況下還要加鎖)。
我想到一個(gè)解決方法是在類(lèi)里面生成寫(xiě)一個(gè)嵌套類(lèi),并聲明一個(gè)該類(lèi)的static成員,要進(jìn)行的初始化動(dòng)作可以放在這個(gè)嵌套類(lèi)的構造函數里。于是,當該類(lèi)被初始化的時(shí)候相應的動(dòng)作也得到執行。因為C++保證:在類(lèi)被使用之前,所有的靜態(tài)成員都已經(jīng)被初始化完畢。于是我們就可以獲得java中static代碼塊一樣的效果,寫(xiě)個(gè)例子如下:
 
4 參考資料:
1. shared_ptr四宗罪 http://blog.liancheng.info/?p=85
2. 關(guān)于boost::shared_ptr...高興得太早 http://www.cppblog.com/Charlib/archive/2010/02/22/76313.html
 
本文來(lái)自CSDN博客,轉載請標明出處:http://blog.csdn.net/kabini/archive/2010/12/05/6056613.aspx
本文來(lái)自CSDN博客,轉載請標明出處:http://blog.csdn.net/kabini/archive/2010/12/05/6056613.aspx
本文來(lái)自CSDN博客,轉載請標明出處:http://blog.csdn.net/kabini/archive/2010/12/05/6056613.aspx
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
C++ 對象生命周期管理
當析構函數遇到多線(xiàn)程 ── C++ 中線(xiàn)程安全的對象回調 - floweronwarmbe...
C++11智能指針
shared_ptr
如何使用C++共享指針std::shared_ptr?
為什么多線(xiàn)程讀寫(xiě) shared_ptr 要加鎖?
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久