国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
c++字符串完全指引之二 —— 字符串封裝類 - 悠悠我心

C++字符串完全指引之二 —— 字符串封裝類


原著:Michael Dunn

作者:Chengjie Sun


原文出處:CodeProject:The Complete Guide to C++ Strings, Part II


引言

  因為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)換可以使用。
促使我寫這兩篇文章的原因是字符串類型轉(zhuǎn)換中經(jīng)常遇到的一些問題。當(dāng)我們使用cast把字符串從類型X轉(zhuǎn)換到類型Z的時候,我們不知道為什么代碼不能正常工作。各種各樣的字符串類型,尤其是BSTR,幾乎沒有在任何一個地方的文檔中被明確的指出可以用cast來實現(xiàn)類型轉(zhuǎn)換。所以我想一些人可能會使用cast來實現(xiàn)類型轉(zhuǎn)換并希望這種轉(zhuǎn)換能夠正常工作。
  除非源字符串是一個被明確指明支持轉(zhuǎn)換操作符的字符串包裝類,否則cast不對字符串做任何轉(zhuǎn)換。對常量字符串使用cast不會起到任何作用,所以下面的代碼:

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

TypeMeaning
WCHARUnicode character (wchar_t)
TCHARMBCS or Unicode character, depending on preprocessor settings
LPSTR string of char (char*)
LPCSTRconstant string of char (const char*)
LPWSTR string of WCHAR (WCHAR*)
LPCWSTR constant string of WCHAR (const WCHAR*)
LPTSTR string of TCHAR (TCHAR*)
LPCTSTR constant string of TCHAR (const TCHAR*)

  一個增加的字符類型是OLETYPE。它表示自動化接口(如word提供的可以使你操作文檔的接口)中使用的字符類型。這種類型一般被定義成wchar_t,然而如果你定義了OLE2ANSI預(yù)處理標(biāo)記,OLECHAR將會被定義成char類型。我知道現(xiàn)在已經(jīng)沒有理由定義OLE2ANSI(從MFC3以后,微軟已經(jīng)不使用它了),所以從現(xiàn)在起我將把OLECHAR當(dāng)作Unicode字符。
這里給出你將會看到的一些OLECHAR相關(guān)的typedefs:

TypeMeaning
OLECHAR Unicode character (wchar_t)
LPOLESTR string of OLECHAR (OLECHAR*)
LPCOLESTR constant string of OLECHAR (const OLECHAR*)

  還有兩個用于包圍字符串和字符常量的宏定義,它們可以使同樣的代碼被用于MBCS和Unicode builds :

Type Meaning
_T(x)Prepends L to the literal in Unicode builds.
OLESTR(x)Prepends L to the literal to make it an LPCOLESTR.

  在文檔或例程中,你還會看到好多_T的變體。有四個等價的宏定義,它們是TEXT, _TEXT, __TEXT和__T,它們都起同樣的做用。

COM 中的字符串 —— BSTR 和 VARIANT

  很多自動化和COM接口使用BSTR來定義字符串。BSTRs中有幾個"陷阱",所以這里我用單獨的部分來說明它。
  BSTR 是 Pascal-style 字符串(字符串長度被明確指出)和C-style字符串(字符串的長度要通過尋找結(jié)束符來計算)的混合產(chǎn)物。一個BSTR是一個Unicode字符串,它的長度是預(yù)先考慮的,并且它還有一個0字符作為結(jié)束標(biāo)記。下面是一個BSTR的示例:

 

06 00 00 0042 006F 0062 0000 00
--length--BobEOS

  注意字符串的長度是如何被加到字符串?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字符。然而,由于這篇文章的目的,我將不考慮那些情況)。
  在 C++ 中,一個 BSTR 實際上就是一個指向字符串中第一個字符的指針。它的定義如下:

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)換。

CRT提供的類

_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 類
  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 tstring; // string of TCHARs // Constructingstring str = "char string";         // construct from a LPCSTRwstring wstr = L"wide char string"; // construct from a LPCWSTRtstring tstr = _T("TCHAR string");  // construct from a LPCTSTR // Extracting dataLPCSTR psz = str.c_str();    // read-only pointer to str‘‘s bufferLPCWSTR pwsz = wstr.c_str(); // read-only pointer to wstr‘‘s bufferLPCTSTR ptsz = tstr.c_str(); // read-only pointer to tstr‘‘s buffer
  不像_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      
ATL 類

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 line      
  CComBSTR有和_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來使用。

CComVariant
  CComVariant是VARIANT的封裝類。然而,不像_variant_t,在CComVariant中VARIANT沒有被隱藏。事實上你需要直接訪問VARIANT的成員。CComVariant提供了很多構(gòu)造函數(shù)來對VARIANT能夠包含的多種類型進(jìn)行處理。這里,我將只介紹和字符串相關(guān)的操作。

