博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++ 沉思录——Chap6:句柄2
阅读量:4312 次
发布时间:2019-06-06

本文共 5609 字,大约阅读时间需要 18 分钟。

上一回讨论的句柄技术有一个明显的缺点:为了将句柄捆绑到类T的对象上,必须要新定义一个具有类型为T的成员对象的新类。
这个毛病相当麻烦,如果想新设计一个类的句柄类,就需要新定义两个类。
 
C++之父提到过一种定义句柄类的技术可以弥补这一个缺点,主要思想就是将引用技术从数据中分离出来,把引用计数放到句柄类自己的对象之中。
class Handle{     public:          // 和前面一样     private:          Point *p;          int *u;};
这里不再有指向UPoint的指针,我们使用指向Point的指针和指向一个int的指针表示引用计数。使用Point* 使得我们不仅能够将一个Handle绑定到一个Point,还能将其绑定到一个继承自Point 的类的对象。
 
此时我们的Handle类要在构造析构的时候要处理两个指针,比如:
Handle :: Handle() : u(new int(1)), p(new Point ) { } Handle :: Handle(int x, int y) : u( new int(1)), p(new Point(x, y)) { }// 需要正确的增加或减少引用计数Handle :: Handle(const Handle &h) : u(h.u), p(h.p) { ++*u; } Handle&  Handle :: operator = (const Handle &h){     ++*h.u;     if ( --*u == 0)     {          delete u;          delete p;          }     u = h.u;     p = h.p;     return *this;}
从这些实现可以看出,引用计数与Point类没有能够很好的协同,每次都需要单独处理它们。
 
如果想让引用计数和Point类更好的协同作用可以对引用计数进行抽象,用一个辅助类来实现引用计数。
 
class UseCount{     public:          UseCount();          UseCount(const UseCount &);          UseCount & operator = (const UseCount &);          ~UseCount();      private:          int *p;};
UseCount成员函数的实现就比较简单了:
UseCount :: UseCount() : p(new int(1)) {}UseCount :: UseCount(const UseCount &u) : p(u.p) { ++*p; }UseCount :: ~UseCount {  if ( --*p == 0) delete p;  }
 
现在重写Handle类:
class Handle{     public:          // 和之前一样     private:          UseCount u;          Point *p;};
 
现在我们来看成员函数的实现,就相对比之前简单了:
Handle :: Handle() : p(new Point) { }Handle :: Handle(int x, int y) : p(new Point(x,y)) { }Handle :: Handle(const Point & p0) : p(new Point(p0)) { }Handle :: Handle(const Handle & h) : u(h.u), p(h.p) { }

 

在写析构函数的时候,我们需要判断引用计数是否为0,以便知道要不要删除句柄的数据。
我们可以让UseCount 类来提供这个数据,通过一个类成员方法来藐视UseCount 对象是不是唯一指向它的计数器对象:
class UseCount{     public:          // 和前面一样     private:          bool only();}; bool UseCount :: only() {     return *p == 1; }
 
当UseCount 有了 only() 成员方法,Handle类的析构函数就可以这样写了:
 
Handle :: ~Handle(){     if( u.only() )          delete p;} 
当Handle类进行复制操作的时候,我们需要对引用计数值增1,或者减1,可能还要删除一个计数器。
但是现阶段我们设计的UseCount 类和Hanlde 类都不支持上面的操作。注意到我们引入UseCount类的原因是使引用计数的处理和Point 协同起来。因此我们将这些操作放在UseCount中来实现,可以增加下面的成员函数:
 
bool UseCount :: reattach( const UseCount & u){     ++*u.p;     if ( --*p == 0)     {          delete p;          p = u.p;          return true;     }     p = u.p;     return false;}

 

现在有了reattach() 成员方法之后,Handle类的赋值操作可以这样写:
Handle & Handle :: operator = (const Handle & h){     if(u.reattach(h.u))          delete p;     p = h.p;     return *this;} 
 
最后,如果我们要改变Point (注意前面的一切Handle、UseCount 类都是为了管理和控制Point类,我们真正需要操作的数据其实是Point 类 , 虽然都是在Handle类层进行操作以达到操作Point对象的目的),也就是对Point对象的单个元素进行读取和写入。当有多个句柄关联同一个Point对象时,我们对某个句柄操作需要改变该Point对象,但是其他句柄关联所持有的信息不需要改变,此时我们就需要对Point进行复制(记得前面引入Handle 的作用之一也就是避免不必要的复制,但是这里就需要进行复制了!)。
因此可以再UseCount类中在增加一个成员函数,用来对引用计数进行适当的控制。
bool UseCount :: makeonly(){     if ( *p == 1)          return false;// 这里的意思就是说,只有一个Handle关联该对象,因此可以直接进行改变,不需要复制对象     --*p;     p = new int (1);     return true;} 
 
Handle 类的存取函数可以这样写:
int Handle :: x() const{     return p->x();} Handle & Handle :: x(int x0){     if ( u.makeonly() )          p = new Point( *p);     p->x(x0);     return *this;}

 

现在列出全部代码:
/*为了将句柄捆绑到类T的对象上,必须要新定义一个具有类型为T的成员对象的新类。这个毛病相当麻烦,如果想新设计一个类的句柄类,就需要新定义两个类。C++之父提到过一种定义句柄类的技术可以弥补这一个缺点,主要思想就是将引用技术从数据中分离出来,把引用计数放到句柄类自己的对象之中。这个策略的一个重要优势:UseCount类可以在不了解其使用者任何信息的情况下与之合为一体。这样一来,我们就可以把这个类当成句柄实现的一部分,与各种不同的数据结构协同工作。 */#include 
using namespace std;//-----------------------------------------class Point{private: int xval,yval;public: Point():xval(0),yval(0){} Point(int x,int y):xval(x),yval(y){} int x()const{
return xval;} int y()const{
return yval;} Point& x(int xv){xval=xv;return *this;} Point& y(int yv){yval=yv;return *this;}};//------------------------------------------------------class UseCount{private: int* p;public: UseCount(); UseCount(const UseCount&); UseCount& operator=(const UseCount&); ~UseCount(); bool only(); bool reattach(const UseCount&); bool make_only();};UseCount::UseCount():p(new int(1)){}UseCount::UseCount(const UseCount&u):p(u.p){++*p;}UseCount::~UseCount(){ if (--*p==0) { delete p; }}bool UseCount::only(){ return *p==1;}bool UseCount::reattach(const UseCount& u){ ++*u.p; if (--*p==0) { delete p; p=u.p; return true; } p=u.p; return false;}bool UseCount::make_only(){ if (*p==1) return false; --*p; p=new int(1); return true;}//-------------------------------------------class Handle{private: Point* p; UseCount u;public: Handle(); Handle(int,int); Handle(const Point&); Handle(const Handle&); Handle& operator =(const Handle&); ~Handle(); int x()const; Handle&x(int); int y()const; Handle&y(int);};Handle::Handle():p(new Point){}Handle::Handle(int x,int y):p(new Point(x,y)){}Handle::Handle(const Point&p0):p(new Point(p0)){}Handle::Handle(const Handle&h):u(h.u),p(h.p){}Handle::~Handle(){ if (u.only()) { delete p; }}Handle& Handle::operator=(const Handle &h){ if (u.reattach(h.u)) delete p; p=h.p; return *this;}int Handle::x()const{ return p->x();}int Handle::y()const{ return p->y();}Handle& Handle::x(int x0){ if (u.make_only()) p=new Point(*p); p->x(x0); return *this;}Handle& Handle::y(int y0){ if (u.make_only()) p=new Point(*p); p->y(y0); return *this; }//---------------------------------------------------int main(){ Handle h(3,4); Handle h2 = h; h2.x(5); int n = h.x(); cout<
<

运行结果为:3

 
 

转载于:https://www.cnblogs.com/zhuyp1015/archive/2012/07/27/2612550.html

你可能感兴趣的文章
netfilter/iptables全攻略
查看>>
Overlay之VXLAN架构
查看>>
Eclipse : An error occurred while filtering resources(Maven错误提示)
查看>>
在eclipse上用tomcat部署项目404解决方案
查看>>
web.xml 配置中classpath: 与classpath*:的区别
查看>>
suse如何修改ssh端口为2222?
查看>>
详细理解“>/dev/null 2>&1”
查看>>
suse如何创建定时任务?
查看>>
suse搭建ftp服务器方法
查看>>
centos虚拟机设置共享文件夹并通过我的电脑访问[增加smbd端口修改]
查看>>
文件拷贝(IFileOperation::CopyItem)
查看>>
MapReduce的 Speculative Execution机制
查看>>
大数据学习之路------借助HDP SANDBOX开始学习
查看>>
Hadoop基础学习:基于Hortonworks HDP
查看>>
为什么linux安装程序 都要放到/usr/local目录下
查看>>
Hive安装前扫盲之Derby和Metastore
查看>>
永久修改PATH环境变量的几种办法
查看>>
大数据学习之HDP SANDBOX开始学习
查看>>
Hive Beeline使用
查看>>
Centos6安装图形界面(hdp不需要,hdp直接从github上下载数据即可)
查看>>