CString的線程安全問題
在多線程中使用一個局部CString變量相加時發現相加的結果是后一個字符串值,沒有時間過多研究,只好改成了char*,不過如下方法也可以避免:CWin32Heap stringHeap( HEAP_NO_SERIALIZE, 0, 0 );


找了一些資料貼上來希望對過客有用:
http://www.shnenglu.com/alantop/archive/2008/07/10/55786.html
討論CString線程安全性問題(內存泄漏)
CString在線程處理中,稍有處理不當,極易引起內存泄漏。
讓我們來看一個例子:
在線程函數中使用如下代碼


可以看到非常簡單,在debug下,很容易看到如下的內存泄漏。
怎么回事?
先把修改好的代碼放上來




http://msdn.microsoft.com/zh-cn/library/cc485480(VS.71).aspx
Visual C++ 概念:添加功能
自定義字符串管理器的實現(基本方法)
為字符串數據自定義內存分配方案的最簡單的方式是使用 ATL 提供的 CAtlStringMgr 類,但您需要自己提供內存分配例程。CAtlStringMgr 的構造函數采用單一參數:即指向 IAtlMemMgr 對象的指針。IAtlMemMgr 是提供到堆的一般接口的抽象基類。通過 IAtlMemMgr 接口,CAtlStringMgr 分配、重新分配和釋放用于存儲字符串數據的內存。您既可以自已實現 IAtlMemMgr 接口,也可以使用由 ATL 提供的五個內存管理器類之一。ATL 提供的內存管理器只包裝現有的內存分配功能:
- CCRTHeap 包裝標準 CRT 堆功能(malloc、free 和 realloc)
- CWin32Heap 使用 HeapAlloc、HeapFree 和 HeapRealloc 包裝 Win32 堆句柄
- CLocalHeap 包裝 Win32 API:LocalAlloc、LocalFree 和 LocalRealloc
- CGlobalHeap 包裝 Win32 API:GlobalAlloc、GlobalFree 和 GlobalRealloc
- CComHeap 包裝 COM 任務分配器 API:CoTaskMemAlloc、CoTaskMemFree 和 CoTaskMemRealloc
要進行字符串內存管理,最有用的類是 CWin32Heap,因為它使您能夠創建多個獨立的堆。例如,如果使用僅用于字符串的獨立堆,可進行以下操作:
//Declare a thread-safe, growable, private heap with initial size 0 CWin32Heap g_stringHeap( 0, 0, 0 ); // Declare a string manager that uses the private heap CAtlStringMgr g_stringMgr( &g_stringHeap );
要使用此專用的字符串管理器來管理 CString 變量的內存,請將一個指針傳遞給管理器作為 CString 變量的構造函數的一個參數:
void PrintPowers( int nBase ) { int n = 1; for( int nPower = 0; nPower < 10; nPower++ ) { // Use the private string manager, instead of the default CString strPower( &g_stringMgr ); strPower.Format( "%d", n ); printf( "%s\n", LPCSTR( strPower ) ); n *= nBase; } }
http://blog.csdn.net/zero_dian/archive/2006/01/08/573352.aspx 談新手對CString的使用
CString類功能強大,比STL的string類有過之無不及.新手使用CString時,都會被它強大的功能所吸引.然而由于對它內部機制的不了解,新手在將CString向C的字符數組轉換時容易出現很多問題.因為CString已經重載了LPCTSTR運算符,所以CString類向const char *轉換時沒有什么麻煩,如下所示:
char a[100];
CString str("aaaaaa");
strncpy(a,(LPCTSTR)str,sizeof(a));
或者如下:
strncpy(a,str,sizeof(a));
以上兩種用法都是正確地.因為strncpy的第二個參數類型為const char *.所以編譯器會自動將CString類轉換成const char *.很多人對LPCTSTR是什么東西迷惑不解,讓我們來看看:
1.LP表示長指針,在win16下有長指針(LP)和短指針(P)的區別,而在win32下是沒有區別的,都是32位.所以這里的LP和P是等價的.
2.C表示const
3.T是什么東西呢,我們知道TCHAR在采用UNICODE方式編譯時是wchar_t,在普通時編譯成char那么就可以看出LPCTSTR(PCTSTR)在UINCODE時是const wchar_t *,PCWSTR,LPCWSTR,在多字節字符模式時是const char *, PCSTR,LPCSTR.接下來我們看在非UNICODE情況下,怎樣將CString轉換成char *,很多初學者都為了方便采用如下方法:
(char *)(LPCSTR)str
這樣對嗎?我們首先來看一個例子:
CString str("aa");
strcpy((char *)(LPCTSTR)str,"aaaaaaaa");
cout<<(LPCTSTR)str<<endl;
在Debug下運行出現了異常,我們都知道CString類內部有自己的字符指針,指向一個已分配的字符緩沖區.如果往里面寫的字符數超出了緩沖區范圍,當然會出現異常.但這個程序在Release版本下不會出現問題.原來對CString類已經進行了優化.當需要分配的內存小于64字節時,直接分配64字節的內存,以此類推,一般CString類字符緩沖區的大小為64,128,256,512...這樣是為了減少內存分配的次數,提高速度.
那有人就說我往里面寫的字符數不超過它原來的字符數,不就不會出錯了,比如
CString str("aaaaaaa");
strcpy((char *)(LPCTSTR)str,"aa");
cout<<(LPCTSTR)str<<endl;
這樣看起來是沒什么問題.我們再來看下面這個例子:
CString str("aaaaaaa");
strcpy((char *)(LPCTSTR)str,"aa");
cout<<(LPCTSTR)str<<endl;
cout<<str.GetLength()<<endl;
我們看到str的長度沒有隨之改變,繼續為7而不是2.還有更嚴重的問題:
CString str("aaaaaaa");
CString str1 = str;
strcpy((char *)(LPCTSTR)str,"aa");
cout<<(LPCTSTR)str<<endl;
cout<<(LPCTSTR)str1<<endl;
按說我們只改變了str,str1應該沒有改變呀,可是事實時他們都變成了"aa".難道str和str1里面的字符指針指向的緩沖區是一個.我們在Effective C++里面得知,如果你的類內部有包含指針,請為你的類寫一個拷貝構造函數和賦值運算符.不要讓兩個對象內部的指針指向同一區域,而應該重新分配內存.難道是微軟犯了錯?
原來這里還有一個"寫時復制"和"引用計數"的概念.CString類的用途很廣,這樣有可能在系統內部產生大量的CString臨時對象.這時為了優化效率,就采用在系統軟件內部廣泛使用的"寫時復制"概念.即當從一個CString產生另一個CString并不復制它的字符緩沖區內容,而只是將字符緩沖區的"引用計數"加1.當需要改寫字符緩沖區內的內容時,才分配內存,并復制內容.以后我會給出一個"寫時復制"和"引用計數"的例子我們回到主題上來,當我們需要將CString轉換成char *時,我們應該怎么做呢?其時只是麻煩一點,如下所示:
CString str("aaaaaaa");
strcpy(str.GetBuffer(10),"aa");
str.ReleaseBuffer();
當我們需要字符數組時調用GetBuffer(int n),其中n為我們需要的字符數組的長度.使用完成后一定要馬上調用ReleaseBuffer();還有很重要的一點就是,在能使用const char *的地方,就不要使用char *
posted on 2008-12-22 17:14 葉子 閱讀(4654) 評論(0) 編輯 收藏 引用 所屬分類: C\C++