opencv中的Ptr源碼分析

來研究一下opencv中的Ptr類,所謂的智能指針...

//////////////////// generic_type ref-counting pointer class for C/C++ objects ////////////////////////  
  
/*! 
  Smart pointer to dynamically allocated objects. 
 
  This is template pointer-wrapping class that stores the associated reference counter along with the 
  object pointer. The class is similar to std::smart_ptr<> from the recent addons to the C++ standard, 
  but is shorter to write :) and self-contained (i.e. does add any dependency on the compiler or an external library). 
 
  Basically, you can use "Ptr<MyObjectType> ptr" (or faster "const Ptr<MyObjectType>& ptr" for read-only access) 
  everywhere instead of "MyObjectType* ptr", where MyObjectType is some C structure or a C++ class. 
  To make it all work, you need to specialize Ptr<>::delete_obj(), like: 
 
  \code 
  template<> void Ptr<MyObjectType>::delete_obj() { call_destructor_func(obj); } 
  \endcode 
 
  \note{if MyObjectType is a C++ class with a destructor, you do not need to specialize delete_obj(), 
  since the default implementation calls "delete obj;"} 
 
  \note{Another good property of the class is that the operations on the reference counter are atomic, 
  i.e. it is safe to use the class in multi-threaded applications} 
*/  
template<typename _Tp> class CV_EXPORTS Ptr  
{  
public:  
    //! empty constructor  
    Ptr();  
    //! take ownership of the pointer. The associated reference counter is allocated and set to 1  
    Ptr(_Tp* _obj);  
    //! calls release()  
    ~Ptr();  
    //! copy constructor. Copies the members and calls addref()  
    Ptr(const Ptr& ptr);  
    //! copy operator. Calls ptr.addref() and release() before copying the members  
    Ptr& operator = (const Ptr& ptr);  
    //! increments the reference counter  
    void addref();  
    //! decrements the reference counter. If it reaches 0, delete_obj() is called  
    void release();  
    //! deletes the object. Override if needed  
    void delete_obj();  
    //! returns true iff obj==NULL  
    bool empty() const;  
  
  
    //! helper operators making "Ptr<T> ptr" use very similar to "T* ptr".  
    _Tp* operator -> ();  
    const _Tp* operator -> () const;  
  
    operator _Tp* ();  
    operator const _Tp*() const;  
  
protected:  
    _Tp* obj; //< the object pointer.  
    int* refcount; //< the associated reference counter  
};  

所謂的智能指針,其實就是模板參數可以是任意的c++類,但考慮到對象在使用完畢的時候需要析構,因此要求特化delete_obj()函數。

接下來看源碼

template<typename _Tp> inline Ptr<_Tp>::Ptr() : obj(0), refcount(0) {}  
template<typename _Tp> inline Ptr<_Tp>::Ptr(_Tp* _obj) : obj(_obj)  
{  
    if(obj)  
    {  
        refcount = (int*)fastMalloc(sizeof(*refcount));  
        *refcount = 1;  
    }  
    else  
        refcount = 0;  
}  

obj顯然是指向對象的指針,refcount是引用數,默認構造函數就構造了一個空對象,帶參數的構造函數則需要動態分配內存,當然如果參數傳入NULL的話,就與默認構造函數沒有區別了...注意這裏使用了fastMalloc這個函數,使用了對齊指針的技術...

void* fastMalloc( size_t size )  
{  
    uchar* udata = (uchar*)malloc(size + sizeof(void*) + CV_MALLOC_ALIGN);  
    if(!udata)  
        return OutOfMemoryError(size);  
    uchar** adata = alignPtr((uchar**)udata + 1, CV_MALLOC_ALIGN);  
    adata[-1] = udata;  
    return adata;  
}  
      
void fastFree(void* ptr)  
{  
    if(ptr)  
    {  
        uchar* udata = ((uchar**)ptr)[-1];  
        CV_DbgAssert(udata < (uchar*)ptr &&  
               ((uchar*)ptr - udata) <= (ptrdiff_t)(sizeof(void*)+CV_MALLOC_ALIGN));   
        free(udata);  
    }  
}  

其實內存多開了20字節的空間,其中4字節是用來存儲這塊空間的首地址的,用於釋放空間時使用,還有16字節是用來調節地址,使地址達到16的倍數,CV_MALLOC_ALIGN在這裏是16.

看一下alignPtr這個就是將地址向上調整至16的倍數,udata+1這裏是指針加法,其實加的是sizeof(uchar**) 也就是4,這個4字節就是用來存首地址的,爲什麼要強轉成uchar**,因爲要訪問這4個字節的內容,這個內容是個首地址,於是就是二級指針了。adata[-1]爲什麼這樣?因爲它先留出4字節之後在調整地址至16倍數,也就是實際存儲數據的地址前還有至少4字節的空間,-1就是向前4字節,這個用來存首地址...

具體參考這篇博客http://blog.csdn.net/lming_08/article/details/26821963?utm_source=tuicool&utm_medium=referral

這個也值得學習

template<typename _Tp> inline void Ptr<_Tp>::addref()  
{ if( refcount ) CV_XADD(refcount, 1); }  

CV_XADD實際上是一個宏,對應了無鎖化編程,類似後置自增運算,返回原值之後再增加,不過這個無鎖化可以用於多線程編程,用戶自己無需再維護鎖了。

具體參考這篇博客:http://blog.csdn.net/hzhsan/article/details/25124901

template<typename _Tp> inline void Ptr<_Tp>::release()  
{  
    if( refcount && CV_XADD(refcount, -1) == 1 )  
    {  
        delete_obj();  
        fastFree(refcount);  
    }  
    refcount = 0;  
    obj = 0;  
}  
  
template<typename _Tp> inline void Ptr<_Tp>::delete_obj()  
{  
    if( obj ) delete obj;  
}  
  
template<typename _Tp> inline Ptr<_Tp>::~Ptr() { release(); }  

注意這裏的delete_obj是個泛化版本,對於無法delete的,需要實現一個特化版本。

其他構造函數

template<typename _Tp> inline Ptr<_Tp>::Ptr(const Ptr<_Tp>& ptr)  
{  
    obj = ptr.obj;  
    refcount = ptr.refcount;  
    addref();  
}  
  
template<typename _Tp> inline Ptr<_Tp>& Ptr<_Tp>::operator = (const Ptr<_Tp>& ptr)  
{  
    int* _refcount = ptr.refcount;  
    if( _refcount )  
        CV_XADD(_refcount, 1);  
    release();  
    obj = ptr.obj;  
    refcount = _refcount;  
    return *this;  
}  

注意拷貝構造函數,參數對應的對象無需釋放,所以引用數加1,而賦值構造函數則需要先釋放掉參數對應的對象,這是區別。

template<typename _Tp> inline _Tp* Ptr<_Tp>::operator -> () { return obj; }  
template<typename _Tp> inline const _Tp* Ptr<_Tp>::operator -> () const { return obj; }  
  
template<typename _Tp> inline Ptr<_Tp>::operator _Tp* () { return obj; }  
template<typename _Tp> inline Ptr<_Tp>::operator const _Tp*() const { return obj; }  
  
template<typename _Tp> inline bool Ptr<_Tp>::empty() const { return obj == 0; }  

關於->的重載非常奇怪,竟然無參數的,返回值必須是object*,但是->貌似又被object*共用去訪問成員了...

具體參考這篇博客:http://blog.csdn.net/zhuxiufenghust/article/details/5708167

 

到此爲止,我們大概知道如何自己實現一個智能指針類了...

原文:https://blog.csdn.net/eric_e/article/details/79375602

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章