C++程序員最難的一環就是處理內存泄漏。
很多情況下,一個對象在一個模塊里分配了內存,忘記了釋放,或者在另一個模塊里釋放都會導致內存相關的問題。
SOUI中大部分暴露在應用層的對象都使用類似COM的引用計數來管理對象的生命周期,包含SWindow, ISkin, EventArg, SStringT, IRenderTarget, IBitmap, IAdapter以及各種擴展組件。
SOUI中引用計數的基類是:SOUI::IObfRef
namespace SOUI { struct IObjRef { virtual long AddRef() PURE; virtual long Release() PURE; virtual void OnFinalRelease() PURE; }; }
SOUI中使用引用計數有一個簡單的原則:一個對象,誰AddRef,那也應該它來Release。
經常有人問:為什么我調用了SImageWnd::SetImage就有內存泄漏了?
用戶的代碼可能是下面這樣的:
bool SDemoSkin::SetImage(SStringW imgfile) { m_bIsColor = false; m_FilePath = imgfile; IBitmap *image = LOADIMAGE2(L"file:" + imgfile); if (image) { SetImage(image); //image->Release(); return true; } return false; }
這個問題很簡單,看一下SetImage(IBitmap*)這個方法的代碼就知道,它會自己持有這個IBitmap*對象。
通常這是用戶從文件或者內存加載后創建的IBitmap對象,所有SOUI對象的創建都自動調用了AddRef,因此調用它個方法的人調用完成后,不再使用這個對象,則應該相應的調用一下Release(即打開代碼中被注釋的行)。
SOUI中也提供了一個智能指針來簡化這個引用計數的操作:SAutoRefPtr (2.x是CAutoRefPtr)
使用智能指針,上面代碼可以改寫為:
bool SDemoSkin::SetImage(SStringW imgfile) { m_bIsColor = false; m_FilePath = imgfile; SAutoRefPtr<IBitmap> image; image.Attach(LOADIMAGE2(L"file:" + imgfile)); if (image) { SetImage(image); return true; } return false; }
注:由於LOADIMAGE2內部已經調用了AddRef,而智能指針直接賦值時會自動AddRef,因此這里要用image.Attach來獲得指針。Attach不會自動調用AddRef.
使用好IObjRef,配合上SAutoRefPtr,可以將SOUI的內存泄漏問題降低很多。
啟程軟件 2019年10月17日