第三章 WebKit智能指針詳解

說到智能指針吱涉,網上相關資料數不勝數,這里我就我自己的理解給大家分享一下猿诸。

1.什么是智能指針

我們在編寫c++程序的時候都知道所使用的對象都有著嚴格定義的生命周期:

? ? ? 全局對象在程序啟動時分配內存辅甥,在程序結束時銷毀;

? ? ? 局部對象在進入其定義所在的程序塊時被創(chuàng)建祟绊,在離開塊時被銷毀楼入;

? ? ? 局部static對象在第一次使用前分配,在程序結束時銷毀久免;

? ? ? 動態(tài)對象只有當顯示的被釋放時浅辙,方能被銷毀扭弧;

動態(tài)對象的正確釋放被證明是編程中極其容易出錯的地方:

常見錯誤一:忘記delete阎姥,導致內存泄露;
常見錯誤二:野指針鸽捻,對象已經被釋放呼巴,這里注意,此時的指針成為了懸垂指針御蒲,即指向曾經存在的對象衣赶,但該對象已經不再存在。結果未定義厚满,而且難以檢測)這時候我們再次使用府瞄,會產生使用非法內存的指針;
常見錯誤三:重復delete碘箍;

由于WebKit大量使用動態(tài)對象遵馆,所以類似這樣的錯誤肯定會有很多,為了更安全的使用動態(tài)對象丰榴,WebKit使用智能指針來管理動態(tài)內存货邓,當一個對象應該被釋放時,指向它的智能指針可以確保自動地釋放它四濒。


2.智能指針實現原理

智能指針(smart pointer)的一種通用實現技術是使用引用計數(reference count)换况;

智能指針類將一個計數器與類指向的對象相關聯(lián),引用計數跟蹤該類有多少個對象共享同一指針盗蟆;

每次創(chuàng)建類的新對象時戈二,初始化指針并將引用計數置為1;

當對象作為另一對象的副本而創(chuàng)建時喳资,拷貝構造函數拷貝指針并增加與之相應的引用計數觉吭;

對一個對象進行賦值時,賦值操作符減少左操作數所指對象的引用計數骨饿,并增加右操作數所指對象的引用計數亏栈;

調用析構函數時台腥,構造函數減少引用計數

如果引用計數減至0,則刪除基礎對象


3.Webkit智能指針

官方文檔(2015.4.27):

http://www.webkit.org/coding/RefPtr.html

WebKit智能指針歷史:

最早绒北,很多對象采用引用計數(繼承自模板類RefCounted)黎侈,依靠手動調用其ref()或者deref()的方式來實現

到了2005年,發(fā)現越來越多的內存泄露問題闷游,其原因就是ref和deref函數調用不匹配導致峻汉。于是WebKit采用智能指針來解決此類問題

但是早期的試驗表明智能指針會進行額外的引用計數處理而影響性能。因此我們尋找一種方式來讓我們使用智能指針同時避免引用計數跳變(churn)

Maciej Stachowiak設計了一組類模板脐往,RefPtr和PassRefPtr休吠,實現這一模式來解決WebKit中惱人的引用計數問題;而C++11標準中增加的std::move操作可以更高效的來解決业簿,所以PassRefPtr逐漸被廢棄

到了2013年瘤礁,發(fā)現針對智能指針和原始指針的使用過程中,判空操作激增梅尤,但其實很多都是沒有必要的柜思;WebKit開始更多的使用引用(Ref)來代替指針(RefPtr)

WebKit智能指針實現:

WebKit智能指針由類族RefPtr來實現,其核心有如下三個類:RefCounted巷燥、RefPtr赡盘、Ref

其中RefCounted提供了引用計數器,RefPtr缰揪、Ref提供了自動管理引用計數器的功能

RefCounted源碼在RefCounted.h中陨享,這個文件里定義了兩個類:非模板類RefCountedBase和模板類RefCounted

定義了成員變量:int m_refCount

函數:ref()
函數:deref()
函數:derefBase()

前面的ref()和deref()就是RefCounted的核心功能了,ref時引用計數加1钝腺,deref時引用計數減1抛姑,減到0就將自己銷毀

使用時需要繼承自RefCounted,但是這里不同于一般的繼承拍屑,舉例:

但是僅僅使用RefCounted類還無法稱之為智能指針途戒,RefCounted使用方法繁瑣

每次操作對象都要做ref()或者deref()操作

為了簡化RefCounted的使用方法,RefPtr誕生了僵驰,其源代碼在RefPtr.h中喷斋,在這個文件中定義了一個模板類RefPtr,RefPtr才能算一個簡單的智能指針

看上面的代碼就能很清楚的知道蒜茴,當把一個對象賦值給RefPtr包裝過的對象后星爪,它會先被賦值的對象ref,然后再給自己原來的對象deref粉私,這實際上就是上例中setTitle的過程顽腾,所以改寫后就極大簡潔了代碼

修改版

這樣修改雖然簡潔了代碼,但沒有簡潔代碼實際的執(zhí)行過程。此段代碼還是會頻繁的使用ref和deref抄肖,這樣就會導致引用計數跳變的問題久信,比如:

開始引用計數為1

setTitle將untitledTitle賦值給document的成員變量,引用計數增加為2

此程序塊返回漓摩,untitledTitle變量銷毀裙士,引用計數減少到1

引用計數跳變在函數參數和返回值都涉及的情況下更嚴重

終極解決方案:

在本例中,引用計數始終為1管毙,另外腿椎,WTF::move主要是封裝了std::move(此函數在賦值操作中直接取右值,中間不會涉及到引用計數加減)夭咬,并添加了錯誤處理啃炸。另外還可以結合PassRefPtr處理這種情況,具體百度有相關資料卓舵,RefPtr和PassRefPtr南用,講得還是非常容易理解的。

