MFC字符串類型總結

MFC支持有很多種字符和字符串數據類型,在此將他們整理一下以便記憶。

1. char wchar_t

char         這個不用說了是標準c的字符類型,8bit

 

wchar_t       用來保存UNICODE字符集的類型,16bit

 

2. CHAR WCHAR TCHAR

CHAR         與小寫的char等價

             typedef char CHAR;

 

WCHAR         也可以認爲它與wchar_t等價

             #ifndef _MAC

             typedef wchar_t WCHAR;

             #else

             typedef unsigned short WCHAR;

             #endif

 

3. LPSTR LPWSTR LPCSTR LPCWSTR

LPSRT         等價於char*

             typedef CHAR *LPSTR;

 

LPWSRT       等價於wchar_t*

             typedef WCHAR *LPWSTR;

 

LPCSRT       等價於const char*

             typedef CONST CHAR *LPCSTR;

 

LPCWSRT       等價於const wchar_t*

             typedef CONST WCHAR *LPCWSTR;

 

4. TCHAR LPTSTR LPCTSTR

TCHAR         在非UNICODE環境下,它等價於char;在UNICODE環境下,它等價於wchar_t

             #ifdef   UNICODE

             typedef WCHAR TCHAR;

             #else

             typedef char TCHAR;

             #endif

 

LPTSTR       在非UNICODE環境下,它等價於char*;在UNICODE環境下,它等價於wchar_t*

             #ifdef   UNICODE

             typedef LPWSTR LPTSTR;

             #else

             typedef LPSTR LPTSTR;

             #endif

 

LPCTSTR       在非UNICODE環境下,它等價於const char*;在UNICODE環境下,它等價於const wchar_t*

             #ifdef   UNICODE

             typedef LPCWSTR LPCTSTR;

             #else

             typedef LPCSTR LPCTSTR;

             #endif

 

5. string CString

string       是C++標準的字符串類,定義在STL中

              typedef basic_string<char, char_traits<char>, allocator<char> > string;

 

CString       是Visual C++中最常用的字符串類,繼承自CSimpleStringT類,主要應用在MFC和ATL編程中。

             typedef CStringT<TCHAR, StrTraitMFC<TCHAR>> CString;

 

6. 常用函數的char版本和wchar_t版本

size_t strlen( const char *string );

size_t wcslen( const wchar_t *string );

 

char *strcpy( char *strDestination, const char *strSource );

wchar_t *wcscpy( wchar_t *strDestination, const wchar_t *strSource );

 

char *strcat( char *strDestination, const char *strSource );

wchar_t *wcscat( wchar_t *strDestination, const wchar_t *strSource );

 

int strcmp( const char *string1, const char *string2 );

int wcscmp( const wchar_t *string1, const wchar_t *string2 );

 

CString 與Char *的轉換:

 

有兩方法LPCTSTR和GetBuffer() 

看看這個

 

LPCTSTR 與 GetBuffer(int nMinBufLength) 

這兩個函數提供了與標準C的兼容轉換。在實際中使用頻率很高,但卻是最容易出錯的地方。這兩個函數實際上返回的都是指針,但它們有何區別呢?以及調用它們後,幕後是做了怎樣的處理過程呢? 

(1) LPCTSTR 它的執行過程其實很簡單,只是返回引用內存塊的串地址。 它是作爲操作符重載提供的, 

所以在代碼中有時可以隱式轉換,而有時卻需強制轉制。如: 

CString str; 

const char* p = (LPCTSTR)str; 

//假設有這樣的一個函數,Test(const char* p); 你就可以這樣調用 

Test(str);//這裏會隱式轉換爲LPCTSTR 

(2) GetBuffer(int nMinBufLength) 它類似,也會返回一個指針,不過它有點差別,返回的是LPTSTR 

(3) 這兩者到底有何不同呢?我想告訴大家,其本質上完全不一樣,一般說LPCTSTR轉換後只應該當常量使用,或者做函數的入參;而GetBuffer(...)取出指針後,可以通過這個指針來修改裏面的內容,或者做函數的入參。爲什麼呢?也許經常有這樣的代碼: 

CString str("abcd"); 

char* p = (char*)(const char*)str; 

p[2] = 'z'; 

其實,也許有這樣的代碼後,你的程序並沒有錯,而且程序也運行得挺好。但它卻是非常危險的。再看 

CString str("abcd"); 

CString test = str; 

.... 

char* p = (char*)(const char*)str; 

p[2] = 'z'; 

strcpy(p, "akfjaksjfakfakfakj");//這下完蛋了 

你知道此時,test中的值是多少嗎?答案是"abzd".它也跟着改變了,這不是你所期望發生的。但爲什麼會這樣呢?你稍微想想就會明白,前面說過,因爲CString是指向引用塊的,str與test指向同一塊地方,當你p[2]='z'後,當然test也會隨着改變。所以用它做LPCTSTR做轉換後,你只能去讀這塊數據,千萬別去改變它的內容。

 

假如我想直接通過指針去修改數據的話,那怎樣辦呢?就是用GetBuffer(...).看下述代碼: 

CString str("abcd"); 

CString test = str; 

.... 

char* p = str.GetBuffer(20); 

p[2] = 'z'; // 執行到此,現在test中值卻仍是"abcd" 

strcpy(p, "akfjaksjfakfakfakj"); // 執行到此,現在test中值還是"abcd" 

爲什麼會這樣?其實GetBuffer(20)調用時,它實際上另外建立了一塊新內塊存,並分配20字節長度的buffer,而原來的內存塊引用計數也相應減1. 所以執行代碼後str與test是指向了兩塊不同的地方,所以相安無事。 

(4) 不過這裏還有一點注意事項:就是str.GetBuffer(20)後,str的分配長度爲20,即指針p它所指向的buffer只有20字節長,給它賦值時,切不可超過,否則災難離你不遠了;如果指定長度小於原來串長度,如GetBuffer(1),實際上它會分配4個字節長度(即原來串長度);另外,當調用GetBuffer(...)後並改變其內容,一定要記得調用ReleaseBuffer(),這個函數會根據串內容來更新引用內存塊的頭部信息。 

(5) 最後還有一注意事項,看下述代碼: 

char* p = NULL; 

const char* q = NULL; 

CString str = "abcd"; 

q = (LPCTSTR)str; 

p = str.GetBuffer(20); 

AfxMessageBox(q);// 合法的 

strcpy(p, "this is test");//合法的, 

AfxMessageBox(q);// 非法的,可能完蛋 

strcpy(p, "this is test");//非法的,可能完蛋 

這裏要說的就是,當返回這些指針後, 如果CString對象生命結束,這些指針也相應無效。 

3 拷貝 & 賦值 & "引用內存塊" 什麼時候釋放?

 

下面演示一段代碼執行過程 

void Test() 

CString str("abcd");//str指向一引用內存塊(引用內存塊的引用計數爲1, 

長度爲4,分配長度爲4) 

CString a;//a指向一初始數據狀態, 

a = str; //a與str指向同一引用內存塊(引用內存塊的引用計數爲2, 

長度爲4,分配長度爲4) 

CString b(a);//a、b與str指向同一引用內存塊(引用內存塊的引用 

計數爲3,長度爲4,分配長度爲4) 

LPCTSTR temp = (LPCTSTR)a;//temp指向引用內存塊的串首地址。 

(引用內存塊的引用計數爲3,長度爲4,分配長度爲4) 

CString d = a; //a、b、d與str指向同一引用內存塊(引用內存塊的引用計數爲4, 長度爲4,分配長度爲4) 

b = "testa"; //這條語句實際是調用CString::operator=(CString&)函數。 

b指向一新分配的引用內存塊。(新分配的引用內存塊的 

引用計數爲1,長度爲5,分配長度爲5) 

//同時原引用內存塊引用計數減1. a、d與str仍指向原 

引用內存塊(引用內存塊的引用計數爲3,長度爲4,分配長度爲4) 

}//由於d生命結束,調用析構函數,導至引用計數減1(引用內存 

塊的引用計數爲2,長度爲4,分配長度爲4) 

LPTSTR temp = a.GetBuffer(10);//此語句也會導致重新分配新內存塊。 

temp指向新分配引用內存塊的串首地址(新 

分配的引用內存塊的引用計數爲1,長度 

爲0,分配長度爲10) 

//同時原引用內存塊引用計數減1. 只有str仍 

指向原引用內存塊(引用內存塊的引用計數爲1, 

長度爲4,分配長度爲4) 

strcpy(temp, "temp"); //a指向的引用內存塊的引用計數爲1,長度爲0,分配長度爲10 

a.ReleaseBuffer();//注意:a指向的引用內存塊的引用計數爲1,長度爲4,分配長度爲10 

//執行到此,所有的局部變量生命週期都已結束。對象str a b 各自調用自己的析構構 

//函數,所指向的引用內存塊也相應減1 

//注意,str a b 所分別指向的引用內存塊的計數均爲0,這導致所分配的內存塊釋放 

通過觀察上面執行過程,我們會發現CString雖然可以多個對象指向同一引用內塊存,但是它們在進行各種拷貝、賦值及改變串內容時,它的處理是很智能並且非常安全的,完全做到了互不干涉、互不影響。當然必須要求你的代碼使用正確恰當,特別是實際使用中會有更復雜的情況,如做函數參數、引用、及有時需保存到CStringList當中,如果哪怕有一小塊地方使用不當,其結果也會導致發生不可預知的錯誤


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