zoukankan      html  css  js  c++  java
  • 句柄类

      代理类,句柄类, 来自于C++ 沉思录的概念

      代理类让我们在一个容器中存储类型不同但相互关联的对象。这种方法需要为每个对象创建一个代理,并要将代理存储在容器中。创建代理将会复制所代理的对象,就像复制代理一样。
      但是,如果想避免这些复制该怎么做呢,一个叫句柄(handle)的类,它允许在保持代理的多态行为的同时,还可以避免进行不必要的复制。 

      一、问题的提出

      通常参数和返回值是通过复制传递的;

      只要几个指针指向同一个对象,就必须考虑在什么时候删除这个对象;

      在保持多态性的前提下避免复制对象;

      handle类的对象通常被绑定到它们所控制的类的对象上,这些handle的行为类似指针,所以人们有时也叫它们智能指针;

      将handle直接绑定到对象p上,则我们的handle最好与p的内存分配和释放无关;

    #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; }            //返回xval
        int y() const { return yval; }            //返回yval
        Point& x(int xv) { xval = xv; return *this; } //更改xval值返回对象
        Point& y(int yv) { yval = yv; return *this; } //更改yval值返回对象
    private:
        int xval, yval;
    };
    
    /* handle可以控制对Point副本的操作,
        是一种只包含单个Point对象的容器 
        */
    class Handle{    
    public:
        Handle(int x, int y){
            p = new Point(x,y);
        }
        Handle(Point& p){
            this->p = new Point(p.x(),p.y()); //创建一个p的副本
        }
        Point* operator->(){
            return p;
        }; //Point* add = h.operator->();
        ~Handle(){ delete p; }
    private:
        Point* p;
    };
    
    int main()
    {
        Point p(1,2);
        
        /* 将handle直接绑定到对象p上,则我们的handle与p的内存分配和释放无关 */
        Handle h0(123,456); //h0绑定到坐标123,456的Point
        Handle h(p); //h创建一个p的副本,并将handle绑定到该副本,控制对副本的操作
        
        cout << h->x() << endl; //使用 operator-> 将Handle的所有操作转发给相应的Point操作来执行
        cout << h.operator->()->y() << endl; //h.operator->() 返回值Point*
        
        cout << h0->x() << endl;
        cout << h0.operator->()->y() << endl;
        return 0;
    } 
    View Code

      二、简单的实现,必须明确地选择让我们的handle类支持哪些Point操作,避免使用operator->隐藏Point对象的真实地址

      通过无参构造,x,y参数构造,Point对象引用,Handle对象引用复制 (拷贝)构造,通过Handle对象对另一个Handle对象的赋值,使得多个Handle对象指向同一个Point对象,而不复制Point对象,Point对象在没有Handle对象指向的时候释放 

    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; return *this; }
        Point& y(int yv) { yval = yv; return *this; }
    private:
        int xval, yval;
    };
    
    //为了避免不必要的对象复制,得允许多个句柄绑定到单个对象上
    //我们必须定义一个新的类来容纳一个引用计数和一个Point对象,称之为UPoint
    
    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();
        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:   
        UPoint * up;//添加的
    };
    
    Handle::Handle():up(new UPoint) { }
    Handle::Handle(int x,int y):up(new UPoint(x,y)) { }
    Handle::Handle(const Point& p):up(new UPoint(p)) { }
    Handle::~Handle()
    {
        if(--up->u == 0)
            delete up;
    }
    Handle::Handle(const Handle& h):up(h.up) { ++up->u; }
    Handle& Handle::operator=(const Handle& h)
    {
        ++h.up->u;
        if(--up->u == 0)
            delete up;
        up = h.up;
        return *this;
    }
    
    int Handle::x() const { return up->p.x(); }
    int Handle::y() const { return up->p.y(); }
    
    //指针语义, 通过任一指向Point对象的Handle对象,可以修改Point对象的值
    Handle& Handle::x(int x0)
    {
        up->p.x(x0);
        return *this;
    }
    Handle& Handle::y(int y0)
    {
        up->p.y(y0);
        return *this;
    }
    View Code

      代码测试

    #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; return *this; }
        Point& y(int yv) { yval = yv; return *this; }
    private:
        int xval, yval;
    };
    
    //为了避免不必要的对象复制,得允许多个句柄绑定到单个对象上
    //我们必须定义一个新的类来容纳一个引用计数和一个Point对象,称之为UPoint
    
    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{
    /*private:
        class UPoint{ //可以设置为内部类,所有成员Handle均可见,外不可见
        public:    
            int u; 
            Point p;
            UPoint():u(1) { }
            UPoint(int x, int y): p(x,y), u(1) { }
            UPoint(const Point& p0):p(p0), u(1) { }
        }; */
    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);
        int UPoint_u(); //测试用
    private:   
        UPoint * up; 
    };
    
    Handle::Handle():up(new UPoint) { }
    Handle::Handle(int x,int y):up(new UPoint(x,y)) { }
    Handle::Handle(const Point& p):up(new UPoint(p)) { }
    Handle::~Handle()
    {
        if(--up->u == 0)  //原来的Point对象一旦没有Handle对象指, 即释放
            delete up;
        //cout << "~Handle()
    "; //测试用
    }
    Handle::Handle(const Handle& h):up(h.up) { ++up->u; }
    Handle& Handle::operator=(const Handle& h)
    {
        ++h.up->u;    // h指向的Point对象的指针数增一
        if(--up->u == 0) //Handle对象原来指向的Point对象的指针数减一
            delete up;   //原来的Point对象一旦没有Handle对象指, 即释放
        up = h.up;    //Handle对象的指针, 指向h指向的Point对象
        return *this;
    }
    
    int Handle::x() const { return up->p.x(); }
    int Handle::y() const { return up->p.y(); }
    
    //指针语义, 通过任一指向Point对象的Handle对象,可以修改Point对象的值
    Handle& Handle::x(int x0)
    {
        up->p.x(x0);
        return *this;
    }
    Handle& Handle::y(int y0)
    {
        up->p.y(y0);
        return *this;
    }
    
    //测试用
    int Handle::UPoint_u(){
        return up->u;
    }
    
    //多个Handle对象, 指向同一个Point对象p, 一样的坐标不一定是同一个对象
    int main()
    {
        Handle h1(123,456);
        Handle h2 = h1;
        Handle h3;
        h3 = h2;
        h3.y(789);    
        cout << h3.UPoint_u() << endl;
        cout << h2.y() << endl;
        Handle h4(123,789);
        cout << h4.UPoint_u() << endl;
        return 0;
    }
    View Code

      三、实现的改进引用计数与Point对象本身分离,在句柄中额外加上一些信息 int * u

      Handle类数据成员, 使用Point* ,不仅能够将一个Handle绑定到一个Point,还能将其绑定到一个继承自Point类的对象

    #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; return *this; }
        Point& y(int yv) { yval = yv; return *this; }
    private:
        int xval, yval;
    };
    
    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);
        int handle_u(){ return *u; } //测试用
    private:   
        Point* p; 
        int* u;
    };
    
    Handle::Handle():u(new int(1)), p(new Point) { }
    Handle::Handle(int x,int y):u(new int(1)), p(new Point(x,y)) { }
    Handle::Handle(const Point& p0):u(new int(1)), p(new Point(p0)) { }
    Handle::~Handle()
    {
        if(--*u == 0){ 
            delete u;
            delete p;
        }
    }
    Handle::Handle(const Handle& h):u(h.u), p(h.p) { ++*u; }
    Handle& Handle::operator=(const Handle& h)
    {
        ++*h.u;    // h指向的Point对象的指针数增一
        if(--*u == 0){ //Handle对象原来指向的Point对象的指针数减一
            delete u;   
            delete p; //原来的Point对象一旦没有Handle对象指, 即释放
        }
        u = h.u;
        p = h.p;
        return *this;
    }
    
    int Handle::x() const { return p->x(); }
    int Handle::y() const { return p->y(); }
    
    //指针语义, 通过任一指向Point对象的Handle对象,可以修改Point对象的值
    Handle& Handle::x(int x0)
    {
        p->x(x0);
        return *this;
    }
    Handle& Handle::y(int y0)
    {
        p->y(y0);
        return *this;
    }
    
    //多个Handle对象, 指向同一个Point对象
    int main()
    {
        Handle h1(123,456);
        Handle h2 = h1;
        Handle h3;
        h3 = h2;
        h3.y(789);    
        cout << h3.handle_u() << endl;
        cout << h2.y() << endl;
        Handle h4(123,789);
        cout << h4.handle_u() << endl;
        return 0;
    }
    View Code

      四、总结

      句柄通过绑定类的指针与绑定的类对象或其子类对象进行绑定,之后,通过句柄的计数器使多个句柄与之进行绑定只要有一个句柄指向被绑定对象,该对象都不会释放,不因其他绑定句柄改变指向或销毁而释放,保证了被绑定对象的安全,也保证了与之绑定的句柄都有具体的指向;绑定对象为动态构造,句柄的成员函数包装绑定类对象的类成员函数,使得句柄对象控制绑定对象的行为,与绑定对象行为相似

      句柄的实质,就是为了指针的安全

        //测试代码
        Handle h1(123,456);
        Handle h2(123,456); //与h1绑定的不是同一个对象
        {
            Point p(8,9); // p出了作用域销毁 
            Handle h3(p); //用p的引用创建绑定对象, h3出了作用域销毁
            h2 = h3;      //改变h2指向
            cout << h2.handle_u() << endl; //h2,h3绑定同一个对象
        }
        cout << h2.handle_u() << endl; //大括号作用域里绑定的对象依然存在
        cout << h2.x() << "," << h2.y() << endl;
    View Code

      五、句柄计数器的抽象

  • 相关阅读:
    新创建django项目,但网页打不开127.0.0.1:8000
    列表计算整数出现次数,并以次数大小重新排序
    Appium+python自动化
    笔试考试系统--学生管理加载和添加
    笔试考试系统--MVC实现登录
    笔试考试系统--配置EF;运行单元测试
    笔试考试系统--项目搭建及用Log4Net记录日志
    笔试考试系统--第一天需求分析及数据库设计
    笔试考试系统--引言
    不用加减乘除做加法,求2个数的平均数
  • 原文地址:https://www.cnblogs.com/GoldenEllipsis/p/13576178.html
Copyright © 2011-2022 走看看