释放参数BSTR使用误区以及隐藏的内存破坏和内存泄漏
PS:明天上午,非常郁悶,有很多簡單基礎的問題搞得我有些迷茫,哎,代碼幾天不寫就忘。目前又不當COO,還是得用心記代碼哦!
????BSTR應用誤區以及隱藏的內存損壞和內存泄漏
????
????作者:magictong
????
????簡介
????BSTR的數據結構是什么模樣并非本文討論的問題,但是卻是本文的基礎。在處理COM的跨平臺編程的問題時,需要定義一種通用的字符串類型,它就這樣被發明白,而且它的結構很輕易匹配到不同的編程環境中,對于C++程序員來講,要記著的最基本的一點就是分配BSTR結構時,并非簡單的調用new、malloc就能夠實現的,而且大部分的字符串相關的API和C庫函數也是不能用于處理BSTR的,其實這也是應用BSTR的誤區之一,在C++里面,BSTR被簡單的define為wchar_t*,這也是輕易引發誤解的原因之一。
????明天注意討論一下BSTR作為函數的參數、返回值時,調用者和被調用者操作BSTR時扮演的不同角色問題。首先需要注意的時,在必須應用BSTR時盡量應用BSTR的包裝類CComBSTR,它會給你額定實現一些資源的管理工作,令你省力不少,犯錯的可能性也就大大降低了。
????像一個很簡單自然的用法:BSTR?bstrInfo?=?L””,實際上是錯誤的(當然如果你說我當前就把bstrInfo完全當成wchar_t*來應用,那我無話可說)。對于一個BSTR變量來講,它只多是NULL或者正確分配的BSTR指針。
????
????BSTR當成參數和返回應用的基本約定
????1、[in\out]參數
????譬如函數:void?GetChangBSTR(BSTR*?pbstrTitle),在GetChangBSTR函數外部需要先讀取pbstrTitle的值應用,然后改變pbstrTitle的值。
????這類情況下被調用者(也就是GetChangBSTR)在給pbstrTitle賦新值前,需要先釋放pbstrTitle里面本來的值,然后再給pbstrTitle分配上新的值。
????而調用者(也就是調用GetChangBSTR的函數),在傳參之前需要先給pbstrTitle賦值上正確的值,調用結束之后還需要再釋放pbstrTitle的值。
????
????2、[in]參數
????譬如函數:void?PutText(BSTR?bstrText),在PutText外部僅需讀取bstrText的值。
????這類情況下被調用者可以隨意讀取bstrText的值,不需要做其它操作。
????而調用者,在傳參之前需要先給bstrText賦上正確的值,調用結束后需要釋放bstrText的值。
????
????3、[out]參數
????譬如函數:void?GetText(BSTR*?pbstrText),在GetText外部直接給pbstrText賦值。
????這類情況下被調用者直接給pbstrText賦值便可,不需要做其它操作。
????而調用者,在傳參之前不能給pbstrText賦值,調用結束后需要釋放pbstrText的值。
????
????4、返回參數
????譬如函數:BSTR?GetText(),在GetText外部會返回一個BSTR出來。調用者直接返回一個有效的BSTR便可,而調用者需要釋放這個返回的BSTR。
????
????BSTR當成參數和返回應用的誤區
????1、[in\out]參數
????這類情況下,被調用者如果沒有給參數賦值,不要釋放原始值,因為根據約定調用者還會釋放一次,這樣會造成多次釋放,可能致使內存損壞。
????void?GetChangBSTR(/*[in\out]?*/BSTR*?pbstrTitle)
????{
????//?using?the?bs?here
????DoSomething(*pbstrTitle);
?????
????if?(...)
????{
????::SysReAllocString(*pbstrTitle,?_T("Tecnet"));
????}
????else
????{
????::SysFreeString(*pbstrTitle);?//?這里的做法是錯誤的。
????}
????return;
????}
????
????2、[in]參數
????被調用者不要對參數停止釋放操作,原因和上面相同,調用者還會重復釋放一次,可能致使內存損壞。
????void?PutText(/*[in]?*/BSTR?bstrText)
????{
????//?using?the?bs?here
????DoSomething(bstrText);
????::SysFreeString(bstrText);?//?這里的做法是錯誤的。
????
????return;
????}
????
????3、[out]參數
????如果調用者在傳參之前給參數賦值,參數傳遞給被調用者之后,在改變值之前是沒有釋放操作的,也就是說會有內存泄漏。
????void?GetText(/*[out]?*/BSTR*?pbstrText)
????{
????::SysAllocString(*pbstrText,?_T("Tecnet"));
????return;
????}
????//?use?GetText
????BSTR?bstrText;
????::SysAllocString(bstrText,?_T("qq"));
????GetText(&bstrText);
????::SysFreeString(bstrText);?//?很可憐,這里實際上只釋放了一次
????
????4、返回參數
????被調用者不要釋放(不管是直接還是直接致使的)返回給調用者的BSTR,因為調用者會釋放。
????BSTR?GetText()
????{
????BSTR?bstrText?=?::SysAllocString(bstrText,?_T("Tecnet"));
????::SysFreeString(bstrText);?//?這里釋放就喜劇了
????return?bstrText;
????}
????//?use?GetText
????BSTR?bstrText?=?GetText(&bstrText);
????//?use?bstrText
????DoSome(bstrText);?//?bstrText已經被釋放,應用是有問題的
每日一道理一個安靜的夜晚,我獨自一人,有些空虛,有些凄涼。坐在星空下,抬頭仰望美麗天空,感覺真實卻由虛幻,閃閃爍爍,似乎看來還有些跳動。美的一切總在瞬間,如同“海市蜃樓”般,也只是剎那間的一閃而過,當天空變得明亮,而這星星也早已一同退去……
????::SysFreeString(bstrText);?//?這就不僅僅是重復釋放的問題了
????
????BSTR在類里面應用的誤區
????1、我想把某個[in]參數BSTR保存到某個類成員變量
????這類情況下,直接賦值是不行的,因為表面調用者會釋放這個BSTR參數,因此要保存的話,需要類函數自己重新申請一個新的BSTR。
????void?CSomeClass::SetText(BSTR?bs)
????{
????//?m_bstrText是CSomeClass的成員變量
????m_bstrText?=?bs;?//?錯誤做法
????m_bstrText?=?::SysReAllocString(bs);?//?正確做法
????}
????
????2、我想傳出一個類的BSTR成員變量
????同樣的道理,因為表面可能在某個時間釋放傳出的BSTR變量,因此要防止類成員變量被無辜釋放,需要生成一個有效的拷貝,再傳出。
????void?CSomeClass::GetText(BSTR&?bs)
????{
????//?m_bstrText是CSomeClass的成員變量
????bs?=?m_bstrText;?//?錯誤做法
????bs?=?::SysAllocString(m_bstrText);?//?正確做法
????}
????
????BSTR的封裝類CComBSTR
????微軟發明我們應用BSTR有上面的各種不爽,因此決議對其停止封裝,很貼心吧!嗯,確實貼心,其中比擬好的一個封裝就是CComBSTR(很多項目組可能有自己的BSTR封裝,但是其實都是迥然不同的),這個封裝類確實很好用(雖然沒有供給CString那么多牛皮的功能),應用很方便,但是,如果我們錯誤應用也會發生噩夢,而且錯誤很難查找,我們來點評幾個(注意:下面的內容需要對CComBSTR封裝的基本原理和供給接口有必定了解,但這些并非本文要討論的內容,另外一個封裝類是_bstr_t,它是用引用計數來管理的,實現比CComBSTR復雜很多,個人不太建議應用_bstr_t)。
????1、被調用者違背out參數應用約定
????void?GetText(/*[out]*/BSTR&?bstrText)
????{
????CComBSTR?bstrT(_T("qqpcmgr"));
????//?錯誤:bstrT會被自動釋放,違背了out參數的應用約定
????bstrText?=?(BSTR)bstrT;
????return;
????
????//
????//?正確的做法,一般來講Detach是效率更好的方法
????//?但是如果bstrT本身是一個類成員變量,可能要用Copy
????bstrText?=?bstrT.Copy();
????????bstrText?=?bstrT.Detach();
????return;
????}
????
????2、調用者違背out參數應用約定
????void?GetText(/*[out]*/BSTR&?bstrText)
????{
????//?……
????}
????//?use?GetText
????CComBSTR?bstrText(L"qq");
????//?內存泄漏,調用GetText前要先清空bstrText
????//?bstrText.Empty();
????GetText(bstrText);
????
????3、看一個費解一點的
????void?GetText(/*[out]*/BSTR&?bstrText)
????{
????//?……
????}
????//?use?GetText
????CComBSTR?bstrText;
????BSTR?bstrInfo?=?NULL;
????GetText(bstrInfo);
????//?如果后面沒有表現釋放bstrInfo
????//?這里就會有內存泄漏,這類混用也是比擬危險的
????bstrText?=?bstrInfo;
????//?如果你想CComBSTR接管一個BSTR,可以應用
????//?bstrText.Attach(bstrInfo);
????
????4、重復釋放,造成內存損壞
????{
????CComBSTR?bstrText(L"Tencent");
????//?因為CComBSTR重載了operator?BSTR操作,因此這里是支持的
????::SysFreeString(bstrText);?//?錯誤做法,如果你確實想釋放,可以調用Empty
????}
????//?超越bstrText范圍,bstrText會被自動釋放,可能致使內存損壞
????//?……
????
????參考文獻
????[1]?BSTR?https://zh.wikipedia.org/zh-cn/BSTR
????[2]?BSTR_INSIDE?http://wenku.baidu.com/view/d577a1c5d5bbfd0a795673b2.html
????
文章結束給大家分享下程序員的一些笑話語錄: 現在社會太數字化了,所以最好是有一個集很多功能于一身的設備!
--------------------------------- 原創文章 By
釋放和參數
---------------------------------
轉載于:https://www.cnblogs.com/xinyuyuanm/archive/2013/05/30/3108990.html
總結
以上是生活随笔為你收集整理的释放参数BSTR使用误区以及隐藏的内存破坏和内存泄漏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: inverted index 反向索引
- 下一篇: truenas快速设置第二弹(nextc