Ref源代碼在Ref.h中边器,在這個文件中定義了一個模板類Ref训枢,Ref很像RefPtr,但是Ref是一個引用忘巧,而RefPtr是一個指針,Ref是一個智能引用睦刃,所以其值不可能為null

智能指針相關函數功能:

get()

可以通過智能指針RefPtr的get函數獲取到原始指針

同樣砚嘴,可以通過智能引用Ref的get函數獲取到原始引用,也可以通過智能引用Ref的ptr函數獲取到原始指針

leafRef()

此函數把管理的指針轉移給接受者涩拙,不涉及到引用計數的操作

adoptRef()

原始指針轉換為智能指針

? ? ? 一個繼承自RefCounted模板類的對象在被創(chuàng)建時际长,需要保證引用計數值為1。最好的辦法是將創(chuàng)建的對象放到Ref中兴泥,以免操作完成后忘記調用此對象的deref()函數工育,這意味著只要調用此類的new操作后要立即調用adoptRef函數

在WebKit中創(chuàng)建對象時,采用create函數替代直接new操作


4.WebKit智能指針使用原則

針對局部變量:

? ? ? 如果生命周期和所有者是確認的搓彻,則允許使用原始引用或指針

? ? ? 如果代碼需要維持對該對象的引用并確定它的生命周期如绸,則應該使用Ref,如果其值可能為null旭贬,則使用RefPtr

針對類的成員變量:

? ? ? 如果生命周期和所有者是確認的怔接,則允許使用原始引用或指針

? ? ? 如果這個類需要維持對該對象的引用并確定它的生命周期,則應該使用Ref或者RefPtr

針對函數的形參:

? ? ? 如果該函數不需要維持對該對象的引用稀轨,則函數參數允許使用原始引用或指針

? ? ? 如果該函數需要維持對該對象的引用扼脐,則函數參數應該是Ref&&或者RefPtr&&。這種情況常見于很多setter函數

針對函數的返回值:

? ? ? 如果返回值是一個對象奋刽,并且所有者并未轉移瓦侮,則返回值類型應該是原始引用或者指針艰赞。這種情況常見于很多getter函數

? ? ? 如果返回值是一個新創(chuàng)建的對象,或者其所有者因為某些原因需要被轉移肚吏,則返回值類型應該是Ref或者RefPtr

新對象:

? ? ? 一個新創(chuàng)建出來的對象應該立即轉化為Ref引用猖毫,以便智能指針能夠自動的對所有引用計數

? ? ? 針對繼承自RefCounted類的對象,上面的過程需要用adoptRef函數來轉化

? ? ? 好的使用智能指針的習慣是將類的構造函數定義為私有函數须喂,并且定義一個公共的create函數用來創(chuàng)建類的對象并返回一個Ref引用


PassRefPtr吁断、RefPtr與Raw Ptr轉換圖
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市坞生,隨后出現的幾起案子仔役,更是在濱河造成了極大的恐慌,老刑警劉巖是己,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件又兵,死亡現場離奇詭異,居然都是意外死亡卒废,警方通過查閱死者的電腦和手機沛厨,發(fā)現死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來摔认,“玉大人逆皮,你說我怎么就攤上這事〔胃ぃ” “怎么了电谣?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵,是天一觀的道長抹蚀。 經常有香客問我剿牺,道長,這世上最難降的妖魔是什么环壤? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任晒来,我火速辦了婚禮,結果婚禮上郑现,老公的妹妹穿的比我還像新娘湃崩。我一直安慰自己,他們只是感情好懂酱,可當我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布竹习。 她就那樣靜靜地躺著,像睡著了一般列牺。 火紅的嫁衣襯著肌膚如雪整陌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天,我揣著相機與錄音泌辫,去河邊找鬼随夸。 笑死,一個胖子當著我的面吹牛震放,可吹牛的內容都是我干的宾毒。 我是一名探鬼主播,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼殿遂,長吁一口氣:“原來是場噩夢啊……” “哼诈铛!你這毒婦竟也來了?” 一聲冷哼從身側響起墨礁,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤幢竹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后恩静,有當地人在樹林里發(fā)現了一具尸體焕毫,經...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年驶乾,在試婚紗的時候發(fā)現自己被綠了邑飒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡级乐,死狀恐怖疙咸,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情唇牧,我是刑警寧澤罕扎,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站丐重,受9級特大地震影響,放射性物質發(fā)生泄漏杆查。R本人自食惡果不足惜扮惦,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望亲桦。 院中可真熱鬧崖蜜,春花似錦、人聲如沸客峭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舔琅。三九已至等恐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背课蔬。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工囱稽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人二跋。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓战惊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親扎即。 傳聞我的和親對象是個殘疾皇子吞获,可洞房花燭夜當晚...
    茶點故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,524評論 1 51
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,343評論 25 707
  • 引言:由于未來需要深入android底層進行系統(tǒng)級別的開發(fā),所以最近在看老羅的《Android系統(tǒng)源代碼情景分析》...
    拿破輪閱讀 2,217評論 0 9
  • 指針 在傳統(tǒng)的C++編程中谚鄙,指針的使用一直是一把雙刃劍各拷。指針賦予了我們直接操作硬件地址的能力,但同時也帶來了諸多問...
    passerbywhu閱讀 2,875評論 0 2
  • 前天下午偶然翻閱手機襟锐,微信服務通知里顯示“微信邀請你使用公眾號原創(chuàng)保護功能”撤逢。記得我發(fā)第一篇公眾號文章的時候,就有...
    桃小妖聲聲慢閱讀 370評論 0 0