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當中,如果哪怕有一小塊地方使用不當,其結果也會導致發生不可預知的錯誤