C++的string實現MFC的CString::GetBuffer


  今天一個老同學QQ留言給我。

  老同學:“STL的string有沒有類似MFC的CString::GetBuffer的函數?"

  我當時正在搜夏娃種子沒空鳥他。

  過了一會,他問得更直接了:“如果調用SDK的::GetWindowText的時候,使用STL的string做為輸出緩沖區,該怎么辦?”

  為了打發他,我毫不猶豫的回到“(LPSTR)string::c_str();”

  5秒鍾后,老同學:“。。。。。。”。

  一看見他的一大串“點點點”,我猛然意識到我可能錯了。

  接着放下手頭的事情,夏娃可以慢慢找,老同學可不能瞎忽悠。隨后仔細想這件事,似乎還真沒這么簡單。

  string::c_str()返回的是const char* 類型。強制轉成char* 類型,是有不足的。一共有兩點:

  第一點顯而易見的是緩沖區溢出問題,解決這個問題只要分配一個足夠大的緩沖區就好了。比如在定義string類型的時候:

string str(MAX_PATH,'\0');

  又或者:

string str;
str.resize(MAX_PATH);

  兩種方法都使得string的成員變量“size”,變得足夠大。這樣只要保證對string::c_str()返回的地址寫操作的時候不超過MAX_PATH個字節就行了。到這里,似乎問題就解決了。不過別急,剛才不是說有兩點么,現在才第一點呢。如果第一點算是隱患,那么接下來的完全就是缺陷了。

  假設,剛才我們調用::GetWindowText的代碼片段如下:

  

using namespace std;
typedef basic_string<TCHAR> tsring;

tstring
strWndTitle(MAX_PATH,'\0'); ::GetWindowText(hWnd,(LPTSTR)strWndTitle.c_str(),MAX_PATH); if(strWndTitle == _T("hello world"))
{ ::MessageBox(NULL,NULL,NULL,MB_OK);
//do something... }

  這段代碼有問題嗎?咋一眼,沒有問題啊。其實不然,這樣的話,if里的代碼塊永遠都執行不到!

  假設hWnd所標示的窗口標題確實是為“hello world”,如果在if語句下個斷點,程序跑起來斷下來后,可以查看此時strWndTitle的內容確實是“hello world”,那么為什么執行不到if里面的語句塊呢?為了好說明,我們再看下面的代碼:

 

// 第一種
char* pBuff = (char*)string::c_str(); 

// 第二種
string::allocator_type alctor = string::get_allocator();
string::pointer pBuff = alctor.address(*(string.begin()));

  這兩種方式獲得的pBuff指針指向的地址其實是一樣的。第二種方式不常用,之所以讓大家看這兩種方式,是為了讓大家看看string::c_str()返回的地址究竟指向哪。本質上,這兩種方式是一模一樣的,也就是指向string的開始迭代器。

  “==”關系運算符,實際上是重載了string::compare,我們跟蹤進STL的源碼發現compare最后的實現是用memcmp實現的代碼如下:

static int __CLRCALL_OR_CDECL compare(const _Elem *_First1, const _Elem *_First2,
        size_t _Count)
        {    // compare [_First1, _First1 + _Count) with [_First2, ...)
        return (_CSTD memcmp(_First1, _First2, _Count));
        }

  看着有點頭大,我幫大家稍微轉換下,上面調用memcmp的時候實際相當於:

memcmp(strTitle.c_str(),"hello world",strlen("hello world"));

  看到這里,似乎沒有什么問題,事實也的確如此。OK,我們退棧看看上層主調函數。

                     // 這個compare就是上面那個調用了memcmp的那個
size_type _Ans = _Traits::compare(_Myptr() + _Off, _Ptr,
            _N0 < _Count ? _N0 : _Count);

return (_Ans != 0 ? (int)_Ans : _N0 < _Count ? -1
            : _N0 == _Count ? 0 : +1);    

  呵呵,是不是更加頭大的感覺?哈哈,下面是我給大家轉化的等價代碼,方便大家容易看明白:

int result = memcmp(strTitle.c_str(),"hello world",strlen("hello world"));

if(result == 0)
{
    if (strTitle.size == strlen("hello world"))
    {
        result = 0;
    }
    else
    {
        result = 1;
    }

    if (strTitle.size < strlen("hello world"))
    {
        result = -1;
    }
}            

  這下清晰多了吧,從上面很容易看出,string::compare先用memcmp比較內存,再檢查sting對象的size成員。盡管我們在memcmp的時候返回的是0,但是由於我們的strTitle的size大於strlen("hello world"),所以最終compare將返回1,即判定strTitle大於"hello world"。

  找到了原因,我們就不難理解剛才所說的為什么執行不到if語句塊中的代碼了。

  那么解決方案也很好辦,直接看碼:

 

using namespace std;
typedef basic_string<TCHAR> tsring;

tstring strWndTitle;

strWndTitle.resize(MAX_PATH);    // 類似於MFC的CString::GetBuffer(MAX_PATH);
LPTSTR pBuff = (LPTSTR)strWndTitle.c_str();
::GetWindowText(hWnd,(LPTSTR)strWndTitle.c_str(),MAX_PATH);
strWndTitle.resize(_tcslen(_T("hello world")));// 類似於MFC的CString::ReleaseBuffer()

if(strWndTitle == _T("hello world"))
{
   ::MessageBox(NULL,NULL,NULL,MB_OK);
    //do something...      
}

  關鍵在於對pBuff寫操作后,再次調用string::resize。

  哈哈,這樣我就可以比較完美的給老同學一個交代了。

總結:

  首先調用string::resize,相當於CSting::GetBuffer,進行內存分配。

  最后再次調用string::resize,相當於CString::ReleaseBuffer,進行釋放閑置內存。

  其間,需要注意沒有釋放閑置內存之前,使用string類的其他方法,會引起不可預料的意外情況。這跟MFC的CString進行GetBuffer后,沒有ReleaseBuffer之前,是一樣的。

  

  

 

  

  

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM