C++ handle(句柄类) part2

摘要:
本文主要解释第二个句柄的编写。在handlepart1的前一部分中,我描述了句柄的设计方法。然而,该句柄有一个缺点,即不方便分离UPoint类进行计数。在本文中,我们将设计一种新的编写方法,使用UseCount类对Handle引用的对象进行计数,减少UPoint类,并简化Handle的编写。在上一篇文章中,我们了解到Handle类需要计数。通过计数和指针减少复制以实现优化。

这是我最近学习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(句柄类)的讲解就结束了.













免责声明:文章转载自《C++ handle(句柄类) part2》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇IntelliJ IDEA常用统一设置2-Inspections检查设置(Linux/Mac/Windows)Linux内核crash/Oops异常定位分析方法【转】下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

snmp学习笔记

snmp5.5 client 包含头文件 #include <net-snmp/net-snmp-config.h> #include <net-snmp/net-snmp-includes.h> 用到的函数有:初始化snmp库:void init_snmp(const char *); 用于初始化snmp库 netsnmp_ses...

C++ 宏和模板简介

参考《21天学通C++》第14章节,对C++中的宏和模板进行了学习,总结起来其主要内容如下: (1) 预处理器简介 (2) 关键字#define与宏 (3) 模板简介 (4) 如何编写函数模板和模板类 (5) 宏和模板之间的区别 (6) 使用static_assert进行编译阶段检查 **********************************...

Egg.js 实现向服务器上传图片

1.安装时间处理 及 压缩 模块 yarn add silly-datetime pump 2.文件保存路径 config/config.default.js config.uploadDir = 'app/public/avatar/upload'; 3.创建tools serviceapp/service/tools.js 'use strict';...

互动直播中的前端技术——即时通讯

前言 在疫情期间,上班族开启了远程办公,体验了各种远程办公软件。老师做起了主播,学生们感受到了被钉钉支配的恐惧,歌手们开启了在线演唱会,许多综艺节目也变成了在线直播。在这全民互动直播的时期,我们来聊聊互动直播中的即时通讯技术在前端中的使用。 即时通讯技术 即时通讯(Instant Messaging,简称IM)是一个实时通信系统,允许两人或多人使用网络实时...

PCRE函数简介和使用示例【转】

PCRE函数简介和使用示例 标签:正则表达式listbuffercompilationnullperl 原文地址:http://blog.csdn.net/sulliy/article/details/6247155 PCRE是一个NFA正则引擎,不然不能提供完全与Perl一致的正则语法功能。但它同时也实现了DFA,只是满足数学意义上的正则。 P...

前端上传数据-图片和视频格式校验

上一篇用 promise 嵌套实现了按 excel 行顺序上传数据,这篇要解决的问题是图片和视频格式校验,图片主要有 jpg png gif 视频 mp4 由于用户选择的资源可能并不是真正的多媒体文件,使用 js 的 file.type 方法获取的文件类型可能不准确,比如将 .xlsx 改为 .jpg, file.type 得到的类型是image/jpeg...