zoukankan      html  css  js  c++  java
  • C++ Handle(句柄) part1

    本文是我学习C++沉思录第6章的笔记

    本文主要讲述了Handle类的概念,定义方法以及写时复制技术。

    在前文(Surrogate代理类)的讲解中我们了解到了代理的实现方法.

    代理类有很多好处,但是麻烦的是每次都得进行复制.如果该类是经常使用并且member很多的话,这样复制的消耗是十分客观的.

    因此这里就要介绍另外一种代理类,Handle,也就是句柄类.

    为何使用句柄类?

    首先就是复制问题.前面有谈到,有些类内部数据很多,采用复制消耗非常大,这种情况下就必须采用句柄类来进行操作.

    其次是由于函数的参数和返回值都是采用了复制进行自动传递.虽然c++中引用可以避免,但是很多情况下返回值采用引用并不明智.

    对于采用指针的方式,可以解决问题,但是又会引入调用者对于动态管理内存的麻烦.而这往往是很多错误的根源.

    何为句柄类呢?

    句柄类可以理解为采用了引用计数的代理类.

    其多个句柄共享了同一个被代理的类.通过引用计数的方式来减少复制以及内存管理.

    其行为类似指针,因此也有智能指针之称,但其实差别很大.后面会有讲述.

    句柄类例子:

    先有一个简单的类Point

     1 class Point
    2 {/*{{{*/
    3 public:
    4 Point():_x(0),_y(0){}
    5 Point(int x,int y):_x(x),_y(y){}
    6 int x()const {return _x;}
    7 void x(int xv) { _x = xv;}
    8 int y()const { return _y;}
    9 void y(int yv) { _y = yv;}
    10 private:
    11 int _x;
    12 int _y;
    13 };/*}}}*/

    接下来我们要定义其的Handle类.

    我们的Handle类:

     1 class Handle
    2 {
    3 public:
    4 Handle():up(new UPoint){}
    5 Handle(int x,int y):up(new UPoint(x,y)){}
    6 Handle(const Point&p):up(new UPoint(p)){}
    7 Handle(const Handle &h);
    8 ~Handle();
    9 Handle& operator=(const Handle &h);
    10 int x() const{ return up->p.x(); }
    11 int y() const{ return up->p.y(); }
    12 Handle& x(int);
    13 Handle& y(int);
    14
    15
    16 private:
    17 UPoint *up;
    18 void allocup();
    19 };

    这里说明我们的Handle和指针的不同之处.

    也许有读者会对Handle有疑问,为什么不采用operator->来直接操作point呢?

    其实顾虑就是operator->返回的是point的地址.也就是使用者可以轻易的获得point的地址进行操作,这并不是我们想要的.这也就是Handle也pointer不想同的地方.

    UPoint是为了采用引用计数定义的数据结构

     1 //all member is private..only assess by Handle
    2 class UPoint
    3 {/*{{{*/
    4 friend class Handle;
    5
    6 Point p;
    7 int u;//count
    8
    9 UPoint():u(0){}
    10 UPoint(const Point&pv):p(pv){}
    11 UPoint(int x,int y):p(x,y),u(1){}
    12 UPoint(const UPoint &up):p(up.p),u(1){}
    13 };/*}}}*/

    对于Handle类的操作,我们要在Handle类进行复制的时候,累加Handle指向的UPoint的计数值

    即复制构造函数以及赋值函数

     1 Handle::Handle(const Handle &h)
    2 :up(h.up)
    3 {
    4 ++up->u;
    5 }
    6
    7 Handle& Handle::operator=(const Handle &h)
    8 {
    9 ++h.up->u;
    10 if (--up->u == 0)
    11 delete up;
    12 up = h.up;
    13 return *this;
    14 }

    而对于析构函数,则是减小引用计数,如果减到0了,就说明没有其他的Handle指向了UPoint,因此我们要删除掉.

    1 Handle::~Handle()
    2 {
    3 if (--up->u == 0)
    4 delete up;
    5 }

    剩下的就是定义Handle对于Point的操作了.即Handle::x(int xv)和Handle::(int yv)了.

    这里有2种写法.

    一种是像指针一样,对于赋值,就直接修改指向的Point里面的值.这种方法有一个问题,即所以都指向这个Point的Handle类获取的x值都会变化.

    代码:

     1 //point like
    2 Handle& Handle::x(int xv)
    3 {
    4 up->p.x(xv);
    5 return *this;
    6 }
    7 //point like
    8 Handle& Handle::y(int yv)
    9 {
    10 up->p.y(yv);
    11 return *this;
    12 }

    还有一种是写时复制技术,即每次对于共享的Point进行修改的时候都复制一份新的Point,然后进行修改.

    这种技术在Handle中大量采用.在stl中,string也采用了同样的方法.

    其额外开销很小,而效率也不差.

    代码:

     1 void Handle::allocup()
    2 {
    3 if (up->u != 1)
    4 {
    5 --up->u;
    6 up = new UPoint(up->p);
    7 }
    8 }
    9
    10 Handle& Handle::x(int xv)
    11 {
    12 allocup();
    13 up->p.x(xv);
    14 return *this;
    15 }
    16
    17 Handle& Handle::y(int yv)
    18 {
    19 allocup();
    20 up->p.y(yv);
    21 return *this;
    22 }


    至此,Handle类的第一部分就讲完了.

    之后会有第二部分的讲解.解决了多出了一个UPoint的麻烦.



  • 相关阅读:
    Linux基础命令—网卡
    SHOW SLAVE STATUS解读
    perf工具crash的问题
    python学习之-requests模块基础
    DELL IDRAC API接口开发文档翻译及client模块
    cobbler ks文件解释--转载
    django学习之- 动态验证码学习
    django学习之- Ajax
    django学习之- modelForm
    django学习之- json序列化
  • 原文地址:https://www.cnblogs.com/marchtea/p/2275534.html
Copyright © 2011-2022 走看看