Xiangism

从一个无知角落里开始,蹒跚学步,一个未知到另一个未知,在跌跌撞撞中越走越快,越走越远,最后宇宙也为之开源。对于探索者来说,最后他们的思想总是变得和自己的足迹一样伟大。
  博客园  :: 首页  :: 联系 :: 管理

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

Posted on 2012-08-31 11:31  Xiangism  阅读(2256)  评论(4编辑  收藏  举报

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

总结:

本人使用这个类进行一年多图像处理,没有遇到功能上的问题,大大方便了自己对图像数据的处理。以后本人在研究游戏时也将继续用到这个类。

下面是这个类的源码(现在将这个类名改为L2DPtr,使用方法和上面一样)

#ifndef LITENICE_2DPTR_HPP
#define LITENICE_2DPTR_HPP 1

#pragma once
#include <boost/shared_ptr.hpp>
#include <assert.h>

namespace ln
{
    //
    //  L2dPtr.h
    //
    //  (C) Copyright xiangism 2015
    //  Copyright (c) 2011-2015
    //
    //  智能二维数组
    //
    //  See http://www.cnblogs.com/xiangism
    //

    template<typename T>
    class L2dPtr
    {
    public:
        L2dPtr()
        {
            m_width = boost::shared_ptr<int>(new int(0));
            m_height = boost::shared_ptr<int>(new int(0));
            m_count = boost::shared_ptr<int>(new int(0));

            m_value = NULL;
        }

        L2dPtr(const int width, const int height)
        {
            m_width = boost::shared_ptr<int>(new int(width));
            m_height = boost::shared_ptr<int>(new int(height));
            m_count = boost::shared_ptr<int>(new int(1));

            m_value = new T[width*height];
        }

        L2dPtr(int width, int height, T *value)
        {
            m_width = boost::shared_ptr<int>(new int(width));
            m_height = boost::shared_ptr<int>(new int(height));
            m_count = boost::shared_ptr<int>(new int(2)); //TODO:注意这里是2,因为这里的数据是与外部共享的,所以这个类不应该销毁数据

            m_value = value;
        }

        L2dPtr(const L2dPtr &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);
            }
        }

        L2dPtr& operator = (const L2dPtr &r)
        {
            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;

            ++(*m_count);

            return *this;
        }

        ~L2dPtr(void)
        {
            dispose();
        }

        L2dPtr<T> DeepClone()
        {
            L2dPtr<T> clone(*m_width, *m_height);

            for (int j=0; j<*m_height; ++j) {
                for (int i=0; i<*m_width; ++i) {
                    clone.SetValue(i, j, GetValue(i, j));
                }            
            }

            return clone;
        }


        const T& GetValue(const int x, const int y) const
        {
            if (!IsInArray(x, y)) {
                printf("GetValue_const Not In Array: %d, %d\n", x, y);
                assert(IsInArray(x, y));
            }
            return m_value[GetIndex(x, y)];
        }


        T& GetValue(const int x, const int y)
        {
            if (!IsInArray(x, y)) {
                printf("GetValue Not In Array: %d, %d\n", x, y);
                assert(IsInArray(x, y));
            }
            
            return m_value[GetIndex(x, y)];
        }

        int GetHeight() const
        {
            return *m_height;
        }
        int GetWidth() const
        {
            return *m_width;
        }

        void SetValue(const int x, const int y, const T &val)
        {
            if (!IsInArray(x, y)) {
                printf("Not In Array: %d, %d\n", x, y);
                assert(IsInArray(x, y));
            }

            m_value[GetIndex(x, y)] = val;
        }

        void SetSize(const int width, const int height)
        {
            dispose();

            m_width = boost::shared_ptr<int>(new int(width));
            m_height = boost::shared_ptr<int>(new int(height));
            m_count = boost::shared_ptr<int>(new int(1));

            int size = width*height;
            if (size != 0) {
                m_value = new T[width*height];
            } else {
                m_value = NULL;
            }        
        }

        void SetAllValue(const T &value)
        {
            int sum = *m_width * *m_height;
            for (int i = 0; i<sum; ++i) {
                m_value[i] = value;
            }
        }

        bool IsInArray(int x, int y) const
        {
            if (x < 0 || x > *m_width-1 || y < 0 || y > *m_height-1) {
                return false;
            }
            return true;
        }

        bool IsEmpty() const
        {
            if (!m_width || !m_height) {
                return true;
            }
            if (*m_width == 0 || *m_height == 0) {
                return true;
            }
            return false;
        }

        T* GetBuffer()
        {
            return m_value;
        }

    private:

        void dispose()
        {
            if (*m_count == 0) {
                return ;
            }
            --(*m_count);

            if (*m_count == 0) {

                if (m_value) {
                    delete [] m_value;
                    m_value = NULL;
                }

            } // end if *m_count == 0
        } // end function dispose()

        int GetIndex(const int x, const int y) const
        {
            return y*(*m_width) + x;
        }


    private:
        boost::shared_ptr<int> m_count;
        boost::shared_ptr<int> m_width;
        boost::shared_ptr<int> m_height;

        T *m_value;

    };

} // end namespcae ln

#endif
View Code