青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆-4  評論-40  文章-117  trackbacks-0

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


原著:Michael Dunn

作者:Chengjie Sun


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



引言

  因為C語言風格的字符串容易出錯且不易管理,黑客們甚至利用可能存在的緩沖區溢出bug把C語言風格的字符串作為攻擊目標,所以出現了很多字符串封裝類。不幸的是,在某些場合下我們不知道該使用哪個字符串類,也不知道怎樣把一個C風格的字符串轉換成一個字符串封裝類。
  這篇文章將介紹所有在Win32 API, MFC, STL, WTL 和 Visual C++ 運行庫中出現的字符串類型。我將描述每一個類的用法,告訴大家怎樣創建每一個類的對象以及怎樣把一個類轉換成其他類。受控字符串和Visual C++ 7中的類兩部分是Nish完成的。
  為了更好的從這篇文章中受益,你必須要明白不同的字符類型和編碼,這些內容我在第一部分中介紹過。

Rule #1 of string classes

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

void SomeFunc ( LPCWSTR widestr );
            main()
            {
            SomeFunc ( (LPCWSTR) "C:\\foo.txt" );  // WRONG!
            }      
  肯定會失敗。它可以被編譯,因為cast操作會撤消編譯器的類型檢查。但是,編譯可以通過并不能說明代碼是正確的。
  在下面的例子中,我將會指明cast在什么時候使用是合法的。
C-style strings and typedefs

  正如我在第一部分中提到的,windows APIs 是用TCHARs來定義的,在編譯時,它可以根據你是否定義_MBCS或者_UNICODE被編譯成MBCS或者Unicode字符。你可以參看第一部分中對TCHAR的完整描述,這里為了方便,我列出了字符的typedefs

Type Meaning
WCHAR Unicode character (wchar_t)
TCHAR MBCS or Unicode character, depending on preprocessor settings
LPSTR  string of char (char*)
LPCSTR constant 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預處理標記,OLECHAR將會被定義成char類型。我知道現在已經沒有理由定義OLE2ANSI(從MFC3以后,微軟已經不使用它了),所以從現在起我將把OLECHAR當作Unicode字符。
這里給出你將會看到的一些OLECHAR相關的typedefs:

Type Meaning
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字符串(字符串的長度要通過尋找結束符來計算)的混合產物。一個BSTR是一個Unicode字符串,它的長度是預先考慮的,并且它還有一個0字符作為結束標記。下面是一個BSTR的示例:

 

06 00 00 00 42 00 6F 00 62 00 00 00
--length-- B o b EOS

  注意字符串的長度是如何被加到字符串數據中的。長度是DWORD類型的,保存了字符串中包含的字節數,但不包括結束標記。在這個例子中,"Bob"包含3個Unicode字符(不包括結束符),總共6個字節。字符串的長度被預先存儲好,以便當一個BSTR在進程或者計算機之間被傳遞時,COM庫知道多少數據需要傳送。(另一方面,一個BSTR能夠存儲任意數據塊,而不僅僅是字符,它還可以包含嵌入在數據中的0字符。然而,由于這篇文章的目的,我將不考慮那些情況)。
  在 C++ 中,一個 BSTR 實際上就是一個指向字符串中第一個字符的指針。它的定義如下:

BSTR bstr = NULL;
            bstr = SysAllocString ( L"Hi Bob!" );
            if ( NULL == bstr )
            // out of memory error
            // Use bstr here...
            SysFreeString ( bstr );      
自然的,各種各樣的BSTR封裝類為你實現內存管理。
  另外一個用在自動化接口中的變量類型是VARIANT。它被用來在無類型(typeless)語言,如Jscript和VBScript,來傳遞數據。一個VARIANT可能含有很多不同類型的數據,例如long和IDispatch*。當一個VARIANT包含一個字符串,字符串被存成一個BSTR。當我后面講到VARIANT封裝類時,我會對VARIANT多些介紹。

