棧里的對象
"構造完畢,請睜開眼吧!",迷迷糊糊中,聽着這個聲音,我睜開了眼睛,一位小哥映入眼簾。
“你是誰?我是誰?這又是什么地方?”,我的大腦一片空白。
“你是一個C++對象,這里是棧空間。我是創建你的線程,你先待着,一會兒會用到你,拜~”
“唉,等一下...”,還沒等我回過神,小哥已經走遠。
環顧四周,這個叫棧的地方,有好多好多的對象,大小不一,不遠處還有一個巨無霸對象足足有好幾千字節。
“哇!好大一個對象!”,我不禁發出了驚嘆。
“一看就是沒見過世面的,這才多小的個頭,就在那大呼小叫的”,突然傳來一個聲音。
“誰在說話?”,我四處望去,卻沒發現動靜。
“往哪看呢,沒那么遠,就在你旁邊”,我這才注意到眼皮底下有個小個子在說話。
“你才8個字節的對象,好大的口氣!”
“唉,糾正一下,我可不是對象哦,我只是一個指針,我指向的對象那才叫大呢!”
原來這小個子是個指針,順着他指向的地址望去,果然有一龐然大物,雖不見其全身,估摸着至少也有幾MB的大小。
“唉,你指向的對象為何不和我們在一塊兒,我看那地方好像不屬於棧的地界兒了”。
“說你沒見過世面,你還不承認。咱們這棧空間有限,哪里裝得下那么多大對象,他們那里叫堆區,一般有身份的對象都放在那邊的。那里的對象都是通過new關鍵字生成的,你們可沒這待遇哦,還有...”
“噓!先別說話,你看線程小哥在干嘛?”,看到之前的小哥去了堆區,我打斷了指針小個子的說話。
堆里的對象
只見小哥執行malloc函數在堆區划了一片空間,接着調用構造函數在那片空間上創建了一個對象。
“你剛才不是說那里的對象是new出來的嗎,騙人!”
“這你都不知道,這個new = 內存分配 + 構造函數,而內存分配一般默認就是malloc,不懂還一驚一乍的,切~”,被指針小個子懟了回來,我真想找個縫躲起來。
正在我難為情的時候,線程小哥又來到了棧區,在我的頭上不遠處又創建了一個對象。
“你們好,我是新來的,請多指教!”
“歡迎歡迎,你好,請問你是?”,我第一個上前打招呼。
“咦,你手里怎么握着剛剛那個新對象的地址呢?你也是個指針嗎,怎么跟我長得不一樣”,小個子指針也湊了上來。
“這位老弟果然好眼力,我也確實是個指針,不過啊,你只是個裸指針,而我是個智能指針!”,新來這小子言語間透露着一股傲嬌的味道。
“智能指針?這是個啥?”,小個子指針繼續追問。
“哎喲,沒想到見多識廣的你也有不知道的事嘛!哈哈”,我趁此機會嘲笑了一把小個子。
智能指針
“好吧,那我就自我介紹下,我是一個shared_ptr,人類將我叫做智能指針,我手里握着一個裸指針,彌補了直接使用裸指針帶來的煩惱!現在人類可喜歡用我們了。”
“煩惱?什么煩惱?”,小個子指針有點不太服氣。
“當然是內存泄漏啦!像你們直接使用new創建出來的對象,程序員們要是忘記寫delete釋放,那這對象就成為被拋棄的孤兒,就會白白占用堆空間。一個兩個還好,要是是在一個頻繁執行的函數中發生這種事情,那堆空間就被這些拋棄的對象占滿,那后果不堪設想!”,說完,智能指針的嘴角揚起了微笑。
“哦,還沒完呢,裸指針還有個毛病就是誰來釋放的問題,因為你們裸指針就是一個地址,可以在函數之間四處傳遞,最后傳來傳去,到底誰來釋放,是不是還有別的函數和線程在使用都搞不清楚了,要是不釋放呢,就內存泄漏了,要是別人還在用呢,釋放了又會出問題,所以怎么着都很惱火”,說完,智能指針嘴角又上揚了許多。
小個子指針聽完有些氣餒,停頓一會之后再一次發出了疑問:“那你們是如何解決這個問題的呢?”
“我的內部有一個計數器,初始創建完對象后,把地址告訴了我,計數器值是1,以后每次把我賦值給別的智能指針,或者是函數傳參拷貝到另一個shared_ptr,我的計數器值都會+1,表示又多了一個shared_ptr在使用它,相反的如果有一個shared_ptr對象析構了,那計數器值就-1。直到最后誰發現了這個計數器變為0,說明沒人在用這個對象了,那就執行delete把它釋放掉。看,是不是很聰明吶!”
“唉,我聽明白了,原來你shared_ptr自己也是一個對象,利用自己的構造函數和析構函數來對一個計數器進行增加和減少操作來實現對裸指針指向對象的管理,是這樣吧?”,我接上了這小子的話茬。
“對,沒錯,這個叫引用計數!我本身和你一樣,也是一個對象哦,咱們這些在棧里的對象,有一個特別大的好處就是不用擔心會內存泄漏,也不用擔心析構函數不會被調用,編譯器在生成指令的時候都已經自動做好了。”
智能指針的幫手
看着我倆打得火熱,小個子指針不開心了,“有什么了不起,我能說出一種情況,就讓你當場下不來台,哼!”
“哦,你倒是說說看?”
“如果兩個對象A和B,A里面有個shared_ptr指向B,B里面有個shared_ptr指向A,也就是互相引用了,這樣子,你們兩個shared_ptr計數器永遠是1,誰也釋放不了,怎么樣,沒轍了吧!哈哈”,小個子一邊說一邊拍自己大腿,樂得不行。
我還在腦子里冥想這到底是個什么場景,智能指針開始回懟了:“你說的這種情況一早就想到了,在遇到互相引用的時候,我就要請出我的助手weaked_ptr了,它和我功能類似,最大的區別就是它不增加引用計數,只要A和B其中一方把shared_ptr換成weaked_ptr,就能打破你說的循環!”
小個子沒料到這智能指針居然還有幫手,一下子語塞,最后實在沒辦法,憋出了一句:“你是厲害,可是很多現成的模塊接口都是裸指針作為參數,要是用你肯定好多不兼容,還得我們裸指針上。”
“唉,此言差矣,既然是智能指針,這點困難都解決不了,哪有臉敢稱智能兩字?我們重載了->運算符和*運算符,讓人類用起我們來和普通指針一樣的體驗。還提供了get()方法提供原始指針,在遇到你說的情況下也能用得上。怎么樣,這下服了吧?唉,你別走啊,真小氣~”
原來沒等他說完,小個子指針已經灰溜溜的躲開了。
未完待續·······
彩蛋
“不好了,不好了”,小個子指針氣喘吁吁的喊着。
“出什么事了?”
“你們看線程老哥在干嘛”
只見線程老哥執行memcpy函數,正在不斷覆蓋棧空間,智能指針那家伙已經遭殃,馬上到我了·······
欲知后事如何,請關注后續精彩......
精彩回顧: