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

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之前,是一樣的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章