zoukankan      html  css  js  c++  java
  • C++ handle(句柄类) part2

    这是我最近学习C++沉思录所作的笔记和感想。

    本文主要讲解了第二种handle(句柄)的写法。

    在前文handle part1的部分我讲了一种handle的设计方法。但是那个handle有一个缺点,就是必须要分离出来一个UPoint类做为计数,十分的不方便。这意味着每个类都要单独的出来一个UPoint。而这个UPoint仅仅只是作为计数作用。

    在本文中,我们将要设计一种新的写法,利用UseCount类来为Handle所指的对象计数,同时减少UPoint类,同时简化Handle的写法。

    在前文中,我们知道了Handle类需要计数。通过计数和指针来完成减少复制,达到优化的目的。

    上文的写法:

     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 };/*}}}*/
    14
    15
    16 //all member is private..only assess by Handle
    17 class UPoint
    18 {/*{{{*/
    19 friend class Handle;
    20
    21 Point p;
    22 int u;//count
    23
    24 //省略
    25 };/*}}}*/
    26
    27 class Handle
    28 {
    29 public:
    30 //省略
    31
    32 private:
    33 UPoint *up;
    34 };

    可以看到,计数是由UPoint来实现的.由Handle来管理.

    但做为编写者的我们肯定是不愿意这么写的.这意味着每写一个Handle都得写一个这样的Uxxxx.实在是费事.

    因此,我们可以很直观的想到一个写法,即在Handle里面指向的是Point *p和int *count;

    这样就没有这个麻烦了.

    当然这是一个很好的解决方案.

    这里我再提出一种方案,即抽象引用计数,增加一个UseCount类.

    通过这个类来计数.其是通用的.所有的Handle都可以使用.其次,这可以减少我们的指针操作.

    实例如下:

    被Handle管理的类的声明:

     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 };/*}}}*/

    这个和原来的是一样的.

    再来看看我们的UseCount

     1 class UseCount
    2 {
    3 public:
    4 UseCount():count(new int(1)){}
    5 UseCount(const UseCount& uc):count(uc.count){ ++*count;}
    6 UseCount& operator=(const UseCount &u);
    7 ~UseCount();
    8 bool isonly() { return *count == 1;}//判断是否只指向一个计数,用于判断是否要删除
    9 bool reattach(const UseCount &u);//重新连接,用于复制
    10 bool makeonly();//分配一个新的,用于写时复制技术
    11 private:
    12 int *count;//计数
    13 };
    14
    15 UseCount& UseCount::operator=(const UseCount &u)
    16 {
    17 reattach(u);
    18 return *this;
    19 }
    20
    21 UseCount::~UseCount()
    22 {
    23 if (--*count == 0)
    24 delete count;
    25 }
    26 bool UseCount::reattach(const UseCount &u)
    27 {
    28
    29 ++*u.count;
    30 if (-- *u.count == 0)
    31 {
    32 delete count;
    33 count = u.count;
    34 return true;
    35 }
    36 count = u.count;
    37 return false;
    38 }
    39
    40 bool UseCount::makeonly()
    41 {
    42 if (*count == 1)
    43 return false;
    44 --*count;
    45 count = new int(1);
    46 return true;
    47 }

    这个类实现了计数的功能.其内部有一个count的int 指针.在进行UseCount的复制的时候仅仅是++use.

    其他的功能函数我也加了注释.大家应该能看懂.

    剩下的就是我们的使用UseCount的Handle类了.

     1 class Handle
    2 {
    3 public:
    4 Handle():p(new Point){}
    5 Handle(int x,int y):p(new Point(x,y)){}//使用了UseCount使得我们的构造函数显得异常简单,
    6 Handle(const Point&pp):p(new Point(pp)){}//仅仅是分配了Point的空间而已
    7 Handle(const Handle &h):p(h.p),count(h.count){};//复制构造函数也很简单.其实可以省略,也不会出错
    8 ~Handle();
    9 Handle& operator=(const Handle &h);
    10 int x() const{ return p->x(); }
    11 int y() const{ return p->y(); }
    12 Handle& x(int);
    13 Handle& y(int);
    14
    15
    16 private:
    17 Point *p;//被Handle的对象
    18 UseCount count;//使用UseCount
    19 };

    我们来看看各个函数

    析构函数:

    1 Handle::~Handle()
    2 {
    3 if (count.isonly())
    4 delete p;
    5
    6 }

    这个析构函数判断了p是否只指向了一个对象,如果是的话,因为要析构,所以p也要相应的被删除

    =操作符:

    1 Handle& Handle::operator=(const Handle &h)
    2 {
    3 if (count.reattach(h.count))
    4 delete p;
    5 p = h.p;
    6 return *this;
    7 }

    reattach函数返回bool指,如果count == 1的话,返回值为true,那就要把p给删除.

    采用了写时复制技术的x(int),y(int)

     1 Handle& Handle::x(int x0)
    2 {
    3 if (count.makeonly())
    4 p = new Point(*p);
    5 p->x(x0);
    6 return *this;
    7 }
    8
    9 Handle& Handle::y(int y0)
    10 {
    11 if (count.makeonly())
    12 p = new Point(*p);
    13 p->y(y0);
    14 return *this;
    15
    16 }

    makeonly()使UseCount分配一个新的计数值,返回true说明需要分配,false则代表其本身就只指向了一个对象,不需要重新分配.

    最后是咱们的测试main函数

     1 int main()
    2 {
    3 Handle h(10,10);
    4 cout << h.x() << " " << h.y() << endl;
    5 h.x(20);
    6 cout << h.x() << " " << h.y() << endl;
    7 Handle h2(h);
    8 cout << h2.x() << " " << h2.y() << endl;
    9 return 0;
    10 }

    总结:

          我们可以看到,通过抽象计数对象,我们的Handle的操作显得特别简单,减少了很多的指针操作.同时也减少了出错的可能.

          而通过这个方法,我们也减少了一个UPoint类的书写.简化了操作.

      Handle类有智能指针一说.其行为有点像指针.同时又省却了我们指针操作的麻烦.而且,减少了很多复制的操作.效率和代码的健壮性得到了提高.

           至此,handle(句柄类)的讲解就结束了.













  • 相关阅读:
    查看Oracle字符集
    Oracle备份还原表须知
    Oracle级联删除,删除用户的同时,将所分配到的表空间同时删除
    python logging模块(1) Marathon
    pycharm中调试django程序 Marathon
    sqlite数据表的增删改查操作 Marathon
    ubuntu 18.04配置固定ip Marathon
    django项目报错request请求不能处理问题 Marathon
    systemd.service配置 Marathon
    shell脚本学习之6小时搞定(1)入门 Marathon
  • 原文地址:https://www.cnblogs.com/marchtea/p/2279224.html
Copyright © 2011-2022 走看看