zoukankan      html  css  js  c++  java
  • C++ 沉思录——Chap6:句柄

         第五章介绍了代理类,这个类能让我们在一个容器中存储类型不同但相互关联的对象。这种方法需要为每个对象创建一个代理,并要将代理存储在容器中。创建代理将会复制所代理的对象。

         如果想避免这些复制该怎么做呢?可以使用句柄类。它允许在保持代理的多态行为的同时,还可以避免进行不必要的复制。
         处于多态的环境中,我们可以知道对象的基类类型,但是不知道对象本身的类型或者怎么样复制这种类型的对象。
         如果多个指针指向同一个对象,就必须考虑要在什么时候删除对象。不能太早也不能太晚,太早删除,就会有某个仍然指向它的指针存在,再使用这个指针就会产生未定义行为。删除得太晚又会占用本来早该另作它用的空间。
         需要一种方法,让我们避免某些缺点的同时又能够获取指针的某些优点,尤其是在能够保持多态性的前提下避免复制对象的代价。C++的解决方法就是定义一个适当的类。由于这些类的对象通常被绑定到它们所控制的对象上,所以这些类常被称为句柄类(handle class)。
         
    假定有这样一个类:
    class Point
    {
    private:
         int xval, yval;
    public:
         Point() : xval(0), yval(0) { }
         Point(int x, int y) : xval(x), yval(y) { }
         int x() const { return xval;}
         int y() const { return yval;}
         Point& x(int xv)
         {
              xval = xv;
              return *this;
         }
         Point& y(int yv)
         {
              yval = yv;
              return *this;
         }
    };
         handle 应该“控制”它所绑定的对象,也就是说handle应该创建和销毁对象。有两种方式:可以创建自己的Point对象并把它赋给一个handle去进行复制,或者可以把用于创建Point的参数传给这个handle。我们要允许这两种方法,所以想让handle类的构造函数和Point类的构造函数一样。也就是,我们想用:
     Handle h0(123,456);
    来创建绑定到新分配的坐标为123和456的Point的handle,而用:
    Handle h(p);
    创建副本,并将handle绑定到该副本。这样,handle就可以控制对副本的操作。从效果上说,handle 就是一种包含单个对象的容器。
     
         当handle绑定到Point类之后,可以对 “->”进行重载,使用operator-> 将handle的所有操作转发给相应的Point操作来执行。但是这种操作也会过于暴露Point的操作,如果想绕开operator->(),就必须为handle提供自己的x和y操作,这两个操作要么返回int,要么返回Handle&。
     
         根据上面的分析给出Handle 类的大致轮廓:
        
     class Handle
         {
              public:
                   Handle();
                   Handle(int, int);
                   Handle(const Point &);
                   Handle(const Handle &);
                   Handle & operator=(const Handle &);
                   ~Handle();
     
                   int x() const;
                   Handle & x(int );
                   int y()  const;
                   Handle & y(int );
              private:
                   // ..
         }; 
    使用句柄原因之一就是为了避免不必要的对象复制,也就是说允许多个句柄绑定到单个对象上。我们必须了解有多少个句柄绑定在同一个对象上,只有这样才能确定应当何时删除对象。而通常使用引用计数来达到这个目的。
     
    但是,这个引用计数不能是句柄的一部分,否则句柄的设计会相当麻烦。也不能让引用计数成为对象的一部分,因为那样要求我们重写已经存在的对象类。我们必须定义一个新的类来容纳一个引用计数和一个Point对象。我们成为UPoint。这个类纯粹是为了实现而设计的,所以我们把其所有成员都设置为private,并且将我们的句柄类声明为友元。我们希望能以创建Point的全部方式创建UPoint对象,所以:
         class UPoint
         {
              friend class Handle;
              Point P;
              int     u;
              
              UPoint() : u(1) { } // 引用计数初始化为1
              UPoint(int x , int y) : P(x, y), u(1) { }
              UPoint(const Point & p0) : P(p0), u(1) { }
         } 
    现在可以完善Handle类了          
    class Handle
    {
    private:
         UPoint *up;    //和间接层UPoint打交道了
    public:
         Handle();
         Handle(int, int);
         Handle(const Point&);
         Handle(const Handle&);
         Handle& operator=(const Handle&);
         ~Handle();
         int x() const;
         Handle& x(int);
         int y() const;
         Handle& y(int);
    }; 
    现在来列出全部代码:
    /*
    从效果上来说,handle就是一种只包含单个对象的容器,它通过允许多个handle
    对象指向同一个对象来避免复制。定义句柄类,我们还需要新定义一个类来容
    纳被引用类对象的引用计数和被引用类对象本身。
    
    这里的引用计数为0时删除p的意思应该是由handle创建的p
    handle的构造函数中有一种是跟p有关的,在这类构造函数中
    创建了p并初始化了p的计数,当这个计数为0时删除的是由handle创建
    的p,如果Point自身不实例化对象的话,这样就真的实现了删除p对象了
    
    可见,handle需要提供hanle(const handle &) , hanle(const Point &)和hanle(int x, int y)
    三类构造函数的必要性,都是为了创建Point对象,而且这样创建的Point对象可以与handle关联
    
    */
    #include <iostream>
    using namespace std;
    
    class Point{
    public:
        Point() : xval(0),yval(0){};
        Point(int x, int y): xval(x), yval(y){};
        int x() const {return xval;};
        int y() const {return yval;};
        Point& x(int xv) 
        { 
            xval = xv; y
            return *this;
        };
        Point& y(int yv) 
        { 
            yval = yv; 
            return *this;
        };
    private:
        int xval, yval;
    };
    
    class UPoint{                            //引用计数类
        friend class Handle;
        Point p;
        int u;                                   //引用计数变量
        UPoint(): u(1){};
        UPoint(int x, int y): p(x,y), u(1){};
        UPoint(const Point& p0): p(p0),u(1){};
    };
    
    class Handle{                       //句柄类
    public:
        Handle(): up(new UPoint){};
        Handle(int x,int y): up(new UPoint(x,y)){};//按创建Point的方式构造handle,handle->UPoint->Point
        Handle(const Point& p): up(new UPoint(p)){};//创建Point的副本
        Handle(const Handle& h): up(h.up){ ++up->u;};//此处复制的是handle,但是底层的point对象并未复制,只是引用计数加1
        Handle& operator=(const Handle& h)
        {
            ++h.up->u;                //右边的对象引用计数加1,左边的减1
            if(--up->u == 0)
                delete up;
            up = h.up;
            return *this;
        };
        ~Handle()
        {
            if(--up->u == 0)
            delete up;
        };
        int x() const{return up->p.x();};
        Handle& x(int xv)
        {
            up->p.x(xv); 
            return *this;
        };
    
        int y() const{return up->p.y();};
    
        Handle& y(int yv)
        {
            up->p.y(yv); 
            return *this;
        };
        int OutputU(){return up->u;};   //输出引用个数
    private:
        UPoint* up;
    };
    
    int main()
    {
        //Point *p = new Point(8,9); 
        Point p(8,9);
        //Point p1 = p.x(88);
        //Point *pp = &p;
        Handle h1(1,2);
        Handle h2 = h1;        //此处调用的是构造函数Handle(const Handle& h)
        h2.x(3).y(4);               //此处的特殊写法是因为写xy函数内返回了对象
        Handle h3(5,6);        //此处调用Handle的赋值运算符重载函数Handle& operator=(const Handle& h)
        h1 = h3;
        Handle h4(p);
        Handle h5(h4);
        h4.x(7).y(8);
        //Handle h5(p1);
        //Handle h5 = h4;
        cout <<"h1(" << h1.x() <<":"<< h1.y() << "):" << h1.OutputU() <<endl;
        cout <<"h2(" << h2.x() <<":"<< h2.y() << "):" << h2.OutputU() <<endl;
        cout <<"h3(" << h3.x() <<":"<< h3.y() << "):" << h3.OutputU() <<endl;
        cout <<"h4(" << h4.x() <<":"<< h4.y() << "):" << h4.OutputU() <<endl;
        cout <<"h5(" << h5.x() <<":"<< h5.y() << "):" << h5.OutputU() <<endl;
        //delete pp; //不能这样,不是用new分配的空间
        //cout <<"h5(" << h5.x() <<":"<< h5.y() << "):" << h5.OutputU() <<endl;
        cout<<p.x()<<" "<<p.y()<<endl;
        //cout<<&p1<<endl;
        return 0;
    }
    运行结果:

  • 相关阅读:
    2015上海网络赛 A Puzzled Elena
    容斥原理——uva 10325 The Lottery
    2015北京网络赛B题 Mission Impossible 6
    2015北京网络赛A题The Cats' Feeding Spots
    POJ3087——map——Shuffle'm Up
    POJ3126——BFS——Prime Path
    POJ1426——BFS——Find The Multiple
    算法总结——Prim
    算法总结——Dijkstra
    算法总结——Floyed
  • 原文地址:https://www.cnblogs.com/zhuyp1015/p/2610847.html
Copyright © 2011-2022 走看看