字符串封裝類

  到目前為止,我已經介紹了各種各樣的字符串。下面,我將說明封裝類。對于每個封裝類,我將展示怎樣創建一個對象及怎樣把它轉換成一個C語言風格的字符串指針。C語言風格的字符串指針對于API的調用,或者創建一個不同的字符串類對象經常是必需的。我不會介紹字符串類提供的其他操作,比如排序和比較。
  重復一遍,除非你確切的明白結果代碼將會做什么,否則不要盲目地使用cast來實現類型轉換。

CRT提供的類

_bstr_t
  _bstr_t是一個對BSTR的完整封裝類,實際上它隱藏了底層的BSTR。它提供各種構造函數和操作符來訪問底層的C語言風格的字符串。然而,_bstr_t卻沒有訪問BSTR本身的操作符,所以一個_bstr_t類型的字符串不能被作為輸出參數傳給一個COM方法。如果你需要一個BSTR*參數,使用ATL類CComBSTR是比較容易的方式。
  一個_bstr_t字符串能夠傳給一個接收參數類型為BSTR的函數,只是因為下列3個條件同時滿足。首先,_bstr_t有一個向wchar_t*轉換的轉換函數;其次,對編譯器而言,因為BSTR的定義,wchar_t*和BSTR有同樣的含義;第三,_bstr_t內部含有的wchar_t*指向一片按BSTR的形式存儲數據的內存。所以,即使沒有文檔說明,_bstr_t可以轉換成BSTR,這種轉換仍然可以正常進行。
// 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 data
            LPCSTR psz1 = bs1;              // automatically converts to MBCS string
            LPCSTR psz2 = (LPCSTR) bs1;     // cast OK, same as previous line
            LPCWSTR pwsz1 = bs1;            // returns the internal Unicode string
            LPCWSTR pwsz2 = (LPCWSTR) bs1;  // cast OK, same as previous line
            BSTR    bstr = bs1.copy();      // copies bs1, returns it as a BSTR
            // ...
            SysFreeString ( bstr );      
  注意_bstr_t也提供char*和wchar_t*之間的轉換操作符。這是一個值得懷疑的設計,因為即使它們是非常量字符串指針,你也一定不能使用這些指針去修改它們指向的緩沖區的內容,因為那將破壞內部的BSTR結構。

_variant_t
  _variant_t是一個對VARIANT的完整封裝,它提供很多構造函數和轉換函數來操作一個VARIANT可能包含的大量的數據類型。這里,我將只介紹與字符串有關的操作。
// 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      
注意
  如果類型轉換不能被執行,_variant_t方法能夠拋出異常,所以應該準備捕獲_com_error異常。

還需要注意的是
  沒有從一個_variant_t變量到一個MBCS字符串的直接轉換。你需要創建一個臨時的_bstr_t變量,使用提供Unicode到MBCS轉換的另一個字符串類或者使用一個ATL轉換宏。
  不像_bstr_t,一個_variant_t變量可以被直接作為參數傳遞給一個COM方法。_variant_t
  繼承自VARIANT類型,所以傳遞一個_variant_t來代替VARIANT變量是C++語言所允許的。

STL 類
  STL只有一個字符串類,basic_string。一個basic_string管理一個以0做結束符的字符串數組。字符的類型是basic_string模般的參數??偟膩碚f,一個basic_string類型的變量應該被當作不透明的對象。你可以得到一個指向內部緩沖區的只讀指針,但是任何寫操作必須使用basic_string的操作符和方法。
  basic_string有兩個預定義的類型:包含char的string類型和包含wchar_t的wstring類型。這里沒有內置的包含TCHAR的類型,但是你可以使用下面列出的代碼來實現。
