getbuffer是為了讓你使用CString類中,儲存字串緩衝區的那塊指標.
至於releasebuffer,在MSDN中有這樣一句話.
If you use the pointer returned by GetBuffer to
change the string contents, you must call ReleaseBuffer
before using any other CString member functions.
在對GetBuffer返回的指標使用之後需要調用ReleaseBuffer,這樣才能使用其他Cstring的operations。否則會發生錯誤.
首先舉個例子。CString s( "abcd" );
int len=s.GetLength();
LPTSTR p = s.GetBuffer( 5 );
strcpy( p, "Hello" );
這是 GetBuffer 的第一種用法,也是最簡單的一種,不用給它傳遞參數,它使用預設值 0,意思是:“給我這個字串的指標,我保證不加長它”。當你調用 ReleaseBuffer 時,字串的實際長度會被重新計算,然後存入 CString 對象中。
如果你需要修改 CString 中的內容,它有一個特殊的方法可以使用,那就是 GetBuffer,它的作用是返回一個可寫的緩衝指標。
如果僅僅是讀出CString中的內容,那麼只需要用GetBuffer(0)即可。如果後面對CString還有其他動作,那麼立刻ReleaseBuffer。
其他:
GetBuffer() 他會create出所指定大小的空間出來 這個空間是可以讓我們修改的
很多時候 有的 API 會要一個(char*)的指標作為輸出
如果我們就因為這樣去產生一個(char*)的buffer 給他 等到資料取出來之後
便無法使用CString 的種種方便功能
因此 比較好的做法 便是用GetBuffer()來產生一個buffer空間給他
等到取出來之後 我們便可以直接使用CString來對他操作
GetBuffer() 使用完後 最好是呼叫一下ReleaseBuffer()做為結束
雖然小弟的網誌之前已經有很多GetBuffer()的使用了 不過還是附個範例
CFile file;
// FILE_NAME 為事先定義好的設定檔名稱
if(file.Open(FILE_NAME,CFile::modeRead))
{
CString szContent;
int nFileLength = file.GetLength();
file.Read(szContent.GetBuffer(nFileLength),nFileLength);
szContent.ReleaseBuffer();
// 取得檔案內容放在szContent中 我們之後可以對其操作
}
關於GetBuffer/ReleaseBuffer,網上比較流行的一種說法是:如果你要直接修改CString的內部資料,就要調用GetBuffer/ReleaseBuffer.我也同意這樣的表述.
下面是幾個例子,主要是錯誤的例子,來加深理解.
1
CString strTest
=
"
123
"
;
char
*
p
=
strTest.GetBuffer(
0
);
int
i
=
atoi(p);
strTest.ReleaseBuffer();
這種用法當然沒有錯,但是我認為這裡的GetBuffer/ReleaseBuffer是沒有必要的
,為什麼呢?因為
int __cdecl atoi(const char *)
的參數是const char*,CString的內部資料肯定不會被修改的
.
所以上面的代碼可以直接寫成
CString strTest
=
"
123
"
;
int
i
=
atoi((LPCTSTR)strTest);
順
便說一下GetBuffer的參數問題,網上的例子中,很多都是GetBuffer(5)
GetBuffer(10)這樣的常數,實際中的程式不可能是這麼容易事Crowdsourced Security Testing道的,所以也就有了
strTest.GetBuffer(strTest.GetLength()
)的寫法.其實,GetBuffer(0)就可以了.可以由GetBuffer的源碼得到驗證.
2
CString strTest
=
"
123 45
"
;
//
some other code
CString strTest2
=
strTest;//之後兩個cstring內容仍然存於同一記憶體位置
char
seps[]
=
"
"
;
char
*
pToken
=
0
;
//
char* pStr = strTest2.GetBuffer(0);
pToken
=
strtok((
char
*
)(LPCTSTR)strTest2, seps);
//pToken
=
strtok(pStr
, seps);
while
(pToken)
pToken
=
strtok(NULL, seps);
//strTest2.ReleaseBuffer(0);
CString 類裡面有專門的結構體來記錄這些資訊
struct CStringData
{
long nRefs; // reference count 引用計數
int nDataLength; // length of data (including terminator) 資料長度
int nAllocLength; // length of allocation 記憶體配置長度
// TCHAR data[nAllocLength]
TCHAR* data() // TCHAR* to managed data
{ return (TCHAR*)(this+1); }
};
賦值的時候只是簡單的把引用計數加1, nRefs++,然後指向同一記憶體單元
每一個對象銷毀時,先把引用計數減1, nRefs--,然後判斷是否為0,如果為0才真正釋放
運行上面的代碼,可以看到strTest的值也變了,呵呵,這就是程式中一些關與CString的奇怪問題的起源.如果用注釋中的GetBuffer/ReleaseBuffer方法,就一點問題也沒有了.
同
樣,對於ReleaseBuffer的參數,預設的是-1,但是我不建議.因為-1表示使用當前的00結束符位置來確定新的長度.而上面的例子
中,strtok是會重新設定00結束符的,所以,安全的做法,就是把這個CString的長度設為0,ReleaseBuffer(0),反正它的內容
已經變了,也沒有人要用了.
說明一下,GetBuffer/ReleaseBuffer方法只能保證strTest不變,strTest2還是會變的.所以,對於一個成員變數,比如m_strTest2調用ReleaseBuffer要多一個心眼,局部變數就不用想這麼多了.
那麼怎麼從最開始就意識到程式寫錯了呢?上面代碼中(char
*
)(LPCTSTR)是很危險的,把const去掉了,否則strtok是編譯不過的,也從一個側面說明了const的重要性.