MFC類
CString
因為一個MFC CString類的對象包含TCHAR類型的字元,所以確切的字元類型取決於你所定義的預先處理符號。大體來說,CString 很像STL string,這意味著你必須把它當成不透明的對象,只能使用CString提供的方法來修改CString對象。CString有一個string所不具備的優點:CString具有接收MBCS和Unicode兩種字串的建構函式,它還有一個LPCTSTR轉換符,所以你可以把CString對象直接傳給一個接收LPCTSTR的函數而不需要調用c_str()函數。
// ConstructingCString s1 = "char string"; // construct from a LPCSTRCString s2 = L"wide char string"; // construct from a LPCWSTRCString s3 ( '' '', 100 ); // pre-allocate a 100-byte buffer, fill with spacesCString s4 = "New window text"; // You can pass a CString in place of an LPCTSTR: SetWindowText ( hwndSomeWindow, s4 ); // Or, equivalently, explicitly cast the CString: SetWindowText ( hwndSomeWindow, (LPCTSTR) s4 );
你可以從你的字串表中裝載一個字串,CString的一個建構函式和LoadString()函數可以完成它。Format()方法能夠從字串表中隨意的讀取一個具有一定格式的字串。
// Constructing/loading from string tableCString s5 ( (LPCTSTR) IDS_SOME_STR ); // load from string tableCString s6, s7; // Load from string table. s6.LoadString ( IDS_SOME_STR ); // Load printf-style format string from the string table: s7.Format ( IDS_SOME_FORMAT, "bob", nSomeStuff, ... );
第一個建構函式看起來有點奇怪,但是這實際上是文檔說明的裝入一個字串的方法。 注意,對一個CString變數,你可以使用的唯一合法轉換符是LPCTSTR。轉換成LPTSTR(非常量指標)是錯誤的。養成把一個CString變數轉換成LPTSTR的習慣將會給你帶來傷害,因為當你的程式後來崩潰時,你可能不知道為什麼,因為你到處都使用同樣的代碼而那時它們都恰巧正常工作。正確的得到一個指向緩衝區的非常量指標的方法是調用GetBuffer()方法。下面是正確的用法的一個例子,這段代碼是給一個清單控制項中的項設定文字:
CString str = _T("new text");LVITEM item = {0}; item.mask = LVIF_TEXT; item.iItem = 1; item.pszText = (LPTSTR)(LPCTSTR) str; // WRONG! item.pszText = str.GetBuffer(0); // correct ListView_SetItem ( &item );str.ReleaseBuffer(); // return control of the buffer to str
pszText成員是一個LPTSTR變數,一個非常量指標,因此你需要對str調用GetBuffer()。GetBuffer()的參數是你需要CString為緩衝區分配的最小長度。如果因為某些原因,你需要一個可修改的緩衝區來存放1K TCHARs,你需要調用GetBuffer(1024)。把0作為參數時,GetBuffer()返回的是指向字串當前內容的指標。
上面劃線的語句可以被編譯,在這種情況下,甚至可以正常起作用。但這並不意味著這行代碼是正確的。通過使用非常量轉換,你已經破壞了物件導向的封裝,並對CString的內部實現作了某些假定。如果你有這樣的轉換習慣,你終將會陷入代碼崩潰的境地。你會想代碼為什麼不能正常工作了,因為你到處都使用同樣的代碼而那些代碼看起來是正確的。
你知道人們總是抱怨現在的軟體的bug是多麼的多嗎?軟體中的bug是因為程式員寫了不正確的代碼。難道你真的想寫一些你知道是錯誤的代碼來為所有的軟體都滿是bug這種認識做貢獻嗎?花些時間來學習使用CString的正確方法讓你的代碼在任何時間都正常工作把。
CString 有兩個函數來從一個 CString 建立一個 BSTR。它們是 AllocSysString() 和SetSysString()。
// Converting to BSTRCString s5 = "Bob!";BSTR bs1 = NULL, bs2 = NULL; bs1 = s5.AllocSysString(); s5.SetSysString ( &bs2 ); SysFreeString ( bs1 ); SysFreeString ( bs2 );
COleVariant
COleVariant和CComVariant.很相似。COleVariant繼承自VARIANT,所以它可以傳給接收VARIANT的函數。然而,不像CComVariant,COleVariant只有一個LPCTSTR建構函式。沒有對LPCSTR 和LPCWSTR的建構函式。在大多數情況下這不是一個問題,因為不管怎樣你的字串很可能是LPCTSTRs,但這是一個需要意識到的問題。COleVariant還有一個接收CString參數的建構函式。
// ConstructingCString s1 = _T("tchar string");COleVariant v1 = _T("Bob"); // construct from an LPCTSTRCOleVariant v2 = s1; // copy from a CString
像CComVariant一樣,你必須直接存取VARIANT的成員。如果需要把VARIANT轉換成一個字串,你應該使用ChangeType()方法。然而,COleVariant::ChangeType()如果失敗會拋出異常,而不是返回一個表示失敗的HRESULT代碼。
// Extracting dataCOleVariant v3 = ...; // fill in v3 from somewhereBSTR bs = NULL; try { v3.ChangeType ( VT_BSTR ); bs = v3.bstrVal; } catch ( COleException* e ) { // error, couldn''t convert } SysFreeString ( bs );
WTL 類
CString
WTL的CString的行為和MFC的 CString完全一樣,所以你可以參考上面關於MFC的 CString的介紹。
CLR 和 VC 7 類
System::String是用來處理字串的.NET類。在內部,一個String對象包含一個不可改變的字串序列。任何對String對象的操作實際上都是返回了一個新的String對象,因為原始的對象是不可改變的。String的一個特性是如果你有不止一個String對象包含相同的字元序列,它們實際上是指向相同的對象的。相對於C++的使用擴充是增加了一個新的字串常量首碼S,S用來代表一個受控的字串常量(a managed string literal)。
// ConstructingString* ms = S"This is a nice managed string";
你可以傳遞一個非受控的字串來建立一個String對象,但是樣會比使用受控字串來建立String對象造成效率的微小損失。這是因為所有以S作為首碼的相同的字串執行個體都代表同樣的對象,但這對非受控對象是不適用的。下面的代碼清楚地闡明了這一點:
String* ms1 = S"this is nice";String* ms2 = S"this is nice";String* ms3 = L"this is nice"; Console::WriteLine ( ms1 == ms2 ); // prints true Console::WriteLine ( ms1 == ms3); // prints false
正確的比較可能沒有使用S首碼的字串的方法是使用String::CompareTo()
Console::WriteLine ( ms1->CompareTo(ms2) ); Console::WriteLine ( ms1->CompareTo(ms3) );
上面的兩行代碼都會列印0,0表示兩個字串相等。 String和MFC 7 CString之間的轉換是很容易的。CString有一個向LPCTSTR的轉換操作,而String有兩個接收char* 和 wchar_t*的建構函式,因此你可以把一個CString變數直接傳給一個String的建構函式。
CString s1 ( "hello world" );String* s2 ( s1 ); // copy from a CString
反方向的轉換也很類似
String* s1 = S"Three cats";CString s2 ( s1 );
這也許會使你感到一點迷惑,但是它確實是起作用的。因為從VS.NET 開始,CString 有了一個接收String 對象的建構函式。
CStringT ( System::String* pString );
對於一些快速控制項目,你可能想訪問底層的字串:
String* s1 = S"Three cats"; Console::WriteLine ( s1 );const __wchar_t __pin* pstr = PtrToStringChars(s1); for ( int i = 0; i < wcslen(pstr); i++ ) (*const_cast<__wchar_t*>(pstr+i))++; Console::WriteLine ( s1 );
PtrToStringChars()返回一個指向底層字串的const __wchar_t* ,我們需要固定它,否則垃圾收集器或許會在我們正在管理它的內容的時候移動了它。
在 printf-style 格式函數中使用字串類
當你在printf()或者類似的函數中使用字串封裝類時你必須十分小心。這些函數包括sprintf()和它的變體,還有TRACE和ATLTRACE宏。因為這些函數沒有對添加的參數的類型檢查,你必須小心,只能傳給它們C語言風格的字串指標,而不是一個完整的字串類。
例如,要把一個_bstr_t 字串傳給ATLTRACE(),你必須使用顯式轉換(LPCSTR) 或者(LPCWSTR):
_bstr_t bs = L"Bob!";ATLTRACE("The string is: %s in line %d/n", (LPCSTR) bs, nLine);
如果你忘了使用轉換符而把整個_bstr_t對象傳給了函數,將會顯示一些毫無意義的輸出,因為_bstr_t儲存的內部資料會全部被輸出。
所有類的總結
兩個字串類之間進行轉換的常用方式是:先把源字串轉換成一個C語言風格的字串指標,然後把這個指標傳遞給目的類型的建構函式。下面這張表顯示了怎樣把一個字串轉換成一個C語言風格的字串指標以及哪些類具有接收C語言風格的字串指標的建構函式。
Class |
string type |
convert to char*? |
convert to const char*? |
convert to wchar_t*? |
convert to const wchar_t*? |
convert to BSTR? |
construct from char*? |
construct from wchar_t*? |
_bstr_t |
BSTR |
yes cast1 |
yes cast |
yes cast1 |
yes cast |
yes2 |
yes |
yes |
_variant_t |
BSTR |
no |
no |
no |
cast to _bstr_t3 |
cast to _bstr_t3 |
yes |
yes |
string |
MBCS |
no |
yes c_str() method |
no |
no |
no |
yes |
no |
wstring |
Unicode |
no |
no |
no |
yes c_str() method |
no |
no |
yes |
CComBSTR |
BSTR |
no |
no |
no |
yes cast to BSTR |
yes cast |
yes |
yes |
CComVariant |
BSTR |
no |
no |
no |
yes4 |
yes4 |
yes |
yes |
CString |
TCHAR |
no6 |
in MBCS builds, cast |
no6 |
in Unicode builds, cast |
no5 |
yes |
yes |
COleVariant |
BSTR |
no |
no |
no |
yes4 |
yes4 |
in MBCS builds |
in Unicode builds |
1、即使 _bstr_t 提供了向非常量指標的轉換操作符,修改底層的緩衝區也會已引起GPF如果你溢出了緩衝區或者造成記憶體流失。 2、_bstr_t 在內部用一個 wchar_t* 來儲存 BSTR,所以你可以使用 const wchar_t* 來訪問BSTR。這是一個實現細節,你可以小心的使用它,將來這個細節也許會改變。 3、如果資料不能轉換成BSTR會拋出一個異常。 4、使用 ChangeType(),然後訪問 VARIANT 的 bstrVal 成員。在MFC中,如果資料轉換不成功將會拋出異常。 5、這裡沒有轉換 BSTR 函數,然而 AllocSysString() 返回一個新的BSTR。 6、使用 GetBuffer() 方法,你可以暫時地得到一個非常量的TCHAR指標。 |