智能指针的几点错误操作心得

   ADO操作经常使用到智能指针,它是利用ATL技术对一些常用结构体或者数据类型的封装(例如_Command、_Connection、VARIANT、BSTR),这些结构体或数据类型涉及到内存的开辟和释放操作,智能指针对这些操作进行了封装,方便程序员的使用。但是方便使用的同时,也会造成一些不易察觉的错误。昨天在调试过程中为了查明内存泄露的原因,几经周折才解决了这些问题,获得几点心得与大家分享。

   首先介绍一下智能指针的内存方面有关的函数和操作。
   智能指针中的Attach和Detach是相对应的函数。这两个函数分别代表分离和捆绑操作。值得注意的是,Detach函数不会进行内存释放操作,它只会返回捆绑的结构体类型的指针。
   智能指针中会自动进行内存释放操作的函数和操作有:Release函数、析构函数和“=”操作。
   
   好了,现在来看下面一段代码,看看你能找出几处错误:
void function()
{
   _ParameterPtr pParameter;
   _variant_t    vtParameterValue;
   _variant_t    vtReturn;
   _CommandPtr   pCommand;
   HRESULT       hResult;
   _bstr_t          bstrStringValue;
   _bstr_t          bstrCollumName;

   hResult 
= pCommand.CreateInstance(__uuidof(Command));
   
if (FAILED(hResult))
{
       
   }


   vtParameterValue.vt 
= VT_BSTR;
   bstrStringValue 
= _com_util::ConvertStringToBSTR("Text");
   vtParameterValue.bstrVal 
= bstrStringValue;
   bstrCollumName 
= L"@strTextValue";
   pParameter 
= pCommand->CreateParameter(bstrCollumName,adVarChar,adParamInput, 10,vtParameterValue);
   pCommand
->Parameters->Append(pParameter);

   

   
// 调用存储过程
   
pCommand->CommandType = adCmdStoredProc;
   pCommand
->Execute(NULL, NULL, adCmdStoredProc); 

   pParameter.Release();
   pCommand.Release();
}
   
   这是一个调用存储过程的函数代码,使用了智能指针_ParameterPtr, _variant_t, _CommandPtr, _bstr_t,其中存储过程包含一个varchar(10)型的名叫"@strTextValue"的参数,在ADO编程中,这样的代码很常见。如果告诉你这段代码会导致内存泄露并且运行时会导致错误,你可以找出这样的错误吗?

   下面公布我的发现:

   一、关于内存泄露
   注意这行代码:“bstrStringValue = _com_util::ConvertStringToBSTR("Text"); ”   这里,函数ConvertStringToBSTR的功能是转换char*型字符串为BSTR型,内部实际上开辟了一块双字节的字符串的空间来存放这串双字节的字符串。然后执行_bstr_t型对象bstrStringValue的“=”号操作,“=”操作也涉及了内存开辟,它会开辟和ConvertStringToBSTR产生的一样大的一块内存来拷贝并存放这个BSTR型的字符串。_bstr_t的“=”操作开辟的内存在析构的时候会被释放掉,但是ConvertStringToBSTR函数产生的那块内存呢?自生自灭,造成内存泄露了。
   解决方法,将代码“bstrStringValue = _com_util::ConvertStringToBSTR("Text"); ”改成:“bstrStringValue = "Text"”。由于_bstr_t型的“=”号操作有这样的重载类型: operate=(char*)。它会将单字节内部转化为双字节并存储。因此大可不必进行额外的转换工作,同时内存泄露的问题也避免了。
   PS:函数结尾的两处Release操作其实也大可不必,因为智能指针在析构的时候自动释放开辟的内存。写上这两行代码只是为了编程规范,有开就有关嘛。

   二、关于运行错误
   该函数结束时必将导致错误。而这个错误也是由于智能指针析构时会释放内存导致的。
   注意这两行代码:“bstrStringValue = _com_util::ConvertStringToBSTR("Text");  vtParameterValue.bstrVal = bstrStringValue;”抛开内存泄露的问题到一边,这里分析运行错误的原因。这两行代码的作用是首先bstrStringValue开辟BSTR的字符串空间,然后将该字符串的指针附给vtParameterValue.bstrVal,注意,这里只是指针哦,因为vtParameterValue虽然是智能指针,“=”号会开辟内存,但是vtParameterValue.bstrVal并不是智能指针,它的“=”操作只是简单的指针复制。函数结束的时候两个智能指针都会进行内存释放,不论这两个智能指针析构的顺序,BSTR型的字符串空间都会被释放两次,一个内存空间被释放两次,当然就会发生运行时错误了。
   解决方法,利用Detach函数,当发现vtParameter的类型是VT_BSTR时,调用Detach函数来分离BSTR字符串,这样可以保证不对BSTR字符串释放,而这个字符串的释放工作交给_bstr_t的析构函数来做就行了。

   最后发一下感叹,智能指针是一把双刃剑,给程序员带来便利的同时,也存在着很大的风险,使用时需谨慎。


posted on 2008-01-23 16:30 迷宫の未来 阅读(3257) 评论(3)  编辑 收藏 引用

评论

# re: 智能指针的几点错误操作心得 2008-01-23 17:12 free2000fly

建议精读 <<COM 本质论>>, 就不会出现这些错误了   回复  更多评论   

# re: 智能指针的几点错误操作心得 2008-01-24 18:04 Rayz

_com_util::ConvertStringToBSTR("Text");

会造成泄露  回复  更多评论   

# re: 智能指针的几点错误操作心得 2008-01-30 09:52 追梦时代

@free2000fly
并不完全赞同楼上的意见,基本原理其实我们都懂,但理论和实践毕竟是两码事,实践才会加深对知识的理解。  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2008年1月>
303112345
6789101112
13141516171819
20212223242526
272829303112
3456789

导航

统计

常用链接

留言簿(10)

随笔档案

文章档案

最新随笔

搜索

积分与排名

最新随笔

最新评论

阅读排行榜

评论排行榜