现在的位置: 首页 > 综合 > 正文

仿shared_ptr的二维智能数组指针TwoDimesionArray

2012年05月27日 ⁄ 综合 ⁄ 共 3992字 ⁄ 字号 评论关闭

由于本人前段时间一直在进行图像处理的研究,大家都知道图像是二维的,故在程序中经常会有二维数组的使用,而在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

抱歉!评论已关闭.