c++ Handle类的理解(2)

来源:互联网 发布:ubuntu golang 安装 编辑:程序博客网 时间:2024/05/29 02:55
上一篇中, 阐明了代理类的产生和由来原因。一切都还可以,但是美中不足的是,每次都需要进行copy操作。这无疑是不好的,尤其是对于一切可能不能复制的情况,如数据库链接,打开的文件句柄,或者由于对象很大,复制会导致较大的开销。例子嘛,还是想到了书中的例子【其他例子也可以,不过这个例子毕竟简单,可能说服性也就不太好- -,理解思想就好】
    //Point类    class Point(){        private:            int m_x;            int m_y;        public:            Point():m_x(0),m_y(0){}             Point(int x,int y):m_x(x),m_y(y){}            //这里省略了拷贝构造函数和operator=的实现。因为默认的已经够用了            int x()const{                return m_x;            }            int y()const{                return m_y;            }            Point& x(int x){                m_x = x;                return *this;            }            Point& y(int y){                m_y = y;                return *this;            }    };
以上定义了一个Point类,你可以把它作用一个普通坐标上的点类来看待。这里没有什么继承层次,所以如果你看完了上篇,在看本篇时,思维还是需要稍微转变下【不在上一个基础上修改,是因为觉得代码量会比较大,偏离要表达的本意】思考:    我们考虑下,如果把本例子按照上一篇的思路来实现【可以把Point"想成“是一个有 pure virtual function的类以及还有一些子类】,当我们需要多态调用函数时,我们可能需要一个容器。这里假设直接是一个数组:并假设 PP为Point子类
    Point p[100];    p[0] = PP(5,10); //不要觉得疑惑,你如果看懂了上篇,这句话应该是可以理解的。
 而其中的过程是,PP(5,10)构造的匿名对象会调用copy方法,从而返回一个从堆上创建的副本并保存在p[0]中。【这里也算是对上篇的一个简单回顾】。 再次啰嗦下,本篇我们使用Handle就是为了不想有copy这样的对象复制发生!!!【哦,这么说其实吧,也容易误导人- - 】 看代码:     p[1] = p[0];     对于这样的代码,其中还是有了copy操作,我们可否省去这里的copy呢,答案是可以,我们可以共享对象。但是共享对象,什么时候该删除呢?a和b两个都引用到了同一个对象c,那么任何一个删除,如果另外一个还在用,是不是就会出问题了呢?。于是乎,解决方案出来了,引用计数!    两个或者多个对象可以共享同一个对象,每次有新的对象引用到已有的对象时,我们就把计数值+1,当有对象脱离时就把这个对象的引用计数-1,当引用计数-1 == 0 的时候,我们就删除这个对象!ok,思路理顺了,那么用代码实现看看,哦,别急。再想想,要对对象进行计数统计,我们应该把计数的放在哪里?放入Point类吗?是可以,但是这无疑是需要我们修改Point类的代码的【此处不要想着直接修改Point很容易,因为Point只是我们举得例子而已,可以想象成Point是一个已经完成的比较复杂的一个对象类!】,那么放入到我们的代理类中可以吗?想想也是不可以,为什么? 因为 代理类【Handle】类既然要共享,也就是多个Handle会绑定到同一个Point上,那么如果引用计数在Handle中,我们如何去跟踪那些引用到同一个Point的Handle对象呢?做不到这一点【或者复杂度太大】,那么引用计数自然也就不能得到正确的改变【引用计数设置为static的。- - ,别逗,100个Handle可能共享不止一个Point对象,可能前50个共享Point a,后50个共享Point b - -】。那么答案呼之欲出,我们需要一个”间接层”,这样子的话,上代码:
    class Handle;//Handle类前向声明    class UPoint{        private:            Point m_p;  //Point            int count; //引用计数变量        private:            friend class Handle;//设置Handle为UPoint类的友元            UPoint():count(1){}            UPoint(const Point& p):m_p(p),count(1){}            UPoint(int x,int y):m_p(new Point(x,y)),count(1){}    };
定义了UPoint类,作为“中间层”保存引用计数。
    class Handle{        private:            UPoint* m_up;        public:            Handle():m_up(new UPoint()){}            Handle(const Handle& h):m_up(h.m_up){++m_up->count;}//增加引用计数            Handle(int x,int y):m_up(new UPoint(x,y)){}            Handle& operator=(const Handle& h){                h.m_up->count++;                if(--m_up->count == 0){                    delete m_up;                }                m_up = h.m_up;                return *this;            }            ~Handle(){                if(--m_up->count == 0){                    delete m_up;                }        public:            int x()const{                return m_up->x();            }            int y()const{                return m_up->y();            }        //这两个有些特殊        /*           Handle& x(int x){               m_up->x(x);               return *this;           }           Handle& y(int y){               m_up->y(y);               return *this;           }       */    };
    暂时算告一段落了,但是还没有说完,最后注释起来的两个函数有点特殊【为什么呢?】,因为它进行了写入,由于我们知道多个Handle对象可以共享Point,那么:假设Handle a和Handle b两个对象共享一个Point:
    Handle h(5,5);    Handle y = h;    y.y(10);//  h.y() == ?   此时应该是5 还是10呢?

当然了按照注释的方式来写的话,是返回10 的,但是这大多数时候可能都不是我们想要的,我们希望平时它们好好共享,当有写入发生时,就成为独立的。于是乎,写时复制(copy on write)出现了。这个技术将在下篇文章中进行讲解。

p.s.以上代码都是在写博客的过程中,按照自己的思考来书写,没有在编译器中运行。如果思想上有什么理解错误或者偏差的地方,恳请提出,谢谢。

0 0