今天让我们来学习库是如何实现动态维护内存的,方便我们以后自己实现的时候作参考...
定义:
/*!
Automatically Allocated Buffer Class
The class is used for temporary buffers in functions and methods.
If a temporary buffer is usually small (a few K's of memory),
but its size depends on the parameters, it makes sense to create a small
fixed-size array on stack and use it if it's large enough. If the required buffer size
is larger than the fixed size, another buffer of sufficient size is allocated dynamically
and released after the processing. Therefore, in typical cases, when the buffer size is small,
there is no overhead associated with malloc()/free().
At the same time, there is no limit on the size of processed data.
This is what AutoBuffer does. The template takes 2 parameters - type of the buffer elements and
the number of stack-allocated elements. Here is how the class is used:
\code
void my_func(const cv::Mat& m)
{
cv::AutoBuffer<float, 1000> buf; // create automatic buffer containing 1000 floats
buf.allocate(m.rows); // if m.rows <= 1000, the pre-allocated buffer is used,
// otherwise the buffer of "m.rows" floats will be allocated
// dynamically and deallocated in cv::AutoBuffer destructor
...
}
\endcode
*/
template<typename _Tp, size_t fixed_size=4096/sizeof(_Tp)+8> class CV_EXPORTS AutoBuffer
{
public:
typedef _Tp value_type;
enum { buffer_padding = (int)((16 + sizeof(_Tp) - 1)/sizeof(_Tp)) };
//! the default contructor
AutoBuffer();
//! constructor taking the real buffer size
AutoBuffer(size_t _size);
//! destructor. calls deallocate()
~AutoBuffer();
//! allocates the new buffer of size _size. if the _size is small enough, stack-allocated buffer is used
void allocate(size_t _size);
//! deallocates the buffer if it was dynamically allocated
void deallocate();
//! returns pointer to the real buffer, stack-allocated or head-allocated
operator _Tp* ();
//! returns read-only pointer to the real buffer, stack-allocated or head-allocated
operator const _Tp* () const;
protected:
//! pointer to the real buffer, can point to buf if the buffer is small enough
_Tp* ptr;
//! size of the real buffer
size_t size;
//! pre-allocated buffer
_Tp buf[fixed_size+buffer_padding];
};
仔细观察,这是一个模板类,模板参数有两个,第一个是类型,第二个带默认值,表示元素个数为4096/sizeof(_Tp)+8,当然还有一个枚举型成员,enum { buffer_padding = (int)((16 + sizeof(_Tp) - 1)/sizeof(_Tp)) };实际上这个成员的取值范围依赖于_Tp,所以范围在1~16之间,所以仅仅用于数组的padding吧,并不能存放元素...
ptr:内存块首地址,默认空间较小时,则ptr = buf
size:内存块可以存放的元素个数
源码:
/// AutoBuffer
template<typename _Tp, size_t fixed_size> inline AutoBuffer<_Tp, fixed_size>::AutoBuffer()
{
ptr = buf;
size = fixed_size;
}
template<typename _Tp, size_t fixed_size> inline AutoBuffer<_Tp, fixed_size>::AutoBuffer(size_t _size)
{
ptr = buf;
size = fixed_size;
allocate(_size);
}
template<typename _Tp, size_t fixed_size> inline AutoBuffer<_Tp, fixed_size>::~AutoBuffer()
{ deallocate(); }
template<typename _Tp, size_t fixed_size> inline void AutoBuffer<_Tp, fixed_size>::allocate(size_t _size)
{
if(_size <= size)
return;
deallocate();
if(_size > fixed_size)
{
ptr = cv::allocate<_Tp>(_size);
size = _size;
}
}
template<typename _Tp, size_t fixed_size> inline void AutoBuffer<_Tp, fixed_size>::deallocate()
{
if( ptr != buf )
{
cv::deallocate<_Tp>(ptr, size);
ptr = buf;
size = fixed_size;
}
}
template<typename _Tp, size_t fixed_size> inline AutoBuffer<_Tp, fixed_size>::operator _Tp* ()
{ return ptr; }
template<typename _Tp, size_t fixed_size> inline AutoBuffer<_Tp, fixed_size>::operator const _Tp* () const
{ return ptr; }
看一下带参数的构造函数,调用了allocate,可以发现当_size<=size时,内存块够用,不需要做任何事情,否则调用deallocate释放内存块,deallocate中先判断ptr是否等于buf,当ptr=buf时,这是块静态数组,无需释放,同时在allocate和deallocate中调用了cv空间下的allocate和deallocate函数。
我们查看一下原型:
template<typename _Tp> static inline _Tp* allocate(size_t n)
{
return new _Tp[n];
}
template<typename _Tp> static inline void deallocate(_Tp* ptr, size_t)
{
delete[] ptr;
}
很简单的写法,没什么难度...
到此为止,AutoBuffer轻松实现了...
AutoBuffer<int> x;
x[0] = 1;
注意一下,有一个特殊的用法,我们可以使用[]运算符,这是为什么呢?原因是上面两个强制转换函数,返回的是一个首地址ptr,于是相当于访问ptr[]了...