SOUI中对象的生命周期管理

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日

 

posted @ 2019-10-17 18:06  启程软件  阅读(1518)  评论(0编辑  收藏  举报