// Specializations
            typedef basic_string tstring; // string of TCHARs
            // Constructing
            string str = "char string";         // construct from a LPCSTR
            wstring wstr = L"wide char string"; // construct from a LPCWSTR
            tstring tstr = _T("TCHAR string");  // construct from a LPCTSTR
            // Extracting data
            LPCSTR psz = str.c_str();    // read-only pointer to str''s buffer
            LPCWSTR pwsz = wstr.c_str(); // read-only pointer to wstr''s buffer
            LPCTSTR ptsz = tstr.c_str(); // read-only pointer to tstr''s buffer
            
  不像_bstr_t,一個basic_string變量不能在字符集之間直接轉換。然而,你可以傳遞由c_str()返回的指針給另外一個類的構造函數(如果這個類的構造函數接受這種字符類型)。例如:
// 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的內存。例如,假設你想調用下面這個接口的方法:
// Sample interface:
            struct IStuff : public IUnknown
            {
            // Boilerplate COM stuff omitted...
            STDMETHOD(SetText)(BSTR bsText);
            STDMETHOD(GetText)(BSTR* pbsText);
            };      
  CComBSTR有一個操作符--BSTR方法,所以它能直接被傳給SetText()函數。還有另外一個操作--&,這個操作符返回一個BSTR*。所以,你可以對一個CComBSTR對象使用&操作符,然后把它傳給需要BSTR*參數的函數。
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相似的構造函數,然而卻沒有內置的向MBCS字符串轉換的函數。因此,你需要使用一個ATL轉換宏。
// Constructing
            CComBSTR bs1 = "char string";       // construct from a LPCSTR
            CComBSTR bs2 = L"wide char string"; // construct from a LPCWSTR
            CComBSTR bs3 = bs1;                 // copy from another CComBSTR
            CComBSTR bs4;
            bs4.LoadString ( IDS_SOME_STR );  // load string from string table
            // Extracting data
            BSTR bstr1 = bs1;        // returns internal BSTR, but don''t modify it!
            BSTR bstr2 = (BSTR) bs1; // cast ok, same as previous line
            BSTR bstr3 = bs1.Copy(); // copies bs1, returns it as a BSTR
            BSTR bstr4;
            bstr4 = bs1.Detach();  // bs1 no longer manages its BSTR
            // ...
            SysFreeString ( bstr3 );
            SysFreeString ( bstr4 );      
  注意在上個例子中使用了Detach()方法。調用這個方法后,CComBSTR對象不再管理它的BSTR字符串或者說它對應的內存。這就是bstr4需要調用SysFreeString()的原因。
  做一個補充說明:重載的&操作符意味著在一些STL容器中你不能直接使用CComBSTR變量,比如list。容器要求&操作符返回一個指向容器包含的類的指針,但是對CComBSTR變量使用&操作符返回的是BSTR*,而不是CComBSTR*。然而,有一個ATL類可以解決這個問題,這個類是CAdapt。例如,你可以這樣聲明一個CComBSTR的list:
std::list< CAdapt > bstr_list;

  CAdapt提供容器所需要的操作符,但這些操作符對你的代碼是透明的。你可以把一個bstr_list當作一個CComBSTR的list來使用。

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

// Constructing
            CComVariant v1 = "char string";       // construct from a LPCSTR
            CComVariant v2 = L"wide char string"; // construct from a LPCWSTR
            CComBSTR bs1 = "BSTR bob";
            CComVariant v3 = (BSTR) bs1;          // copy from a BSTR
            // Extracting data
            CComBSTR bs2 = v1.bstrVal;            // extract BSTR from the VARIANT      
  不像_variant_t,這里沒有提供針對VARIANT包含的各種類型的轉換操作符。正如上面介紹的,你必須直接訪問VARIANT的成員并且確保這個VARIANT變量保存著你期望的類型。如果你需要把一個CComVariant類型的數據轉換成一個BSTR類型的數據,你可以調用ChangeType()方法。
CComVariant v4 = ... // Init v4 from somewhere
            CComBSTR bs3;
            if ( SUCCEEDED( v4.ChangeType ( VT_BSTR ) ))
            bs3 = v4.bstrVal;      
  像_variant_t一樣,CComVariant也沒有提供向MBCS字符串轉換的轉換操作。你需要創建一個_bstr_t類型的中間變量,使用提供從Unicode到MBCS轉換的另一個字符串類,或者使用一個ATL的轉換宏。

