Cyclic dependency主要來自reference counted物件互相持有對方,導致無法順利將其指向所指向的物件解構。其實只需要一個reference counted物件即可說明此例,只要該物件持有自己即可,接下來以此為例說明。
我們以SmartPtr來做為reference counted此物件,圖中的每個箭頭代表一個reference count。
假設有個物件內容如下:
struct Self{
SmartPtr<Self> m_pSelf;
};
假設方塊物件是SmartPtr,裡面的星號代表指向真正物件的指標,而圓形物件則是Self。當你寫下 SmartPtr
接下來你繼續寫完整行 SmartPtr
此時counter為1(一個箭頭)。這時你再寫 pSelf->m_pSelf = pSelf; 也就是另pSelf的data member m_pSelf指向自己,於是counter變成2了(有兩個箭頭,都指向我們的圓型物件Self),示意圖如下:
好了,假設現在要離開pSelf所在的scope了,於是SmartPtr的destructor被呼叫,而將counter減1,counter剩下1,而圖案變成這樣:
問題來了:原來唯一知道圓形物件的pSelf已經不存在了,於是沒人知道這個被new出來的圓形物件,而他的reference counter不為0,也無法降為0,因此它的destructor不會被呼叫起來!! Leak就這樣產生啦!完整的code如下,你可以自己試試看self的destructor會不會被呼叫起來(當然你得自己寫個destructor幫助你觀察):
void Test( ){
SmartPtr<Self> pSelf = new Self;
pSelf.m_pSelf = pSelf;
}
如果不知道為什會有人寫這種code,你不妨想像現在有兩個SmartPtr A跟B,其中A有個member指向B,B也有個member指向A,code如下:
void Test( ){
SmartPtr<Self> pSelfA = new self, pSelfB = new self;
pSelfA.m_pSelf = pSelfB;
pSelfB.m_pSelf = pSelfA;
}
甚至不一定要是兩個,可以是一大串,A指向B,B指到C,C指到D,……最後又指回A,這個問題又發生了。
解決方案呢?寫累了,下次再寫吧!反正就是用WeakPtr的概念,WeakPtr只會observe而不會持有一個物件,Boost::weak_ptr 或 Loki::StrongPtr (整合了strong pointer與weak pointer)都可以解決這問題。
註:這篇是從 Yahoo Blog 搬過來的,原發文時間為 2007/02/12 16:47。
沒有留言:
張貼留言