`
blogfeifei
  • 浏览: 1196126 次
文章分类
社区版块
存档分类
最新评论

理解句柄类

 
阅读更多

在代理类的帮助下,我们已经可以实现在一个容器里存储一个类层次里所有类型的对象,但是代理有一个很明显的缺点,就是需要复制对象,当一个对象非常大或者是一种不能轻易复制的资源的时候,这个实现遇到了很大的困难,于是我们有了句柄(handle)类这个技术。

我们有这么一个类

classpoint
{
public:
point():x_cdt(0),y_cdt(0){}
point(intx,inty):x_cdt(x),y_cdt(y){}
intget_x()
{

returnx_cdt;

}
intget_y()
{

returny_cdt;

}
point&set_x(intx)//返回值是point&是为了实现这样的表达//p.set_x(2).set_y(4);
{
x_cdt=x;
return*this;
}
point&set_y(inty)
{
y_cdt=y;
return*this;
}

private:
intx_cdt,y_cdt;
};

这个类有一切他应该有的成员函数,然后我们希望有这么一个handle能够替代实现他的所有功能,而又保证不需要复制这个对象,我们能想到的第一个方法当然是得到这个对象的指针但是这样我们就又把内存的分配暴露到了用户面前,可以随意的得到底层的指针。这个handle第一次是写成这样的:

class handle
{
public:
handle();
handle(intx,inty);
handle(const point&);
handle(const handle&);
handle& operator=(const handle&);
~handle();
private:
point*operator->();
}

我们暂时没有把set_x()这样的成员先假如到这里,为了简便。当我们写完之后,我们又遇到了时刻困扰我们的问题,就是对象的删除,既然我们允许把一个对象绑定到多个handle上,当其中的一个handle删除的时候,这个对象会怎么样呢?这个问题是要解决的。这里,我们呢引入了引用计数的概念,很显然,我们不能把这个引用计数放到我们的handle类中,因为一旦我们这么做了,当我们用一个handle绑定到一个对象的时候,他必须知道其他所有绑定到这个对象的handle的引用计数,然后更新这个计数,这非常困难。那么如果我们把这个引用计数放到对象本身呢?稍微思考一下就知道也是不合理的,这样做意味着我们每次都要改写对象。所以,综上考虑,我们决定写一个新的类来存放这个引用计数,这个类是完全为了技术的实现设计的,所以我们把他的所有成员设置成private,而让handle成为他的友元类,具体是这样的:

classcount_point
{
friendclasshandle;
point p;
intcount;
count_point();
count_point(intx,inty);
count_point(constcount_point&);
};

有了这个引用计数,我们就可以让我们之前的一个小忧虑变得合理点,就是我们在handle中有一个point*operator->();现在我们这样改写这个handle类:

classhandle
{
public:
handle();
handle(intx,inty);
handle(constpoint&);
handle(consthandle&);
handle&operator=(consthandle&);
~handle();
private:
count_point*ptr;
};

好了,至此,框架已经行程,我们来实现句柄。

classcount_point
{
friendclasshandle;
point p;
intcount;
count_point():count(1){}
count_point(intx,inty):p(x,y),count(1){}
count_point(constpoint&h):p(h),count(1){}

};

然后是handle类:

classhandle
{
public:
handle():ptr(newcount_point){}
handle(intx,inty):ptr(newcount_point(x,y)){}
handle(constpoint&h):ptr(newcount_point(h)){}
handle(consthandle&h):ptr(h.ptr){++ptr->count;}
handle&operator=(consthandle&h)
{
++h.ptr->count;
if(--ptr->count==0)
deleteptr;
ptr=h.ptr;
return*this;
}
~handle()
{
if(--ptr->count==0)
deleteptr;
}
private:
count_point*ptr;
};

最后,我们还有一个小任务,就是实现handle中的set_x()和set_y()成员,注意当初我们为了看上去简便暂时没有把他们添加到handle中,事实上他们是应该被包含的,我们来定义一个

handle::set_x(intx)
{
ptr->p.set_x(x);
return*this;
}

不得不提醒,这里涉及到指针语义和值语义的问题,我们这样做,是选择了指针语义,这样做的后果就是,你改动一个handle绑定的对象将使所有与这个对象绑定的handle发生一样的变化,这样可以避免复制。

二:

在我的前一篇博文里,我们讨论了有关句柄类的实现,这个句柄类有一个明显的缺陷,那就是必须知道你要代理的那个类的具体类型,如果是一个继承类的话,我们之前的实现就出问题了。这个问题就是

classcount_point
{
friendclasshandle;
point p;//这里

intcount;
count_point();
count_point(intx,inty);
count_point(constcount_point&);


};

现在如果我们把引用计数从count_point类中分离出来,也就是我们这样写handle类

classhandle
{
public:
handle();
handle(intx,inty);
handle(constpoint&);
handle(consthandle&);
handle&operator=(consthandle&);
~handle();
private:
point*ptr;
int*u;//指向引用计数

}

使用了这个point的指针后我们就可以把这个handle绑定到不管是一个基类还是他的派生类,这是动态绑定的优点。这里我们发现舍去了之前我们写的count_point类,看上去简洁了很多,以后我们会回头来写一个辅助类。好了,现在要做的就是实现handle类

handle::handle():ptr(newpoint),u(newint(1)){}

handle::handle(intx,inty):ptr(newpoint(x,y)),u(newint(1)){}

handle::handle(constpoint&h):ptr(newpoint(h)),u(){}

handle::handle(consthandle&h):ptr(h.p),u(h.u)
{
++*u;
}


handle::handle&operator=(consthandle&h)
{
++*h.u;
if(--*u==0){
deleteu;
deleteptr;
}
u=h.u;
ptr=h.ptr;
return*this;
}

现在我们希望最好我们能把引用计数也抽象一下,应该怎么抽象呢?最好的就是用类类实现,把int*这个计数放到一个类里,这个类具有必要的初始化函数,拷贝构造函数,赋值操作符,析构函数,于是我们构造了这样的一个类

classusecount
{
public:
usecount();
usecount(constusecount&);
usecount&operator=(constusecount&);
~usecount();
private:
int*p;
}

按照惯例,现在我们要做的就是实现它

usecount::usecount():p(newint(1)){}

usecount::usecount(constusecount&h):p(h.p)
{++*p;}

usecount::~usecount()
{
if(--*p==0)
deletep;
}

注意我们没有实现赋值操作符,等下会解释为什么这么做,现在我们可以改写handle类了

classhandle
{
public:
handle();
handle(intx,inty);
handle(constpoint&);
handle(consthandle&);
handle&operator=(consthandle&);
~handle();
private:
point*ptr;
usecount u;//改成这个
}

按照老习惯,接下来要实现这个类的成员函数

handle::handle():ptr(newpoint){}

handle::handle(intx,inty):ptr(newpoint(x,y)){}

handle::handle(constpoint&h):ptr(newpoint(h)){}

handle::handle(consthandle&):ptr(h.ptr),u(h.u){}

handle::~handle()
{
if(--u==0)
deleteptr;
}

恩,是时候交代一下我们为什么没有写operator=函数了,这个函数要做什么?他要对一个计数器加一,对另外一个减一,可能还要删除一个计数器,这些操作很可能改变引用计数,所以我们要在usecount类中添加一个额外的操作,我们命名为reattach,因为我们不定义operator=了,所以他应该在类中成为一个私有成员,现在看看reattach的实现

boolusecount::reattach(constusecount&h)
{
++*h.p;
if(--*p==0){
deletep;
p=h.p;
returntrue;
}
p=h.p;
returnfalse;

}

现在我们可以定义handle的operator=了


handle::operator=(consthandle&h)
{
if(u.reattach(h.u))
deleteptr;
ptr=h.ptr;
return*this;
}


分享到:
评论

相关推荐

    C++实现句柄类

    用C++实现的句柄类,有利于初学者理解什么是句柄~~

    MFC 句柄的理解

    windows 编程中MFC 句柄的理解,形象的说明了句柄的作用

    C++指针及句柄

    C++指针及句柄,深入理解 指针的奥秘 .

    列举顶层窗口句柄及标题名

    列举桌面顶层窗口的句柄和标题,代码比较简单,也比较容易理解,只是适合新手理解一些关于窗口句柄的函数。

    Linux文件句柄限制总结

    网上说什么的也有,你抄我的我抄你的,也是醉了,故自己综合查阅的资料,根据自己的理解和判断以及部分的实践整理下吧,也不敢保证都是对的,如果有比较大的错误,希望看到这篇文章的你提出来,大家共同进步!...

    句柄-进程通信-windows消息机制-看门狗

    跨进程通信,利用句柄,可以用来理解windows消息机制,两个程序间通信,高效通信。功能是将程序1TextBox1.Text传递给程序2的label。程序2的TextBox1.Text传递给程序1的lable. 不在需要内存共享等。可以用来做看门狗...

    iOS App使用SQLite之句柄的定义及数据库的基本操作

    要操纵一个数据库你就得有一个这个数据库的句柄(又碰到这个难以理解的词了,不过确实还没得一个更好的词来替代它)。其实你跟本不需要去在乎这个词叫什么,你只要搞清楚他是一个什么玩意儿。就如同鞋子为什么叫鞋子,...

    《对话框》之《MFC和Win32》

    MFC中最重要的封装是对Win32 API的封装,因此,理解Windows Object和MFC Object (C++对象,一个C++类的实例)之间的关系是理解MFC的关键之一。所谓Windows Object(Windows对象)是Win32下用句柄表示的Windows操作...

    对String的深入理解

    语句声明一个类A的引用变量aa[我常常称之为句柄],而对象一般通过new创建。所以题目中s仅仅是一个引用变量,它不是对象。[ref 句柄、引用与对象] 二、Java中所有的字符串文字[字符串常量]都是一个String的对象。有...

    VC++ 6.0非模式对话框示例以及对比Java的Swing编程的感想

    Windows的自定义消息句柄处理过程非常类似于Swing编程中Frame类与监听器的关系,只不过Java的虚拟机隐藏了底层系统的细节部分。这使得本人在学习了VC++的非模式对话框编程之后,更加理解了Java中Swing编程的原理--...

    isomorphic-loader:Webpack同构加载器工具,使节点需要句柄文件(如图像)进行服务器端渲染(SSR)

    使用此模块,您可以扩展require以便它理解这些文件。 它包含三个部分: 一个webpack加载器-标记资产文件 一个webpack插件-收集资产文件并生成映射数据 使Node.js库-延伸require使用所述映射数据

    C++ Primer第四版【中文高清扫描版】.pdf

    16.5 一个泛型句柄类 560 16.5.1 定义句柄类 561 16.5.2 使用句柄 562 16.6 模板特化 564 16.6.1 函数模板的特化 565 16.6.2 类模板的特化 567 16.6.3 特化成员而不特化类 569 16.6.4 类模板的部分特化 570 16.7 ...

    VC++6.0MFC入门学习

    2、理解Windows的消息机制,窗口句柄和其他GUI句柄的含义和用途。了解和MFC各个类功能相近的API函数。 3、一定要理解MFC中消息映射的作用。 4、训练自己在编写代码时不使用参考书而是使用Help Online。 5、记住一些...

    Visual C++ 2005入门经典.part08.rar (整理并添加所有书签)

    6.8.1 理解类函数 6.8.2 CLR版本的计算器程序 6.9 小结 6.10 练习 第7章 自定义数据类型 7.1 C++中的结构 7.1.1 结构的概念 7.1.2 定义结构 7.1.3 初始化结构 7.1.4 访问结构的成员 7.1.5 伴随结构的智能帮助 7.1.6 ...

    Visual C++ 2005入门经典.part04.rar (整理并添加所有书签)

    6.8.1 理解类函数 6.8.2 CLR版本的计算器程序 6.9 小结 6.10 练习 第7章 自定义数据类型 7.1 C++中的结构 7.1.1 结构的概念 7.1.2 定义结构 7.1.3 初始化结构 7.1.4 访问结构的成员 7.1.5 伴随结构的智能帮助 7.1.6 ...

    Visual C++ 2005入门经典.part07.rar (整理并添加所有书签)

    6.8.1 理解类函数 6.8.2 CLR版本的计算器程序 6.9 小结 6.10 练习 第7章 自定义数据类型 7.1 C++中的结构 7.1.1 结构的概念 7.1.2 定义结构 7.1.3 初始化结构 7.1.4 访问结构的成员 7.1.5 伴随结构的智能帮助 7.1.6 ...

    Visual C++ 2005入门经典.part09.rar (整理并添加所有书签)

    6.8.1 理解类函数 6.8.2 CLR版本的计算器程序 6.9 小结 6.10 练习 第7章 自定义数据类型 7.1 C++中的结构 7.1.1 结构的概念 7.1.2 定义结构 7.1.3 初始化结构 7.1.4 访问结构的成员 7.1.5 伴随结构的智能帮助 7.1.6 ...

Global site tag (gtag.js) - Google Analytics