// 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)換宏

  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字符串常量。
  為了使用這些宏,需要先包含atlconv.h頭文件。你甚至可以在非ATL工程中包含這個頭文件來使用其中定義的宏,因為這個頭文件獨立于ATL中的其他部分,不需要一個_Module全局變量。當(dāng)你在一個函數(shù)中使用轉(zhuǎn)換宏時,需要把USES_CONVERSION宏放在函數(shù)的開頭。它定義了轉(zhuǎn)換宏所需的一些局部變量。
  當(dāng)轉(zhuǎn)換的目的類型是除了BSTR以外的其他類型時,被轉(zhuǎn)換的字符串是存在棧中的。所以,如果你想讓字符串的生命周期比當(dāng)前的函數(shù)長,你需要把這個字符串拷貝到其他的字符串類中。當(dāng)目的類型是BSTR時,內(nèi)存不會自動被釋放,你必須把返回值賦給一個BSTR變量或者一個BSTR封裝類以避免內(nèi)存泄漏。
  下面是一些各種轉(zhuǎn)換宏的使用例子:

// 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)換宏是非常方便的。

MFC類

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 str      
  pszText成員是一個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 );      

WTL 類

CString
  WTL的CString的行為和MFC的 CString完全一樣,所以你可以參考上面關(guān)于MFC的 CString的介紹。

CLR 和 VC 7 類

  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)容的時候移動了它。

在 printf-style 格式函數(shù)中使用字符串類

  當(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ù)會全部被輸出。

所有類的總結(jié)

  兩個字符串類之間進(jìn)行轉(zhuǎn)換的常用方式是:先把源字符串轉(zhuǎn)換成一個C語言風(fēng)格的字符串指針,然后把這個指針傳遞給目的類型的構(gòu)造函數(shù)。下面這張表顯示了怎樣把一個字符串轉(zhuǎn)換成一個C語言風(fēng)格的字符串指針以及哪些類具有接收C語言風(fēng)格的字符串指針的構(gòu)造函數(shù)。

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_tBSTRyes cast1yes castyes cast1yes castyes2yesyes
_variant_tBSTRnononocast to
_bstr_t3
cast to
_bstr_t3
yesyes
stringMBCSnoyes c_str() methodnononoyesno
wstringUnicodenononoyes c_str() methodnonoyes
CComBSTRBSTRnononoyes cast to BSTRyes castyesyes
CComVariantBSTRnononoyes4yes4yesyes
CString TCHARno6in MBCS
builds, cast
no6in Unicode
builds, cast
no5yesyes
COleVariantBSTRnononoyes4yes4in MBCS
builds
in Unicode
builds
  • 1、即使 _bstr_t 提供了向非常量指針的轉(zhuǎn)換操作符,修改底層的緩沖區(qū)也會已引起GPF如果你溢出了緩沖區(qū)或者造成內(nèi)存泄漏。
  • 2、_bstr_t 在內(nèi)部用一個 wchar_t* 來保存 BSTR,所以你可以使用 const wchar_t* 來訪問BSTR。這是一個實現(xiàn)細(xì)節(jié),你可以小心的使用它,將來這個細(xì)節(jié)也許會改變。
  • 3、如果數(shù)據(jù)不能轉(zhuǎn)換成BSTR會拋出一個異常。
  • 4、使用 ChangeType(),然后訪問 VARIANT 的 bstrVal 成員。在MFC中,如果數(shù)據(jù)轉(zhuǎn)換不成功將會拋出異常。
  • 5、這里沒有轉(zhuǎn)換 BSTR 函數(shù),然而 AllocSysString() 返回一個新的BSTR。
  • 6、使用 GetBuffer() 方法,你可以暫時地得到一個非常量的TCHAR指針。

  • 作者簡介

    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>  他還寫了一部浪漫戲劇《Summer Love and Some more Cricket》和一本編程書籍《Extending MFC applications with the .NET Framework》。他還管理者M(jìn)VP的一個網(wǎng)站http://www.voidnish.com/ 。在這個網(wǎng)站上,你可以看到他的很多關(guān)于編程方面的思想和文章。
    Nish 還計劃好旅游,他希望自一生中能夠到達(dá)地球上盡可能多的地方。
    本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報
    打開APP,閱讀全文并永久保存 查看更多類似文章
    猜你喜歡
    類似文章
    VC常用數(shù)據(jù)類型列表
    常用字符串長度計算函數(shù) - 游戲程序設(shè)計 - 云世界日志
    C 編程的 42 條建議(四)
    C++_數(shù)據(jù)類型轉(zhuǎn)換技巧
    VC常用數(shù)據(jù)類型使用轉(zhuǎn)換詳解
    BSTR、LPSTR和LPWSTR 等多種VC字符類型分析及各種字符類型轉(zhuǎn)換
    更多類似文章 >>
    生活服務(wù)
    分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
    綁定賬號成功
    后續(xù)可登錄賬號暢享VIP特權(quán)!
    如果VIP功能使用有故障,
    可點擊這里聯(lián)系客服!

    聯(lián)系客服