C++中句柄

 C++中句柄

一、句柄的概念

 

1.windows方面理解:

 

  • 句柄,是整个windows编程的基础。
  • 一个句柄是指使用的一个唯一的整数值,即一个四字节长的数值,来标志应用程序中的不同对象和同类对象中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。
  • 应用程序能够通过句柄访问相应的对象的信息,但是句柄不是一个指针,程序不能利用句柄来直接阅读文件中的信息。如果句柄不用在I/O文件中,它是毫无用处的。 
  • 句柄是windows用来标志应用程序中建立的或是使用的唯一整数,windows使用了大量的句柄来标志很多对象。

 

2.本人理解:

 

  • C++句柄是一种指向指针的指针。
  • C++句柄类的主要目的是在对象层面上实现多态。

二、实例详细说明

 
1.在visual studio 2010 创建控件器平台:
 
 
 
2.添加两个类,helloA,helloB
helloA.h
[cpp] view plain copy
 
  1. #pragma once  
  2. class helloA  
  3. {  
  4. public:  
  5.     helloA(void);  
  6.     ~helloA(void);  
  7.     virtual void func()const{printf("A");}  
  8. };  
helloB.h
[cpp] view plain copy
 
  1. #pragma once  
  2. #include "helloa.h"  
  3. class helloB :  
  4.     public helloA  
  5. {  
  6. public:  
  7.     helloB(void);  
  8.     ~helloB(void);  
  9.      void func()const{printf("B");}  
  10. };  
handel.cpp
[cpp] view plain copy
 
  1. #include "stdafx.h"  
  2. #include "helloA.h"  
  3. #include "helloB.h"  
  4. #include <vector>  
  5. using namespace std;  
  6.   
  7. int _tmain(int argc, _TCHAR* argv[])  
  8. {  
  9.     vector<helloA> vec;  
  10.     helloA a;  
  11.     helloB b;  
  12.     vec.push_back(a);  
  13.     vec.push_back(b);  
  14.       
  15.     for ( vector<helloA>::iterator iter=vec.begin();iter!=vec.end();iter++)  
  16.     {  
  17.         (*iter).func();  
  18.     }  
  19.   
  20.     return 0;  
  21. }  
运行的结果:
 
 
原因:vec.push_back(b);  ,其实把截断了,将b转化为了它的基类helloA.
 
如何解决这个问题,可以用指针代替对象方法:如:vector<helloA*>vec;这样做可以达到多态的目的,但程序员必须接管内存管理,例如,更改下handel.cpp代码:
 
[cpp] view plain copy
 
  1. vector<helloA*> vec;  
  2.     helloA *a = new helloA;  
  3.     helloB *b = new helloB;  
  4.     vec.push_back(a);  
  5.     vec.push_back(b);  
  6.   
  7.     for ( vector<helloA*>::iterator iter=vec.begin();iter!=vec.end();iter++)  
  8.     {  
  9.         (*iter)->func();  
  10.     }  

运行结果:
 
 
 
效果达到,但问题来了,这里假如vec的生命周期结束了,vec不会主动释放b所占用的内存,如果不手动delete b,就会产生内存泄漏。
 
接下来,就实现下句柄类,来解决如上问题。
 
更改下helloA,helloB的代码:
helloA.h
[cpp] view plain copy
 
  1. #pragma once  
  2. class helloA  
  3. {  
  4. public:  
  5.     helloA(void);  
  6.     ~helloA(void);  
  7.     virtual void func()const{printf("A");}  
  8.     virtual helloA* clone() const {return new helloA(*this);}  
  9. };  
helloB.h
[cpp] view plain copy
 
  1. #pragma once  
  2. #include "helloa.h"  
  3. class helloB :  
  4.     public helloA  
  5. {  
  6. public:  
  7.     helloB(void);  
  8.     ~helloB(void);  
  9.      void func()const{printf("B");}  
  10.      virtual helloB* clone() const{return new helloB(*this);}  
  11. };  
同时,添加sample类,记得把sample.cpp的代码注释掉,我们只在sample头文件更改代码即可。
sample.h
[cpp] view plain copy
 
  1. #pragma once  
  2. #include "helloA.h"  
  3. #include "helloB.h"  
  4. #include <stddef.h>  
  5. class sample  
  6. {  
  7. public:  
  8.   
  9.     sample():p(0),use(1){};  
  10.     sample(const helloA& a):p(a.clone()),use(1){};  
  11.     sample(const sample&i):p(i.p),use(i.use){use++;}  
  12.     ~sample(){decr_use();};  
  13.     sample& operator = (const sample& i)  
  14.     {  
  15.         use++;  
  16.         decr_use();  
  17.         p = i.p;  
  18.         use = i.use;  
  19.         return *this;  
  20.     }  
  21.     const helloA *operator->() const {if (p)return p;}  
  22.   
  23.     const helloA &operator*() const{if(p)return *p;}  
  24. private:  
  25.     helloA* p;  
  26.     size_t use;  
  27.     void decr_use(){if (-use == 0)delete p;}  
  28. };  
回到main函数,更改代码
handle.cpp
[cpp] view plain copy
 
  1. // handle.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include "helloA.h"  
  6. #include "helloB.h"  
  7. #include "sample.h"  
  8. #include <vector>  
  9. #include <list>  
  10. using namespace std;  
  11.   
  12.   
  13. int _tmain(int argc, _TCHAR* argv[])  
  14. {  
  15.   
  16.     vector<sample> vec;  
  17.     helloA a;  
  18.     helloB b;  
  19.   
  20.     sample sample1(a);  
  21.     sample sample2(b);  
  22.   
  23.     vec.push_back(sample1);  
  24.     vec.push_back(sample2);  
  25.   
  26.       
  27.     for ( vector<sample>::iterator iter=vec.begin();iter!=vec.end();iter++)  
  28.     {  
  29.         (*iter)->func();  
  30.     }  
  31.   
  32.     return 0;  
  33. }  
运行结果:
 
 
 
我们得到了正确的输出,如同shared_ptr,我们引入了计数,在vec生命周期结束时,我们不需要自己释放内存。
 
对上面代码需要注意,为什么克隆函数,要用virtual helloA* clone() const {return new A(*this);}而不是直接virtual A*clone() const {return this;}
 
因为,这样潜在一个问题,比如 
 
helloB b;
sample sam(b);
vec.push_back(sam);
 
当b的生命周期结束,而vec的生命周期尚未结束时,调用(*iter)->A.func();就会出错,而更直接的是,这样做与直接用指针进行多态没有太大区别了。
 
参考文章:http://blog.csdn.net/linuxtiger/article/details/6879366/
posted @   菜鸡一枚  阅读(12672)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
历史上的今天:
2015-08-20 Python|绝不乱入的靠谱书单
2015-08-20 当你念大学时,你需要忙些什么?
2015-08-20 Productivity tips, tricks and hacks for academics (2015 edition)
2015-08-20 如何画好科技论文中的插图
2015-08-20 怎样出色完成 1 分钟左右的英语自我介绍?
2015-08-20 matlab的rem()和mod()函数
点击右上角即可分享
微信分享提示