Chinaunix首页 | 论坛 | 博客
  • 博客访问: 16498705
  • 博文数量: 5645
  • 博客积分: 9880
  • 博客等级: 中将
  • 技术积分: 68081
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-28 13:35
文章分类

全部博文(5645)

文章存档

2008年(5645)

我的朋友

分类:

2008-04-28 21:00:06

下载本文示例代码
  前段时间,在查控件的内存泄露时,最终找出一个错误:在使用XMLDom(COM)时,由于重复使用某接口指针前未释放Dispatch指针(Release),而导致内存泄露,而此类错误(如同BSTR类型的泄漏),VC的调试器和Bondcheck均无能为力。解决办法,似乎只有细心一途。  但只要稍稍仔细看看,就可发现,实际上如果正确使用VC提供的智能指针,是可以避免此问题的。  另外,一直为Java程序员津津乐道的内存使用无需管理的优势,一直知道用C 的智能指针可以模拟。但一直没实际动手做过,趁此分析之机,用C 简单包装了一个。反正粗看之下,可以达到与Java类似的效果,当然,C 的对象更高效且节省内存。  就以上所提到的,时间关系,我只能简单罗列几点,代码应该是正确的(但未检查)。前后文没什么逻辑关系,但如果要进一步应用C 的智能指针,相信会起到抛砖引玉之效。   一:关于纠错,MFC和ATL中智能指针的应用  1:在Windows中如何方便的查看当前进程使用的内存。  虽然代码简单,但对纠错时有大用处,不用不停的通过切换任务管理器来查看内存使用。代码如下: UINT C_BaseUtil::getProcessMemoryUsed(){ UINT uiTotal = 0L; HANDLE hProcess = ::GetCurrentProcess(); PROCESS_MEMORY_COUNTERS pmc; if(::GetProcessMemoryInfo(hProcess,&pmc,sizeof(pmc)))  uiTotal = pmc.WorkingSetSize; return uiTotal;}  注意:由于内存使用会是一个不稳定的过程,所以,需要在程序稳定时进行调用,才能准确。  2:在使用Com的Dispatch指针时,如果不使用COM智能指针,容易出现的错误。  2.1:忘记在所有出口释放指针。     如: IXMLDOMDocument *pDoc = NULL;CoCreateInstance(...)……pDoc->Release();  错误:如果中间代码发生异常,则pDoc未能正常释放,造成内存泄露。  2.2:重复使用同一指针变量,导致中间生成的Dispatch指针未能释放。 IXMLDOMNode *pNode = NULL;if(FAILED(pDoc->selectSingleNode(_bstr_t("Workbook"), &pNode)) || pNode==NULL)throw(_T("selectSingleNode failed!"));if(FAILED(pDoc->selectSingleNode(_bstr_t("Workbook"), &pNode)) || pNode==NULL)throw(_T("selectSingleNode failed!"));  错误:pNode未释放就开始第二次调用,造成内存泄露。或者类似pNode = pNode2的这种写法,也随手就出问题了。必须调用if(pNode) {pNode->Release();pNode=NULL;}  3:使用MFC提供的Com智能指针解决上述问题。  注意:可通过查看源码,看到#import生成的智能指针的原型是_com_ptr_t。  3.1: IXMLDOMDocumentPtr docPtr = NULL;docPtr.CreateInstance(...)……  这下不会有问题了,因为docPtr在析构时会有正确的释放处理。  3.2: IXMLDOMNodePtr nodePtr = NULL;if(FAILED(pDoc->selectSingleNode(_bstr_t("Workbook"), &nodePtr)) || nodePtr==NULL)throw(_T("selectSingleNode failed!"));if(FAILED(pDoc->selectSingleNode(_bstr_t("Workbook"), &nodePtr)) || nodePtr==NULL)throw(_T("selectSingleNode failed!"));  不会出错了,因为_com_ptr_t重载了&操作符,在取指针时,有如下操作,嘿。 Interface** operator&() throw(){ _Release(); m_pInterface = NULL; return &m_pInterface;}  3.3: nodePtr = nodePrt2 ,也不会有问题:  仔细查看源码,在=操作符中会调用Attach,而Attach的做法是:会先调用_Release();  3.4:再看看值传递:拷贝构造函数如下 template<> _com_ptr_t(const _com_ptr_t& cp) throw(): m_pInterface(cp.m_pInterface){  _AddRef(); }  嗯,也不会有问题。  3.5:最后我们也总结一下使用COM智能指针时的注意事项:  ·不要在Com智能指针的生命期如果在::CoUninitailize之后,那请在调用::CoUninitailize之前,强制调用MyComPtr = NULL;达到强制释放的目的。否则会出错。  ·不要混用智能指针和普通Dispatch指针,不要调用MyComPtr->Release(),这违背智能指针的原意,会在析构时报错。  4:使用ATL提供智能指针:CComPtr或是CComQIPtr.  如果不使用MFC框架,要自已包装IDispatch,生成智能指针,还可以使用ATL提供的智能指针。查看源码,并参照《深入解析ATL》一书,发现实现与_com_ptr_t大同小异,效果一致。  二:引申一下,我们来看看C 的智能指针  1:说到智能指针,我们一定要看看标准C 提供的auto_ptr。而auto_ptr的使用是有很多限制的,我们一条一条来细数:  1.1:auto_ptr要求一个对象只能有一个拥有者,严禁一物二主。  比如以下用法是错误的。 classA *pA = new classA;auto_ptr ptr1(pA);auto_ptr ptr2(pA);  1.2:auto_ptr是不能以传值方式进行传递的。  因为所有权的转移,会导致传入的智能指针失去对指针的所有权。如果要传递,可以采用引用方式,利用const引用方式还可以避免程序内其它方式的所有权的转移。就其所有权转移的做法:可以查看auto_ptr的拷贝构造和=操作符的源码,此处略。  1.3:其它注意事项:  ·不支持数组。  ·注意其Release语意,它没有引用计数,与com提供的智能指针不同。Release是指释放出指针,即交出指针的所有权。  ·auto_ptr在拷贝构造和=操作符时的特珠含义决定它不能做为STL标准容器的成员,  好了,看了上面的注意事项,特别是第三条,基本上可以得出结论:在实际应用场合,auto_ptr基本没什么应用价值的。  2:如何得到支持容器的智能指针。  我们利用auto_ptr的原型,制作一个引用计数的智能指针,则时让它支持STL容器的标准。实现代码很简单,参照了《C 标准程序库》中的代码,关键代码如下: templateclass CountedPtr { T * ptr; long * counter; public:  //构造  explicit CountedPtr(T* p = NULL)  :ptr(p),count(new long(1){}  //析构  ~CountedPtr() {Release();}  //拷贝构造  CountedPtr(cont CountedPtr& p)  :ptr(p.ptr),count(p.count) { *counter;}  //=操作符  CountedPtr& operator= (const CountedPtr& p) {   if(this!=&p) {    Release();     ptr=p.ptr;    counter=p.counter; *counter;   }   return *this;  }  //其它略  .... private:  void Release() {   if(--*counter == 0) {    delete counter;    delete ptr;   }   }}  好了,这样,当复制智能指针时,原指针与新指针副本都是有效的,这样就可以应用于容器了。现在,通过CountedPtr包装的C 对象,是不是和Java的对象类似了呢,呵呵。只要再加上一些必要的操作符,它就可以作为容器中的共享资源来使用了。 编辑推荐:在VC 6.0中用Win32 API实现串行通信   前段时间,在查控件的内存泄露时,最终找出一个错误:在使用XMLDom(COM)时,由于重复使用某接口指针前未释放Dispatch指针(Release),而导致内存泄露,而此类错误(如同BSTR类型的泄漏),VC的调试器和Bondcheck均无能为力。解决办法,似乎只有细心一途。  但只要稍稍仔细看看,就可发现,实际上如果正确使用VC提供的智能指针,是可以避免此问题的。  另外,一直为Java程序员津津乐道的内存使用无需管理的优势,一直知道用C 的智能指针可以模拟。但一直没实际动手做过,趁此分析之机,用C 简单包装了一个。反正粗看之下,可以达到与Java类似的效果,当然,C 的对象更高效且节省内存。  就以上所提到的,时间关系,我只能简单罗列几点,代码应该是正确的(但未检查)。前后文没什么逻辑关系,但如果要进一步应用C 的智能指针,相信会起到抛砖引玉之效。   一:关于纠错,MFC和ATL中智能指针的应用  1:在Windows中如何方便的查看当前进程使用的内存。  虽然代码简单,但对纠错时有大用处,不用不停的通过切换任务管理器来查看内存使用。代码如下: UINT C_BaseUtil::getProcessMemoryUsed(){ UINT uiTotal = 0L; HANDLE hProcess = ::GetCurrentProcess(); PROCESS_MEMORY_COUNTERS pmc; if(::GetProcessMemoryInfo(hProcess,&pmc,sizeof(pmc)))  uiTotal = pmc.WorkingSetSize; return uiTotal;}  注意:由于内存使用会是一个不稳定的过程,所以,需要在程序稳定时进行调用,才能准确。  2:在使用Com的Dispatch指针时,如果不使用COM智能指针,容易出现的错误。  2.1:忘记在所有出口释放指针。     如: IXMLDOMDocument *pDoc = NULL;CoCreateInstance(...)……pDoc->Release();  错误:如果中间代码发生异常,则pDoc未能正常释放,造成内存泄露。  2.2:重复使用同一指针变量,导致中间生成的Dispatch指针未能释放。 IXMLDOMNode *pNode = NULL;if(FAILED(pDoc->selectSingleNode(_bstr_t("Workbook"), &pNode)) || pNode==NULL)throw(_T("selectSingleNode failed!"));if(FAILED(pDoc->selectSingleNode(_bstr_t("Workbook"), &pNode)) || pNode==NULL)throw(_T("selectSingleNode failed!"));  错误:pNode未释放就开始第二次调用,造成内存泄露。或者类似pNode = pNode2的这种写法,也随手就出问题了。必须调用if(pNode) {pNode->Release();pNode=NULL;}  3:使用MFC提供的Com智能指针解决上述问题。  注意:可通过查看源码,看到#import生成的智能指针的原型是_com_ptr_t。  3.1: IXMLDOMDocumentPtr docPtr = NULL;docPtr.CreateInstance(...)……  这下不会有问题了,因为docPtr在析构时会有正确的释放处理。  3.2: IXMLDOMNodePtr nodePtr = NULL;if(FAILED(pDoc->selectSingleNode(_bstr_t("Workbook"), &nodePtr)) || nodePtr==NULL)throw(_T("selectSingleNode failed!"));if(FAILED(pDoc->selectSingleNode(_bstr_t("Workbook"), &nodePtr)) || nodePtr==NULL)throw(_T("selectSingleNode failed!"));  不会出错了,因为_com_ptr_t重载了&操作符,在取指针时,有如下操作,嘿。 Interface** operator&() throw(){ _Release(); m_pInterface = NULL; return &m_pInterface;}  3.3: nodePtr = nodePrt2 ,也不会有问题:  仔细查看源码,在=操作符中会调用Attach,而Attach的做法是:会先调用_Release();  3.4:再看看值传递:拷贝构造函数如下 template<> _com_ptr_t(const _com_ptr_t& cp) throw(): m_pInterface(cp.m_pInterface){  _AddRef(); }  嗯,也不会有问题。  3.5:最后我们也总结一下使用COM智能指针时的注意事项:  ·不要在Com智能指针的生命期如果在::CoUninitailize之后,那请在调用::CoUninitailize之前,强制调用MyComPtr = NULL;达到强制释放的目的。否则会出错。  ·不要混用智能指针和普通Dispatch指针,不要调用MyComPtr->Release(),这违背智能指针的原意,会在析构时报错。  4:使用ATL提供智能指针:CComPtr或是CComQIPtr.  如果不使用MFC框架,要自已包装IDispatch,生成智能指针,还可以使用ATL提供的智能指针。查看源码,并参照《深入解析ATL》一书,发现实现与_com_ptr_t大同小异,效果一致。  二:引申一下,我们来看看C 的智能指针  1:说到智能指针,我们一定要看看标准C 提供的auto_ptr。而auto_ptr的使用是有很多限制的,我们一条一条来细数:  1.1:auto_ptr要求一个对象只能有一个拥有者,严禁一物二主。  比如以下用法是错误的。 classA *pA = new classA;auto_ptr ptr1(pA);auto_ptr ptr2(pA);  1.2:auto_ptr是不能以传值方式进行传递的。  因为所有权的转移,会导致传入的智能指针失去对指针的所有权。如果要传递,可以采用引用方式,利用const引用方式还可以避免程序内其它方式的所有权的转移。就其所有权转移的做法:可以查看auto_ptr的拷贝构造和=操作符的源码,此处略。  1.3:其它注意事项:  ·不支持数组。  ·注意其Release语意,它没有引用计数,与com提供的智能指针不同。Release是指释放出指针,即交出指针的所有权。  ·auto_ptr在拷贝构造和=操作符时的特珠含义决定它不能做为STL标准容器的成员,  好了,看了上面的注意事项,特别是第三条,基本上可以得出结论:在实际应用场合,auto_ptr基本没什么应用价值的。  2:如何得到支持容器的智能指针。  我们利用auto_ptr的原型,制作一个引用计数的智能指针,则时让它支持STL容器的标准。实现代码很简单,参照了《C 标准程序库》中的代码,关键代码如下: templateclass CountedPtr { T * ptr; long * counter; public:  //构造  explicit CountedPtr(T* p = NULL)  :ptr(p),count(new long(1){}  //析构  ~CountedPtr() {Release();}  //拷贝构造  CountedPtr(cont CountedPtr& p)  :ptr(p.ptr),count(p.count) { *counter;}  //=操作符  CountedPtr& operator= (const CountedPtr& p) {   if(this!=&p) {    Release();     ptr=p.ptr;    counter=p.counter; *counter;   }   return *this;  }  //其它略  .... private:  void Release() {   if(--*counter == 0) {    delete counter;    delete ptr;   }   }}  好了,这样,当复制智能指针时,原指针与新指针副本都是有效的,这样就可以应用于容器了。现在,通过CountedPtr包装的C 对象,是不是和Java的对象类似了呢,呵呵。只要再加上一些必要的操作符,它就可以作为容器中的共享资源来使用了。 编辑推荐:在VC 6.0中用Win32 API实现串行通信 下载本文示例代码


Visual C 及C 中的智能指针应用分析Visual C 及C 中的智能指针应用分析Visual C 及C 中的智能指针应用分析Visual C 及C 中的智能指针应用分析Visual C 及C 中的智能指针应用分析Visual C 及C 中的智能指针应用分析Visual C 及C 中的智能指针应用分析Visual C 及C 中的智能指针应用分析Visual C 及C 中的智能指针应用分析Visual C 及C 中的智能指针应用分析Visual C 及C 中的智能指针应用分析Visual C 及C 中的智能指针应用分析Visual C 及C 中的智能指针应用分析Visual C 及C 中的智能指针应用分析Visual C 及C 中的智能指针应用分析
阅读(193) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~