ATL轉換宏

  ATL:轉換宏是各種字符編碼之間進行轉換的一種很方便的方式,在函數調用時,它們顯得非常有用。ATL轉換宏的名稱是根據下面的模式來命名的[源類型]2[新類型]或者[源類型]2C[新類型]。據有第二種形式的名字的宏的轉換結果是常量指針(對應名字中的"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字符串轉換成一個MBCS字符串。T2CW()宏把一個TCHAR字符串轉轉成一個Unicode字符串常量。
  為了使用這些宏,需要先包含atlconv.h頭文件。你甚至可以在非ATL工程中包含這個頭文件來使用其中定義的宏,因為這個頭文件獨立于ATL中的其他部分,不需要一個_Module全局變量。當你在一個函數中使用轉換宏時,需要把USES_CONVERSION宏放在函數的開頭。它定義了轉換宏所需的一些局部變量。
  當轉換的目的類型是除了BSTR以外的其他類型時,被轉換的字符串是存在棧中的。所以,如果你想讓字符串的生命周期比當前的函數長,你需要把這個字符串拷貝到其他的字符串類中。當目的類型是BSTR時,內存不會自動被釋放,你必須把返回值賦給一個BSTR變量或者一個BSTR封裝類以避免內存泄漏。
  下面是一些各種轉換宏的使用例子:

// Functions taking various strings:
            void Foo ( LPCWSTR wstr );
            void Bar ( BSTR bstr );
            // Functions returning strings:
            void Baz ( BSTR* pbstr );
            #include 
            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
            }      
  正如你所看見的,當你有一個和函數所需的參數類型不同的字符串時,使用這些轉換宏是非常方便的。

MFC類

CString
  因為一個MFC CString類的對象包含TCHAR類型的字符,所以確切的字符類型取決于你所定義的預處理符號。大體來說,CString 很像STL string,這意味著你必須把它當成不透明的對象,只能使用CString提供的方法來修改CString對象。CString有一個string所不具備的優點:CString具有接收MBCS和Unicode兩種字符串的構造函數,它還有一個LPCTSTR轉換符,所以你可以把CString對象直接傳給一個接收LPCTSTR的函數而不需要調用c_str()函數。
// Constructing
            CString s1 = "char string";  // construct from a LPCSTR
            CString s2 = L"wide char string";  // construct from a LPCWSTR
            CString s3 ( '' '', 100 );  // pre-allocate a 100-byte buffer, fill with spaces
            CString 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 table
            CString s5 ( (LPCTSTR) IDS_SOME_STR );  // load from string table
            CString 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 BSTR
            CString 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參數的構造函數。
// Constructing
            CString s1 = _T("tchar string");
            COleVariant v1 = _T("Bob"); // construct from an LPCTSTR
            COleVariant v2 = s1; // copy from a CString      
  像CComVariant一樣,你必須直接訪問VARIANT的成員。如果需要把VARIANT轉換成一個字符串,你應該使用ChangeType()方法。然而,COleVariant::ChangeType()如果失敗會拋出異常,而不是返回一個表示失敗的HRESULT代碼。
// Extracting data
            COleVariant v3 = ...; // fill in v3 from somewhere
            BSTR 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)。
