1.LPCTSTR
L表示Long
P表示指針
C表示是一個常量
T表示Win32環境中有一個_T宏
STR表示一個字符串
所以LPCTSTR就是一個指向const對象的指針
2.常量字符串ansi和unicode的區分是由宏_T來決定的。但是用_T("abcd")時, 字符串"abcd"就會根據編譯時是否定是_UNICODE來決定是char* 還是 wchar_t*。
ansi情況下,LPCTSTR 就是 const char*, 是常量字符串(不能修改的)。
而LPTSTR 就是 char*, 即普通字符串(非常量,可修改的)。
這兩種都是基本類型, 而CString 是 C++類, 兼容這兩種基本類型是最起碼的任務了。
由於const char* 最簡單(常量,不涉及內存變更,操作迅速), CString 直接定義了一個類型轉換函數:
operator LPCTSTR( )
{.
.....
}
函數直接返回所維護的字符串。
3.LPCTSTR 它的執行過程其實很簡單,只是返回引用內存塊的串地址。 它是作為操作符重載提供的,
所以在代碼中有時可以隱式轉換,而有時卻需強制轉制。如:
CString str;
const char* p = (LPCTSTR)str;
//假設有這樣的一個函數,Test(const char* p); 你就可以這樣調用
Test(str);//這里會隱式轉換為LPCTSTR LPTSTR和LPCTSTR的區別就是不在是const類型,相當於char*
再者,string類型轉換成const char* 類型的方法:string::c_str().
1. string轉const char* string s = "abc"; const char* c_s = s.c_str(); 2. const char*轉string 直接賦值即可 const char* c_s = "abc"; string s(c_s); 3. string轉char* string s = "abc"; char* c; const int len = s.length(); c = new char[len+1]; strcpy(c,s.c_str()); 4. char*轉string char* c = "abc"; string s(c); 5. const char*轉char* const char* cpc = "abc"; char* pc = new char[100];//足夠長 strcpy(pc,cpc); 6. char*轉const char* 直接賦值即可 char* pc = "abc"; const char* cpc = pc;
4.GetBuffer(int nMinBufLength)與 它類似,也會返回一個指針,不過它有點差別,返回的是LPTSTR
5.這兩者到底有何不同呢?其本質上完全不一樣,一般說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是指向了兩塊不同的地方,所以相安無事。
不過這里還有一點注意事項:就是str.GetBuffer(20)后,str的分配長度為20,即指針p它所指向的buffer只有20字節長,給它賦值時,切不可超過,否則災難離你不遠了;如果指定長度小於原來串長度,如GetBuffer(1),實際上它會分配4個字節長度(即原來串長度);另外,當調用GetBuffer(...)后並改變其內容,一定要記得調用ReleaseBuffer(),這個函數會根據串內容來更新引用內存塊的頭部信息。
6.拷貝 & 賦值 & "引用內存塊" 什么時候釋放?
下面演示一段代碼執行過程
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當中,如果哪怕有一小塊地方使用不當,其結果也會導致發生不可預知的錯誤
7.C語言中表示字符串,沒有string概念,只可以用char*和char[];在MFC中,為了服務於字符串操作,添加了類CString,該類的頭 文件是afx.h.
從char* 到CString的轉換很簡單,只需要用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)); 注:上面語句將CString-->const char*
以上兩種用法都是正確地.因為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-->char*,轉換后不修改字符串的內容,這個轉換是可以的。
這樣對嗎?我們首先來看一個例子:
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();
注:將CString-->char*,並修改字符串內容。
當我們需要字符數組時調用GetBuffer(int n),其中n為我們需要的字符數組的長度.使用完成后一定要馬上調用ReleaseBuffer();還有很重要的一點就是,在能使用const char *的地方,就不要使用char *
8.UNICODE下寬字符的CString轉換為const char *和char到WCHAR的相互轉換
UNICODE環境下TCHAR等價於WCHAR,ANSI環境下TCHAR等價於CHAR
//CString轉換為char*類型
CString theString( "This is a test" ); int sizeOfString = (theString.GetLength() + 1); LPTSTR lpsz = new TCHAR[ sizeOfString ]; _tcscpy_s(lpsz, sizeOfString, theString); 最后再轉換一下lpsz為const型的 LPTSTR在UNICODE環境下編譯是wchar_t類型
從WCHAR到char的轉換: int WideCharToMultiByte( UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar );