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的麻烦.



  • 相关阅读:
    73. Set Matrix Zeroes
    289. Game of Live
    212. Word Search II
    79. Word Search
    142. Linked List Cycle II
    141. Linked List Cycle
    287. Find the Duplicate Number
    260. Single Number III
    137. Single Number II
    Oracle EBS中有关Form的触发器的执行顺序
  • 原文地址:https://www.cnblogs.com/marchtea/p/2275534.html
Copyright © 2011-2022 走看看