C++字符串完全指引之二 —— 字符串封裝類
![]() 因為C語言風(fēng)格的字符串容易出錯且不易管理,黑客們甚至利用可能存在的緩沖區(qū)溢出bug把C語言風(fēng)格的字符串作為攻擊目標(biāo),所以出現(xiàn)了很多字符串封裝類。不幸的是,在某些場合下我們不知道該使用哪個字符串類,也不知道怎樣把一個C風(fēng)格的字符串轉(zhuǎn)換成一個字符串封裝類。 這篇文章將介紹所有在Win32 API, MFC, STL, WTL 和 Visual C++ 運行庫中出現(xiàn)的字符串類型。我將描述每一個類的用法,告訴大家怎樣創(chuàng)建每一個類的對象以及怎樣把一個類轉(zhuǎn)換成其他類。受控字符串和Visual C++ 7中的類兩部分是Nish完成的。 為了更好的從這篇文章中受益,你必須要明白不同的字符類型和編碼,這些內(nèi)容我在第一部分中介紹過。 Rule #1 of string classes 使用cast來實現(xiàn)類型轉(zhuǎn)換是不好的做法,除非有文檔明確指出這種轉(zhuǎn)換可以使用。 void SomeFunc ( LPCWSTR widestr );main(){ SomeFunc ( (LPCWSTR) "C:\\foo.txt" ); // WRONG!}肯定會失敗。它可以被編譯,因為cast操作會撤消編譯器的類型檢查。但是,編譯可以通過并不能說明代碼是正確的。 在下面的例子中,我將會指明cast在什么時候使用是合法的。 C-style strings and typedefs 正如我在第一部分中提到的,windows APIs 是用TCHARs來定義的,在編譯時,它可以根據(jù)你是否定義_MBCS或者_(dá)UNICODE被編譯成MBCS或者Unicode字符。你可以參看第一部分中對TCHAR的完整描述,這里為了方便,我列出了字符的typedefs
一個增加的字符類型是OLETYPE。它表示自動化接口(如word提供的可以使你操作文檔的接口)中使用的字符類型。這種類型一般被定義成wchar_t,然而如果你定義了OLE2ANSI預(yù)處理標(biāo)記,OLECHAR將會被定義成char類型。我知道現(xiàn)在已經(jīng)沒有理由定義OLE2ANSI(從MFC3以后,微軟已經(jīng)不使用它了),所以從現(xiàn)在起我將把OLECHAR當(dāng)作Unicode字符。
還有兩個用于包圍字符串和字符常量的宏定義,它們可以使同樣的代碼被用于MBCS和Unicode builds :
在文檔或例程中,你還會看到好多_T的變體。有四個等價的宏定義,它們是TEXT, _TEXT, __TEXT和__T,它們都起同樣的做用。 ![]() 很多自動化和COM接口使用BSTR來定義字符串。BSTRs中有幾個"陷阱",所以這里我用單獨的部分來說明它。 BSTR 是 Pascal-style 字符串(字符串長度被明確指出)和C-style字符串(字符串的長度要通過尋找結(jié)束符來計算)的混合產(chǎn)物。一個BSTR是一個Unicode字符串,它的長度是預(yù)先考慮的,并且它還有一個0字符作為結(jié)束標(biāo)記。下面是一個BSTR的示例:
注意字符串的長度是如何被加到字符串?dāng)?shù)據(jù)中的。長度是DWORD類型的,保存了字符串中包含的字節(jié)數(shù),但不包括結(jié)束標(biāo)記。在這個例子中,"Bob"包含3個Unicode字符(不包括結(jié)束符),總共6個字節(jié)。字符串的長度被預(yù)先存儲好,以便當(dāng)一個BSTR在進(jìn)程或者計算機(jī)之間被傳遞時,COM庫知道多少數(shù)據(jù)需要傳送。(另一方面,一個BSTR能夠存儲任意數(shù)據(jù)塊,而不僅僅是字符,它還可以包含嵌入在數(shù)據(jù)中的0字符。然而,由于這篇文章的目的,我將不考慮那些情況)。 BSTR bstr = NULL; bstr = SysAllocString ( L"Hi Bob!" ); if ( NULL == bstr ) // out of memory error // Use bstr here... SysFreeString ( bstr );自然的,各種各樣的BSTR封裝類為你實現(xiàn)內(nèi)存管理。 另外一個用在自動化接口中的變量類型是VARIANT。它被用來在無類型(typeless)語言,如Jscript和VBScript,來傳遞數(shù)據(jù)。一個VARIANT可能含有很多不同類型的數(shù)據(jù),例如long和IDispatch*。當(dāng)一個VARIANT包含一個字符串,字符串被存成一個BSTR。當(dāng)我后面講到VARIANT封裝類時,我會對VARIANT多些介紹。 ![]() 到目前為止,我已經(jīng)介紹了各種各樣的字符串。下面,我將說明封裝類。對于每個封裝類,我將展示怎樣創(chuàng)建一個對象及怎樣把它轉(zhuǎn)換成一個C語言風(fēng)格的字符串指針。C語言風(fēng)格的字符串指針對于API的調(diào)用,或者創(chuàng)建一個不同的字符串類對象經(jīng)常是必需的。我不會介紹字符串類提供的其他操作,比如排序和比較。 重復(fù)一遍,除非你確切的明白結(jié)果代碼將會做什么,否則不要盲目地使用cast來實現(xiàn)類型轉(zhuǎn)換。 ![]() _bstr_t _bstr_t是一個對BSTR的完整封裝類,實際上它隱藏了底層的BSTR。它提供各種構(gòu)造函數(shù)和操作符來訪問底層的C語言風(fēng)格的字符串。然而,_bstr_t卻沒有訪問BSTR本身的操作符,所以一個_bstr_t類型的字符串不能被作為輸出參數(shù)傳給一個COM方法。如果你需要一個BSTR*參數(shù),使用ATL類CComBSTR是比較容易的方式。 一個_bstr_t字符串能夠傳給一個接收參數(shù)類型為BSTR的函數(shù),只是因為下列3個條件同時滿足。首先,_bstr_t有一個向wchar_t*轉(zhuǎn)換的轉(zhuǎn)換函數(shù);其次,對編譯器而言,因為BSTR的定義,wchar_t*和BSTR有同樣的含義;第三,_bstr_t內(nèi)部含有的wchar_t*指向一片按BSTR的形式存儲數(shù)據(jù)的內(nèi)存。所以,即使沒有文檔說明,_bstr_t可以轉(zhuǎn)換成BSTR,這種轉(zhuǎn)換仍然可以正常進(jìn)行。 // Constructing_bstr_t bs1 = "char string"; // construct from a LPCSTR_bstr_t bs2 = L"wide char string"; // construct from a LPCWSTR_bstr_t bs3 = bs1; // copy from another _bstr_t_variant_t v = "Bob";_bstr_t bs4 = v; // construct from a _variant_t that has a string // Extracting dataLPCSTR psz1 = bs1; // automatically converts to MBCS stringLPCSTR psz2 = (LPCSTR) bs1; // cast OK, same as previous lineLPCWSTR pwsz1 = bs1; // returns the internal Unicode stringLPCWSTR pwsz2 = (LPCWSTR) bs1; // cast OK, same as previous lineBSTR bstr = bs1.copy(); // copies bs1, returns it as a BSTR // ...SysFreeString ( bstr );注意_bstr_t也提供char*和wchar_t*之間的轉(zhuǎn)換操作符。這是一個值得懷疑的設(shè)計,因為即使它們是非常量字符串指針,你也一定不能使用這些指針去修改它們指向的緩沖區(qū)的內(nèi)容,因為那將破壞內(nèi)部的BSTR結(jié)構(gòu)。 _variant_t _variant_t是一個對VARIANT的完整封裝,它提供很多構(gòu)造函數(shù)和轉(zhuǎn)換函數(shù)來操作一個VARIANT可能包含的大量的數(shù)據(jù)類型。這里,我將只介紹與字符串有關(guān)的操作。 // Constructing_variant_t v1 = "char string"; // construct from a LPCSTR_variant_t v2 = L"wide char string"; // construct from a LPCWSTR_bstr_t bs1 = "Bob";_variant_t v3 = bs1; // copy from a _bstr_t object // Extracting data_bstr_t bs2 = v1; // extract BSTR from the VARIANT_bstr_t bs3 = (_bstr_t) v1; // cast OK, same as previous line注意: 如果類型轉(zhuǎn)換不能被執(zhí)行,_variant_t方法能夠拋出異常,所以應(yīng)該準(zhǔn)備捕獲_com_error異常。 還需要注意的是: 沒有從一個_variant_t變量到一個MBCS字符串的直接轉(zhuǎn)換。你需要創(chuàng)建一個臨時的_bstr_t變量,使用提供Unicode到MBCS轉(zhuǎn)換的另一個字符串類或者使用一個ATL轉(zhuǎn)換宏。 不像_bstr_t,一個_variant_t變量可以被直接作為參數(shù)傳遞給一個COM方法。_variant_t 繼承自VARIANT類型,所以傳遞一個_variant_t來代替VARIANT變量是C++語言所允許的。 ![]() STL只有一個字符串類,basic_string。一個basic_string管理一個以0做結(jié)束符的字符串?dāng)?shù)組。字符的類型是basic_string模般的參數(shù)??偟膩碚f,一個basic_string類型的變量應(yīng)該被當(dāng)作不透明的對象。你可以得到一個指向內(nèi)部緩沖區(qū)的只讀指針,但是任何寫操作必須使用basic_string的操作符和方法。 basic_string有兩個預(yù)定義的類型:包含char的string類型和包含wchar_t的wstring類型。這里沒有內(nèi)置的包含TCHAR的類型,但是你可以使用下面列出的代碼來實現(xiàn)。 // Specializationstypedef basic_string不像_bstr_t,一個basic_string變量不能在字符集之間直接轉(zhuǎn)換。然而,你可以傳遞由c_str()返回的指針給另外一個類的構(gòu)造函數(shù)(如果這個類的構(gòu)造函數(shù)接受這種字符類型)。例如: // Example, construct _bstr_t from basic_string_bstr_t bs1 = str.c_str(); // construct a _bstr_t from a LPCSTR_bstr_t bs2 = wstr.c_str(); // construct a _bstr_t from a LPCWSTR ![]() CComBSTR CComBSTR 是 ATL 中的 BSTR 封裝類,它在某些情況下比_bstr_t有用的多。最引人注意的是CComBSTR允許訪問底層的BSTR,這意味著你可以傳遞一個CComBSTR對象給COM的方法。CComBSTR對象能夠替你自動的管理BSTR的內(nèi)存。例如,假設(shè)你想調(diào)用下面這個接口的方法: // Sample interface:struct IStuff : public IUnknown{ // Boilerplate COM stuff omitted... STDMETHOD(SetText)(BSTR bsText); STDMETHOD(GetText)(BSTR* pbsText);};CComBSTR有一個操作符--BSTR方法,所以它能直接被傳給SetText()函數(shù)。還有另外一個操作--&,這個操作符返回一個BSTR*。所以,你可以對一個CComBSTR對象使用&操作符,然后把它傳給需要BSTR*參數(shù)的函數(shù)。 CComBSTR bs1;CComBSTR bs2 = "new text"; pStuff->GetText ( &bs1 ); // ok, takes address of internal BSTR pStuff->SetText ( bs2 ); // ok, calls BSTR converter pStuff->SetText ( (BSTR) bs2 ); // cast ok, same as previous lineCComBSTR有和_bstr_t相似的構(gòu)造函數(shù),然而卻沒有內(nèi)置的向MBCS字符串轉(zhuǎn)換的函數(shù)。因此,你需要使用一個ATL轉(zhuǎn)換宏。 // ConstructingCComBSTR bs1 = "char string"; // construct from a LPCSTRCComBSTR bs2 = L"wide char string"; // construct from a LPCWSTRCComBSTR bs3 = bs1; // copy from another CComBSTRCComBSTR bs4; bs4.LoadString ( IDS_SOME_STR ); // load string from string table// Extracting dataBSTR bstr1 = bs1; // returns internal BSTR, but don‘‘t modify it!BSTR bstr2 = (BSTR) bs1; // cast ok, same as previous lineBSTR bstr3 = bs1.Copy(); // copies bs1, returns it as a BSTRBSTR bstr4; bstr4 = bs1.Detach(); // bs1 no longer manages its BSTR // ... SysFreeString ( bstr3 ); SysFreeString ( bstr4 );注意在上個例子中使用了Detach()方法。調(diào)用這個方法后,CComBSTR對象不再管理它的BSTR字符串或者說它對應(yīng)的內(nèi)存。這就是bstr4需要調(diào)用SysFreeString()的原因。 做一個補充說明:重載的&操作符意味著在一些STL容器中你不能直接使用CComBSTR變量,比如list。容器要求&操作符返回一個指向容器包含的類的指針,但是對CComBSTR變量使用&操作符返回的是BSTR*,而不是CComBSTR*。然而,有一個ATL類可以解決這個問題,這個類是CAdapt。例如,你可以這樣聲明一個CComBSTR的list: std::list< CAdapt<CComBSTR> > bstr_list; CAdapt提供容器所需要的操作符,但這些操作符對你的代碼是透明的。你可以把一個bstr_list當(dāng)作一個CComBSTR的list來使用。 // ConstructingCComVariant v1 = "char string"; // construct from a LPCSTRCComVariant v2 = L"wide char string"; // construct from a LPCWSTRCComBSTR bs1 = "BSTR bob";CComVariant v3 = (BSTR) bs1; // copy from a BSTR // Extracting dataCComBSTR bs2 = v1.bstrVal; // extract BSTR from the VARIANT不像_variant_t,這里沒有提供針對VARIANT包含的各種類型的轉(zhuǎn)換操作符。正如上面介紹的,你必須直接訪問VARIANT的成員并且確保這個VARIANT變量保存著你期望的類型。如果你需要把一個CComVariant類型的數(shù)據(jù)轉(zhuǎn)換成一個BSTR類型的數(shù)據(jù),你可以調(diào)用ChangeType()方法。 CComVariant v4 = ... // Init v4 from somewhereCComBSTR bs3; if ( SUCCEEDED( v4.ChangeType ( VT_BSTR ) )) bs3 = v4.bstrVal;像_variant_t一樣,CComVariant也沒有提供向MBCS字符串轉(zhuǎn)換的轉(zhuǎn)換操作。你需要創(chuàng)建一個_bstr_t類型的中間變量,使用提供從Unicode到MBCS轉(zhuǎn)換的另一個字符串類,或者使用一個ATL的轉(zhuǎn)換宏。 ![]() ATL:轉(zhuǎn)換宏是各種字符編碼之間進(jìn)行轉(zhuǎn)換的一種很方便的方式,在函數(shù)調(diào)用時,它們顯得非常有用。ATL轉(zhuǎn)換宏的名稱是根據(jù)下面的模式來命名的[源類型]2[新類型]或者[源類型]2C[新類型]。據(jù)有第二種形式的名字的宏的轉(zhuǎn)換結(jié)果是常量指針(對應(yīng)名字中的"C")。各種類型的簡稱如下: A: MBCS string, char* (A for ANSI)W: Unicode string, wchar_t* (W for wide)T: TCHAR string, TCHAR*OLE: OLECHAR string, OLECHAR* (in practice, equivalent to W)BSTR: BSTR (used as the destination type only) 所以,W2A()宏把一個Unicode字符串轉(zhuǎn)換成一個MBCS字符串。T2CW()宏把一個TCHAR字符串轉(zhuǎn)轉(zhuǎn)成一個Unicode字符串常量。 // Functions taking various strings:void Foo ( LPCWSTR wstr );void Bar ( BSTR bstr );// Functions returning strings:void Baz ( BSTR* pbstr );#include <atlconv.h>main(){using std::string;USES_CONVERSION; // declare locals used by the ATL macros// Example 1: Send an MBCS string to Foo()LPCSTR psz1 = "Bob";string str1 = "Bob"; Foo ( A2CW(psz1) ); Foo ( A2CW(str1.c_str()) ); // Example 2: Send a MBCS and Unicode string to Bar()LPCSTR psz2 = "Bob";LPCWSTR wsz = L"Bob";BSTR bs1;CComBSTR bs2; bs1 = A2BSTR(psz2); // create a BSTR bs2.Attach ( W2BSTR(wsz) ); // ditto, assign to a CComBSTR Bar ( bs1 ); Bar ( bs2 ); SysFreeString ( bs1 ); // free bs1 memory // No need to free bs2 since CComBSTR will do it for us. // Example 3: Convert the BSTR returned by Baz()BSTR bs3 = NULL;string str2; Baz ( &bs3 ); // Baz() fills in bs3 str2 = W2CA(bs3); // convert to an MBCS string SysFreeString ( bs3 ); // free bs3 memory}正如你所看見的,當(dāng)你有一個和函數(shù)所需的參數(shù)類型不同的字符串時,使用這些轉(zhuǎn)換宏是非常方便的。 ![]() CString 因為一個MFC CString類的對象包含TCHAR類型的字符,所以確切的字符類型取決于你所定義的預(yù)處理符號。大體來說,CString 很像STL string,這意味著你必須把它當(dāng)成不透明的對象,只能使用CString提供的方法來修改CString對象。CString有一個string所不具備的優(yōu)點:CString具有接收MBCS和Unicode兩種字符串的構(gòu)造函數(shù),它還有一個LPCTSTR轉(zhuǎn)換符,所以你可以把CString對象直接傳給一個接收LPCTSTR的函數(shù)而不需要調(diào)用c_str()函數(shù)。 // 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的一個構(gòu)造函數(shù)和LoadString()函數(shù)可以完成它。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, ... );第一個構(gòu)造函數(shù)看起來有點奇怪,但是這實際上是文檔說明的裝入一個字符串的方法。 注意,對一個CString變量,你可以使用的唯一合法轉(zhuǎn)換符是LPCTSTR。轉(zhuǎn)換成LPTSTR(非常量指針)是錯誤的。養(yǎng)成把一個CString變量轉(zhuǎn)換成LPTSTR的習(xí)慣將會給你帶來傷害,因為當(dāng)你的程序后來崩潰時,你可能不知道為什么,因為你到處都使用同樣的代碼而那時它們都恰巧正常工作。正確的得到一個指向緩沖區(qū)的非常量指針的方法是調(diào)用GetBuffer()方法。下面是正確的用法的一個例子,這段代碼是給一個列表控件中的項設(shè)定文字: 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 strpszText成員是一個LPTSTR變量,一個非常量指針,因此你需要對str調(diào)用GetBuffer()。GetBuffer()的參數(shù)是你需要CString為緩沖區(qū)分配的最小長度。如果因為某些原因,你需要一個可修改的緩沖區(qū)來存放1K TCHARs,你需要調(diào)用GetBuffer(1024)。把0作為參數(shù)時,GetBuffer()返回的是指向字符串當(dāng)前內(nèi)容的指針。 上面劃線的語句可以被編譯,在這種情況下,甚至可以正常起作用。但這并不意味著這行代碼是正確的。通過使用非常量轉(zhuǎn)換,你已經(jīng)破壞了面向?qū)ο蟮姆庋b,并對CString的內(nèi)部實現(xiàn)作了某些假定。如果你有這樣的轉(zhuǎn)換習(xí)慣,你終將會陷入代碼崩潰的境地。你會想代碼為什么不能正常工作了,因為你到處都使用同樣的代碼而那些代碼看起來是正確的。 你知道人們總是抱怨現(xiàn)在的軟件的bug是多么的多嗎?軟件中的bug是因為程序員寫了不正確的代碼。難道你真的想寫一些你知道是錯誤的代碼來為所有的軟件都滿是bug這種認(rèn)識做貢獻(xiàn)嗎?花些時間來學(xué)習(xí)使用CString的正確方法讓你的代碼在任何時間都正常工作把。 CString 有兩個函數(shù)來從一個 CString 創(chuàng)建一個 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的函數(shù)。然而,不像CComVariant,COleVariant只有一個LPCTSTR構(gòu)造函數(shù)。沒有對LPCSTR 和LPCWSTR的構(gòu)造函數(shù)。在大多數(shù)情況下這不是一個問題,因為不管怎樣你的字符串很可能是LPCTSTRs,但這是一個需要意識到的問題。COleVariant還有一個接收CString參數(shù)的構(gòu)造函數(shù)。 // ConstructingCString s1 = _T("tchar string");COleVariant v1 = _T("Bob"); // construct from an LPCTSTRCOleVariant v2 = s1; // copy from a CString像CComVariant一樣,你必須直接訪問VARIANT的成員。如果需要把VARIANT轉(zhuǎn)換成一個字符串,你應(yīng)該使用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 ); ![]() CString WTL的CString的行為和MFC的 CString完全一樣,所以你可以參考上面關(guān)于MFC的 CString的介紹。 ![]() System::String是用來處理字符串的.NET類。在內(nèi)部,一個String對象包含一個不可改變的字符串序列。任何對String對象的操作實際上都是返回了一個新的String對象,因為原始的對象是不可改變的。String的一個特性是如果你有不止一個String對象包含相同的字符序列,它們實際上是指向相同的對象的。相對于C++的使用擴(kuò)展是增加了一個新的字符串常量前綴S,S用來代表一個受控的字符串常量(a managed string literal)。 // ConstructingString* ms = S"This is a nice managed string";你可以傳遞一個非受控的字符串來創(chuàng)建一個String對象,但是樣會比使用受控字符串來創(chuàng)建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之間的轉(zhuǎn)換是很容易的。CString有一個向LPCTSTR的轉(zhuǎn)換操作,而String有兩個接收char* 和 wchar_t*的構(gòu)造函數(shù),因此你可以把一個CString變量直接傳給一個String的構(gòu)造函數(shù)。 CString s1 ( "hello world" );String* s2 ( s1 ); // copy from a CString反方向的轉(zhuǎn)換也很類似 String* s1 = S"Three cats";CString s2 ( s1 );這也許會使你感到一點迷惑,但是它確實是起作用的。因為從VS.NET 開始,CString 有了一個接收String 對象的構(gòu)造函數(shù)。 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* ,我們需要固定它,否則垃圾收集器或許會在我們正在管理它的內(nèi)容的時候移動了它。 ![]() 當(dāng)你在printf()或者類似的函數(shù)中使用字符串封裝類時你必須十分小心。這些函數(shù)包括sprintf()和它的變體,還有TRACE和ATLTRACE宏。因為這些函數(shù)沒有對添加的參數(shù)的類型檢查,你必須小心,只能傳給它們C語言風(fēng)格的字符串指針,而不是一個完整的字符串類。 例如,要把一個_bstr_t 字符串傳給ATLTRACE(),你必須使用顯式轉(zhuǎn)換(LPCSTR) 或者(LPCWSTR): _bstr_t bs = L"Bob!";ATLTRACE("The string is: %s in line %d\n", (LPCSTR) bs, nLine); 如果你忘了使用轉(zhuǎn)換符而把整個_bstr_t對象傳給了函數(shù),將會顯示一些毫無意義的輸出,因為_bstr_t保存的內(nèi)部數(shù)據(jù)會全部被輸出。 ![]() 兩個字符串類之間進(jìn)行轉(zhuǎn)換的常用方式是:先把源字符串轉(zhuǎn)換成一個C語言風(fēng)格的字符串指針,然后把這個指針傳遞給目的類型的構(gòu)造函數(shù)。下面這張表顯示了怎樣把一個字符串轉(zhuǎn)換成一個C語言風(fēng)格的字符串指針以及哪些類具有接收C語言風(fēng)格的字符串指針的構(gòu)造函數(shù)。
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() Michael Dunn: Michael Dunn居住在陽光城市洛杉磯。他是如此的喜歡這里的天氣以致于想一生都住在這里。他在4年級時開始編程,那時用的電腦是Apple //e。1995年,在UCLA獲得數(shù)學(xué)學(xué)士學(xué)位,隨后在Symantec公司做QA工程師,在 Norton AntiVirus 組工作。他自學(xué)了 Windows 和 MFC 編程。1999-2000年,他設(shè)計并實現(xiàn)了 Norton AntiVirus的新界面。 Michael 現(xiàn)在在 Napster(一個提供在線訂閱音樂服務(wù)的公司)做開發(fā)工作,他還開發(fā)了UltraBar,一個IE工具欄插件,它可以使網(wǎng)絡(luò)搜索更加容易,給了 googlebar 以沉重打擊;他還開發(fā)了 CodeProject SearchBar;與人共同創(chuàng)建了 Zabersoft 公司,該公司在洛杉磯和丹麥的 Odense 都設(shè)有辦事處。 他喜歡玩游戲。愛玩的游戲有 pinball, bike riding,偶爾還玩 PS, Dreamcasth 和 MAME 游戲。他因忘了自己曾經(jīng)學(xué)過的語言:法語、漢語、日語而感到悲哀。 Nishant S(Nish): Nish是來自印度 Trivandrum,的 Microsoft Visual C++ MVP。他從1990年開始編碼?,F(xiàn)在,Nish為作為合同雇員在家里為 CodeProject 工作?! ?br> 他還寫了一部浪漫戲劇《 Nish 還計劃好 |