Background
估計只要是C++程序員,沒有一個不痛恨這個野指針啦,而對于我們這種只能通過log來debug的程序員來說,其恨更深。
Solution
每次看到形如下面的代碼時
A* p1 = new A;
A* p2 = p1;
…
delete p1;
我都有一種想要將p2也置成空的沖動,但往往都不遂我心愿,因為在實際中p1,p2的出現實在是神出鬼沒,讓你防不勝防也煩不勝煩。
魯迅先生說過: 不在沉默中暴發就在沉默中滅亡。幸好,我沒有滅亡,所以我要暴發。
在防夠了,煩飽了以后,我下定決心,要端掉這個讓我受盡折磨地暗堡。
復雜問題的解決方案往往都是簡單而“暴力”地,因為問題的難解決一般來說都是因為”暴力”無處著力。我堅信這個規則,所以我的思路很簡單,就是要在delete p1的時候把p2也置為空。
計算機科學中有個神話般的格言:計算機科學中的大部分問題都可以通過增加一個中間層來解決。我希望這一次神話能得以延續。
好了,想想吧,我們要解決的問題實際上只有一個,那就是要找一個機制,讓p2能知悉它所指向對象的狀態(在這里是生命周期)。如果我們把A的生命周期作為一個類提出來,把它叫LifeObject吧,并給每個A的實例一個配備一個LifeObject對象,再讓p1和p2指向這個生命周期對象,這樣我們只須在A的構造函數中創建一個LifeObject對象,在析構函數中將告訴LifeObject,這樣在使用p1和p2的時候就知道當前使用的指針是否是有效的啦。
其關系示意如下:
Implement
思路有了,就像有了作戰計劃,那當然要開始行軍了:
那個示意圖告訴了我們致少4件事:
- p1,p2現在不能是指向A的裸指針啦,因為一個裸指針無法自己做到從LifeObject中獲取相應的信息。需要封裝一下,就叫SafePtr吧;
- SafePtr要提供銷毀A的方法,取名為Destroy;
- LifeObject是有引用計數的,因為它要負責在A銷毀之后,通知p1,p2,…pn,同時它還必須是創建在heap上的;
- LifeObject中要有A的狀態信息,在A構造函數中新建LifeObject時將其狀態信息置為valid,在A析構時置為invalid。
最后,其結構圖大致如下:
從這個結構圖上,我們可以看到:
- LifeObject是用SafeObject的一個指針m_pPointee來指示SafeObject的狀態的,在SafeObject創建時把自己的地址賦給m_pPointee,在析構時將m_pPointee設為NULL;
- 通過將LifeObject的析構函數設為私有,并提供銷毀方法Destroy,可以保證它只能在heap上創建,同時除了它的friend SafeObject以外沒有人能創建它。
- SafePtr提供了判空函數IsNUll和NotNull;
在具體實現的時候,還要考慮的問題是SafePtr要像裸指針一樣,能夠從SafePtr<Derived>轉化為SafePtr<Base>以及從SafePtr<Base> 安全轉換到SafePtr<Derived>一些事。大家可以參見其源碼
How to use it?
- 首先你要讓你的類繼承處SafeObject,如下:
class Test: public SafeObject
- 像使用智能指針一樣使用它
SafePtr<Test> pp(new Test);
SafePtr<Test> pp1 = pp;
if(pp1.NotNull())
{
pp1->output();
pp1->Fun();
SafePtr<SafeObject> pp3(pp);
cout<<pp3->test()<<endl;
}
pp.Destroy();
if(pp1.NotNull())
{
pp1->Fun();
}
Test Code如下:
Advantage
我想大家已經看出來了,接口的定義和智能指針很相似,只是多了一個Destroy函數而已,所以它的優點就有兩個啦:
- 野指針在這兒灰飛煙滅了;
- 如果不調用destroy函數,它就是一智能指針,用它可以防止內存泄漏。
Disadvantage
雖然它解決了C++中兩大問題,但是它的缺點也是有目共睹的,除了常規智能指針的缺陷外它還多出了兩個:
- 不能調用delete來銷毀一個指針,你要通通換成對Destroy函數的的調用;
- 如果你想要享受特權公民的待遇,你就得讓你的類從SafeObject繼承一下。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
