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


    由于本人前段时间一直在进行图像处理的研究,大家都知道图像是二维的,故在程序中经常会有二维数组的使用,而在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
  • 相关阅读:
    python 集合
    jQuery选择器
    hdu 5747 Aaronson
    hdu 2049 不容易系列之(4)——考新郎
    hdu 2048 神、上帝以及老天爷
    hdu 2045 不容易系列之(3)—— LELE的RPG难题
    hdu 2047 阿牛的EOF牛肉串
    hdu 2046 骨牌铺方格
    hdu 2050 折线分割平面
    hdu 2044 一只小蜜蜂
  • 原文地址:https://www.cnblogs.com/xiangism/p/2665092.html
Copyright © 2020-2023  润新知