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 ptr" (or faster "const Ptr& 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::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 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 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 inline Ptr<_Tp>::Ptr() : obj(0), refcount(0) {}
template 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 inline void Ptr<_Tp>::addref()
{ if( refcount ) CV_XADD(refcount, 1); }

CV_XADD实际上是一个宏,对应了无锁化编程,类似后置自增运算,返回原值之后再增加,不过这个无锁化可以用于多线程编程,用户自己无需再维护锁了。

具体参考这篇博客:http://blog.csdn.net/hzhsan/article/details/25124901

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

template inline void Ptr<_Tp>::delete_obj()
{
    if( obj ) delete obj;
}

template inline Ptr<_Tp>::~Ptr() { release(); }

注意这里的delete_obj是个泛化版本,对于无法delete的,需要实现一个特化版本。

其他构造函数

template inline Ptr<_Tp>::Ptr(const Ptr<_Tp>& ptr)
{
    obj = ptr.obj;
    refcount = ptr.refcount;
    addref();
}

template 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 inline _Tp* Ptr<_Tp>::operator -> () { return obj; }
template inline const _Tp* Ptr<_Tp>::operator -> () const { return obj; }

template inline Ptr<_Tp>::operator _Tp* () { return obj; }
template inline Ptr<_Tp>::operator const _Tp*() const { return obj; }

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

关于->的重载非常奇怪,竟然无参数的,返回值必须是object*,但是->貌似又被object*共用去访问成员了...

具体参考这篇博客:http://blog.csdn.net/zhuxiufenghust/article/details/5708167


到此为止,我们大概知道如何自己实现一个智能指针类了...




你可能感兴趣的:(OpenCV)