// Constructing
            String* 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指針。

  • 作者簡介

    Michael Dunn:
      
    Michael Dunn居住在陽光城市洛杉磯。他是如此的喜歡這里的天氣以致于想一生都住在這里。他在4年級時開始編程,那時用的電腦是Apple //e。1995年,在UCLA獲得數學學士學位,隨后在Symantec公司做QA工程師,在 Norton AntiVirus 組工作。他自學了 Windows 和 MFC 編程。1999-2000年,他設計并實現了 Norton AntiVirus的新界面。
      Michael 現在在 Napster(一個提供在線訂閱音樂服務的公司)做開發工作,他還開發了UltraBar,一個IE工具欄插件,它可以使網絡搜索更加容易,給了 googlebar 以沉重打擊;他還開發了 CodeProject SearchBar;與人共同創建了 Zabersoft 公司,該公司在洛杉磯和丹麥的 Odense 都設有辦事處。
      他喜歡玩游戲。愛玩的游戲有 pinball, bike riding,偶爾還玩 PS, Dreamcasth 和 MAME 游戲。他因忘了自己曾經學過的語言:法語、漢語、日語而感到悲哀。

    Nishant S(Nish)
      Nish是來自印度 Trivandrum,的 Microsoft Visual C++ MVP。他從1990年開始編碼?,F在,Nish為作為合同雇員在家里為 CodeProject 工作。   
      他還寫了一部浪漫戲劇《Summer Love and Some more Cricket》和一本編程書籍《Extending MFC applications with the .NET Framework》。他還管理者MVP的一個網站http://www.voidnish.com/ 。在這個網站上,你可以看到他的很多關于編程方面的思想和文章。
    Nish 還計劃好旅游,他希望自一生中能夠到達地球上盡可能多的地方。




    posted on 2009-04-04 18:20 李陽 閱讀(285) 評論(0)  編輯 收藏 引用
    青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久精品国产91精品亚洲| 国产一区免费视频| 亚洲人成精品久久久久| 欧美电影免费观看大全| 欧美xart系列在线观看| 99re6这里只有精品视频在线观看| 亚洲第一色在线| 欧美精品aa| 日韩午夜电影av| 亚洲一区二区三区色| 国产日本欧美一区二区| 蜜桃精品久久久久久久免费影院| 免费观看久久久4p| 亚洲综合色婷婷| 久久久噜噜噜久噜久久| 亚洲乱码国产乱码精品精98午夜 | 国产一区观看| 亚洲国产第一| 国产精品乱码一区二三区小蝌蚪 | 欧美二区在线观看| 欧美色网一区二区| 久久综合中文色婷婷| 欧美日韩国产一级| 久久亚洲一区二区三区四区| 欧美精品久久久久久久免费观看| 亚洲欧美国产毛片在线| 久久综合伊人77777蜜臀| 亚洲欧美福利一区二区| 麻豆精品视频| 午夜精品久久久久久久99热浪潮 | 一区二区精品在线| 亚洲第一页自拍| 亚洲欧美日韩国产成人精品影院| 91久久精品美女高潮| 亚洲欧美在线x视频| 99精品久久| 久久亚洲影音av资源网| 欧美伊人久久久久久久久影院| 欧美本精品男人aⅴ天堂| 久久精品亚洲国产奇米99| 欧美日韩一区在线视频| 欧美大片免费观看在线观看网站推荐| 国产精品国产三级国产aⅴ浪潮| 另类图片国产| 国产一区 二区 三区一级| 一区二区三区国产在线观看| 亚洲欧洲免费视频| 久久久综合激的五月天| 亚洲国产精品美女| 亚洲国产欧美精品| 欧美伊人久久大香线蕉综合69| 中文精品一区二区三区| 欧美精品www| 免费国产一区二区| 国内精品视频在线播放| 亚洲欧美日韩区| 午夜精品久久久久久久99热浪潮| 欧美另类在线播放| 亚洲激情一区二区三区| 亚洲精品1区2区| 免费亚洲网站| 亚洲国产成人91精品| 亚洲高清三级视频| 麻豆av一区二区三区久久| 麻豆精品传媒视频| 有码中文亚洲精品| 久热精品视频| 亚洲黄色免费电影| 日韩午夜剧场| 欧美日韩视频一区二区| 日韩一二三在线视频播| 亚洲在线观看免费视频| 国产精品视频区| 午夜精品国产| 久久婷婷人人澡人人喊人人爽| 国产一区日韩二区欧美三区| 久久九九国产| 亚洲国产精彩中文乱码av在线播放| 亚洲精美视频| 国产精品成人一区二区网站软件| 亚洲婷婷综合久久一本伊一区| 亚洲综合视频一区| 国产一区二三区| 免费观看成人www动漫视频| 亚洲国产欧美一区二区三区同亚洲 | 国产三级精品三级| 久久网站免费| 日韩午夜激情av| 欧美与黑人午夜性猛交久久久| 国语自产在线不卡| 欧美激情久久久| 亚洲欧美日韩一区二区三区在线观看 | 欧美国产第二页| 中国av一区| 国产一区二区三区高清在线观看| 久热综合在线亚洲精品| 亚洲精品美女91| 久久精品国产免费| 亚洲免费高清| 国产亚洲欧美一区二区三区| 美女在线一区二区| 亚洲性夜色噜噜噜7777| 免费成人黄色av| 亚洲欧美日韩国产中文| 在线看不卡av| 欧美午夜在线| 免费看精品久久片| 性做久久久久久久免费看| 亚洲欧洲午夜| 久久精品国产一区二区三| 亚洲欧美日韩一区二区三区在线观看 | 国产精品久久久久秋霞鲁丝| 免费成人美女女| 午夜精品久久久久久久99水蜜桃 | 日韩网站在线观看| 女女同性精品视频| 午夜在线一区| 亚洲一区二区免费| 亚洲精品欧美极品| 在线观看三级视频欧美| 国产酒店精品激情| 欧美三级视频在线播放| 免费成人性网站| 久久精品一二三| 欧美一区二区私人影院日本 | 亚洲男女自偷自拍图片另类| 亚洲开发第一视频在线播放| 亚洲第一页中文字幕| 久久亚洲图片| 久久综合九色欧美综合狠狠| 欧美一区二区三区精品| 亚洲伊人网站| 中日韩美女免费视频网址在线观看| 最近看过的日韩成人| 精品不卡在线| 激情欧美日韩| 精久久久久久| 一区免费观看视频| 在线成人av.com| 亚洲第一综合天堂另类专| 黄色一区三区| 亚洲第一搞黄网站| 亚洲国产婷婷| 亚洲精品日日夜夜| 亚洲精品欧美激情| 中文日韩在线| 亚洲欧美日韩一区二区三区在线观看 | 欧美xart系列高清| 欧美国产欧美综合| 欧美日韩视频第一区| 国产精品v片在线观看不卡| 国产精品成人aaaaa网站| 国产精品白丝av嫩草影院 | 欧美精品国产一区二区| 欧美人交a欧美精品| 国产精品久久777777毛茸茸| 国产精品久99| 韩日视频一区| 亚洲精品乱码久久久久久蜜桃麻豆 | 国产精品久久久久91| 国产麻豆9l精品三级站| 韩日在线一区| 亚洲精品一线二线三线无人区| 日韩午夜在线电影| 亚洲欧美国产精品桃花| 久久久久综合| 亚洲日本中文字幕| 亚洲主播在线观看| 久久综合综合久久综合| 欧美午夜激情在线| 国产综合激情| 日韩亚洲欧美成人| 欧美一区影院| 亚洲国产精品一区二区www在线| 亚洲乱码国产乱码精品精可以看 | 国产精品成人观看视频免费| 国产欧美日韩综合一区在线播放| 一区二区在线视频观看| 一区二区欧美亚洲| 久久婷婷综合激情| 日韩午夜av| 久久亚洲精品视频| 国产精品久久久久久久一区探花| 国产真实久久| 亚洲尤物影院| 亚洲第一精品夜夜躁人人躁| 亚洲一区视频在线| 欧美国产免费| 黄色亚洲在线| 性欧美xxxx视频在线观看| 欧美激情bt| 欧美一区二区三区啪啪| 欧美午夜激情视频| 日韩午夜激情av| 老牛国产精品一区的观看方式| 一本久道综合久久精品| 久久免费视频网站| 国内精品久久久久影院色| 亚洲专区一区二区三区| 亚洲日本视频|