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

來源:互聯網
上載者:User

  今天一個老同學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之前,是一樣的。

  

  

 

  

  

  

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.