C++字串完全指引之三 —— 字串封裝類續

來源:互聯網
上載者:User
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指標。 
  •   

    相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.