由于本人前段时间一直在进行图像处理的研究,大家都知道图像是二维的,故在程序中经常会有二维数组的使用,而在C++中是用二维指针T** value来表示二维数组。如果直接使用T** value就会在程序中经常出现二重for循环分配内存、二重for循环释放内存的代码——非常的无趣,非常的容易出错。(C++的指针使用方法请见另一篇博文http://www.cnblogs.com/xiangism/archive/2011/07/18/2109981.html )
因此本人写了一个TwoDimesionArray<T>的泛型类用以表示二维数组,写这个类的灵感来些于boost::shared_ptr。
boost::shared_ptr<T>是一个非常好用的智能指针。程序员只管new出指针对象,而不需要自己手动delete释放。shared_ptr最关键的地方在于,它实现了引用计数,当计数大于0时,对象一直存在;而当引用计数等于0时,则释放内存。为了提高性能,将一个shared_ptr<T>对象赋值给另一个shared_ptr<T>后,两个shared_ptr<T>对象将指向同一块内存,修改其中一个也将修改另一个对象的值。
1 void test1() 2 { 3 shared_ptr<int> va=shared_ptr<int>(new int(1)); 4 shared_ptr<int> vb=va; 5 (*vb)=2; 6 cout<<*va<<"\n"; //这里将输出2 7 }//当程序退出这个函数后,第3行末尾new出来的内存将自动被释放
本人的TwoDimesionArray<T>也想实现类似的功能。
先看具体用法:
TwoDimesionArray<int> creatValue() { TwoDimesionArray<int> values(1000,2000); //在Release模式下,这个类的效率可以和原生二维指针相媲美 values.SetAllValue(1); values.SetValue(10,20,100); return values; //可以将values将为函数返回值返回,这里只会引用计数加1,而不会有新对象产生 } void changeValue(TwoDimesionArray<int> values) //直接用作函数参数,指向的内存地址仍为原先的内存地址 { values.SetValue(10,20,200); //这里修改的值会影响到函数到 } void test1() { TwoDimesionArray<int> values=creatValue(); changeValue(values); cout<<values.GetValue(10,20)<<"\n" ; //这里将输出200 }
使用shared_ptr和TwoDimesionArray时就像在C#中使用对象(对象都是引用)一样,这也使得本人现在看C++代码比看C#代码更亲切了。
下面讲解其具体实现方式:
TwoDimesionArray<T>的类图
其中m_width,m_height分别表示二维数组的宽高;m_value是二维指针,指向元素真正所在的地址;m_count是引用计数,表示内存地址被引用的次数。
下面分别讲解类的成员函数——
一、构造函数
TwoDimesionArray() { m_width=shared_ptr<int>(new int(0)); m_height=shared_ptr<int>(new int(0)); m_count=shared_ptr<long>(new long(0)); //cout<<"construct\n"; } TwoDimesionArray(int _width,int _height) { m_width=shared_ptr<int>(new int(_width)); m_height=shared_ptr<int>(new int(_height)); m_value=new T *[_height]; //如果这里用new T*[_width]那么在取值时即可x,y正序 m_count=shared_ptr<long>(new long(1)); for (int j=0;j<_height;++j) { m_value[j]=new T[_width]; } //cout <<"w,h\n"; } TwoDimesionArray(const TwoDimesionArray &_rhs) :m_width(_rhs.m_width) ,m_height(_rhs.m_height) ,m_value(_rhs.m_value) ,m_count(_rhs.m_count) { if(*m_count!=0) ++(*m_count); }
此类接受无参数的构造函数和一个指定宽、高的构造函数。注意在第二个构造函数中,就指定了m_count=1,并且用for循环分配了内存。在复制构造函数中,将四个成员分别赋值后,再将引用计数自加1。在自加1之前之所有要判断是否为0,是为了防止下面的代码
void test3() { TwoDimesionArray<int> ints ; //这里引用计数为0 TwoDimesionArray<int> ints1(ints); //这里应该还为0 }
二、重载等号
TwoDimesionArray& operator=(const TwoDimesionArray &_r) { //cout<<"==\n"; if(this==&_r) return *this; dispose(); m_value=_r.m_value; m_width=_r.m_width; m_height=_r.m_height; m_count=_r.m_count;
if(*m_count!=0) ++(*m_count); return *this; }
这里先判断是否为自己赋值给自己。再进行成员变量的赋值,与复制构造函数类似。
三、get,set函数
const T& GetValue(const int x,const int y) const { if(!IsInArray(x,y)) { //boost::format fmt("%1%,%2%"); //fmt % x % y; ////string str=boost::lexical_cast<string>(x) //throw std::out_of_range(fmt.str()); //ASSERT(0); throw; } return m_value[y][x]; } T& GetValue(const int x,const int y) { if(!IsInArray(x,y)) { throw; } return m_value[y][x]; } const int GetWidth() const {return *m_width;} const int GetHeight() const {return *m_height;} void SetAllValue(const T &_value) //N次在这个函数上栽跟头,开始这个函数名为SetValue(与下面的相同),当在循环中给每一项赋值时,本来应该是twoD.SetValue(i,j,value),而经常会写成twoD.SetValue(value) { for (int j=0;j<*m_height;++j) { for (int i=0;i<*m_width;++i) { m_value[j][i]=_value; } } } void SetValue(int x,int y,const T &_value) { if(!IsInArray(x,y)) { throw; } m_value[y][x]=_value; }
上面GetValue有两个重载形式,支持函数左值赋值。GetWidth,GetHeight分别为得到宽、高。
SetAllValue给二维数组每个元素赋同样的值,一般用在初始化中。SetValue给指定位置上的元素赋值。
注意引用到特定位置元素时,m_value[y][x]中x,y的顺序。
四、深度复制函数
TwoDimesionArray<T> DeepClone() const { TwoDimesionArray<T> r(*m_width,*m_height); for (int j=0;j<*m_height;++j) { for (int i=0;i<*m_width;++i) { r.m_value[j][i]=m_value[j][i]; } } return r; }
由于用等号操作符会使两个TwoDimesionArray<T>引用到同一个内存地址,故有必要提供一个深度复制函数。
五、析构函数
~TwoDimesionArray() { dispose(); //cout <<"Dispose\n"; } void dispose() { if(*m_count==0) return; --(*m_count); if(*m_count==0) { for (int j=0;j<*m_height;++j) { //这里必须加[],在有些情况下只能用delete[] delete[] m_value[j]; } // if(m_height!=0) // { delete[] m_value; /*} */ } }
类析构时,判断m_count是否为0,如果为0的话就用for循环进行delete释放内存。
总结:
本人使用这个类进行一年多图像处理,没有遇到功能上的问题,大大方便了自己对图像数据的处理。以后本人在研究游戏时也将继续用到这个类。
下载地址:TwoDimesionArray.hpp
欢迎转载和链接,但转载时需保留原作者姓名,并注明出处。本文链接 http://www.cnblogs.com/xiangism/archive/2012/08/31/2665092.html