zoukankan      html  css  js  c++  java
  • 【C++沉思录】句柄1

    1、在【C++沉思录】代理类中,使用了代理类,存在问题:
    a、代理复制,每次创建一个副本,这个开销有可能很大
    b、有些对象不能轻易创建副本,比如文件
    2、怎么解决这个问题?
    使用引用计数句柄,对动态资源封装,句柄包含指针,多个句柄可以指向同一个对象。复制的时候,只是复制句柄的指针。
    3、使用引用计数句柄,是为了避免不必要的对象复制,因此我们要知道有多少个句柄绑定到当前对象,也就是引用计数,
    这样才能确定何时可以释放资源。
    4、需要注意的是:引用计数不能是句柄的一部分,如果怎么做,当前句柄必须知道指向同一个对象的其他句柄,引用计数也要保持一致。
    同时,引用计数不能成为对象的一部分,如果这样做,要求我们重写已经存在的对象类。
    5、以Point类为例说明,解决办法是:增加一个新的类UPoint,包含Point对象和引用计数u,如下:
    class Point
    {
    public:
    Point():_x(0),_y(0){}
    Point(int x,int y):_x(x),_y(y){}
    Point(const Point& rhs):_x(rhs._x),_y(rhs._y){}

    Point& SetX(int x)
    {
    _x = x;
    return *this;
    }

    int GetX()
    {
    return _x;
    }

    Point& SetY(int y)
    {
    _y = y;
    return *this;
    }

    int GetY()
    {
    return _y;
    }

    private:
    int _x;
    int _y;
    };

    #include "point.h"

    // UPoint的目的是对Point和引用计数封装,用户是不可见的
    class UPoint
    {
    friend class Handle_1;

    private:
    Point p;
    int u;

    UPoint():u(1){}

    UPoint(int x,int y)
    {
    p.SetX(x);
    p.SetY(y);
    u = 1;
    }

    UPoint(const Point& rhs)
    {
    p = rhs;
    u = 1;
    }
    };

    6、现在考虑Handle_1的实现细节,
    #include "u_point.h"

    class Handle_1
    {
    public:
    Handle_1():_up(new UPoint){}

    Handle_1(int x,int y):_up(new UPoint(x,y)){}

    Handle_1(const Point& rhs):_up(new UPoint(rhs)){}

    ~Handle_1()
    {
    subRef();
    }

    // copy构造,复制指针,增加引用
    Handle_1(const Handle_1& rhs)
    {
    addRef(rhs._up);
    }

    // copy赋值,左边减少引用计数,判断是否delete,右边增加引用计数,考虑自我赋值
    Handle_1& operator=(const Handle_1& rhs)
    {
    if(this != &rhs)
    {
    subRef();
    addRef(rhs._up);
    }

    return * this;
    }


    int GetX()
    {
    return _up->p.GetX();
    }

    int GetY()
    {
    return _up->p.GetY();
    }

    Handle_1& SetX(int x)
    {
    _up->p.SetX(x);
    return *this;
    }

    Handle_1& SetY(int y)
    {
    _up->p.SetY(y);
    return *this;
    }

    private:
    void addRef(UPoint* up) // 复制指针,增加引用
    {
    _up = up;
    ++_up->u;
    }

    void subRef() // 减少引用,判断是否delete
    {
    if(--_up->u == 0)
    {
    delete _up;
    }
    }

    private:
    UPoint* _up;
    };

    7、考虑下面的情况,
    Handle_1 h1(3,4);
    Handle_1 h2(h1);
    h2.SetX(5);
    int dd = h1.GetX();
    dd的值是5,也就是说,多个句柄指向同一个对象,避免了不必要的对象复制,实现的是 指针语义。但是对于上面的情况,往往不是用户所期望的,
    怎么解决这个问题?
    8、使用写时拷贝,每次修改的时候重新创建一个对象。也就是说,修改的时候变成值语义,原对象h1是不可变对象,使用h2修改,会导致重新创建一个对象。
    如下:
    Handle_1& SetX(int x)
    {
    //_up->p.SetX(x);
    if(_up->u == 1) // 当前是唯一的引用
    {
    _up->p.SetX(x);
    }
    else
    {
    --_up->u;
    _up = new UPoint(x,_up->p.GetY());
    }
    return *this;
    }

    Handle_1& SetY(int y)
    {
    //_up->p.SetY(y);
    if(_up->u == 1) // 当前是唯一的引用
    {
    _up->p.SetY(y);
    }
    else
    {
    --_up->u;
    _up = new UPoint(_up->p.GetX(),y);
    }
    return *this;
    }

  • 相关阅读:
    Fluent API
    什么是blazor
    10.事务
    9.用ExecuteSqlCommand执行存储过程
    8.自增主键 插入指定主键的数据
    7.图
    6.实体与上下文的关系
    5.并发
    4.跟踪
    3.级联删除
  • 原文地址:https://www.cnblogs.com/nzbbody/p/4678653.html
Copyright © 2011-2022 走看看