• <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>
            隨筆-4  評(píng)論-40  文章-117  trackbacks-0

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


            原著:Michael Dunn

            作者:Chengjie Sun


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



            引言

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

            Rule #1 of string classes

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

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

              正如我在第一部分中提到的,windows APIs 是用TCHARs來定義的,在編譯時(shí),它可以根據(jù)你是否定義_MBCS或者_(dá)UNICODE被編譯成MBCS或者Unicode字符。你可以參看第一部分中對(duì)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*)

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

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

              還有兩個(gè)用于包圍字符串和字符常量的宏定義,它們可以使同樣的代碼被用于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.

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

            COM 中的字符串 —— BSTR 和 VARIANT

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

             

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

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

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

            字符串封裝類

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

            CRT提供的類

            _bstr_t
              _bstr_t是一個(gè)對(duì)BSTR的完整封裝類,實(shí)際上它隱藏了底層的BSTR。它提供各種構(gòu)造函數(shù)和操作符來訪問底層的C語言風(fēng)格的字符串。然而,_bstr_t卻沒有訪問BSTR本身的操作符,所以一個(gè)_bstr_t類型的字符串不能被作為輸出參數(shù)傳給一個(gè)COM方法。如果你需要一個(gè)BSTR*參數(shù),使用ATL類CComBSTR是比較容易的方式。
              一個(gè)_bstr_t字符串能夠傳給一個(gè)接收參數(shù)類型為BSTR的函數(shù),只是因?yàn)橄铝?個(gè)條件同時(shí)滿足。首先,_bstr_t有一個(gè)向wchar_t*轉(zhuǎn)換的轉(zhuǎn)換函數(shù);其次,對(duì)編譯器而言,因?yàn)锽STR的定義,wchar_t*和BSTR有同樣的含義;第三,_bstr_t內(nèi)部含有的wchar_t*指向一片按BSTR的形式存儲(chǔ)數(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 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*之間的轉(zhuǎn)換操作符。這是一個(gè)值得懷疑的設(shè)計(jì),因?yàn)榧词顾鼈兪欠浅A孔址羔槪阋惨欢ú荒苁褂眠@些指針去修改它們指向的緩沖區(qū)的內(nèi)容,因?yàn)槟菍⑵茐膬?nèi)部的BSTR結(jié)構(gòu)。

            _variant_t
              _variant_t是一個(gè)對(duì)VARIANT的完整封裝,它提供很多構(gòu)造函數(shù)和轉(zhuǎn)換函數(shù)來操作一個(gè)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異常。

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

            STL 類
              STL只有一個(gè)字符串類,basic_string。一個(gè)basic_string管理一個(gè)以0做結(jié)束符的字符串?dāng)?shù)組。字符的類型是basic_string模般的參數(shù)。總的來說,一個(gè)basic_string類型的變量應(yīng)該被當(dāng)作不透明的對(duì)象。你可以得到一個(gè)指向內(nèi)部緩沖區(qū)的只讀指針,但是任何寫操作必須使用basic_string的操作符和方法。
              basic_string有兩個(gè)預(yù)定義的類型:包含char的string類型和包含wchar_t的wstring類型。這里沒有內(nèi)置的包含TCHAR的類型,但是你可以使用下面列出的代碼來實(shí)現(xiàn)。
            // 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,一個(gè)basic_string變量不能在字符集之間直接轉(zhuǎn)換。然而,你可以傳遞由c_str()返回的指針給另外一個(gè)類的構(gòu)造函數(shù)(如果這個(gè)類的構(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,這意味著你可以傳遞一個(gè)CComBSTR對(duì)象給COM的方法。CComBSTR對(duì)象能夠替你自動(dòng)的管理BSTR的內(nèi)存。例如,假設(shè)你想調(diào)用下面這個(gè)接口的方法:
            // Sample interface:
                        struct IStuff : public IUnknown
                        {
                        // Boilerplate COM stuff omitted...
                        STDMETHOD(SetText)(BSTR bsText);
                        STDMETHOD(GetText)(BSTR* pbsText);
                        };      
              CComBSTR有一個(gè)操作符--BSTR方法,所以它能直接被傳給SetText()函數(shù)。還有另外一個(gè)操作--&,這個(gè)操作符返回一個(gè)BSTR*。所以,你可以對(duì)一個(gè)CComBSTR對(duì)象使用&操作符,然后把它傳給需要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ù)。因此,你需要使用一個(gè)ATL轉(zhuǎn)換宏。
            // 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 );      
              注意在上個(gè)例子中使用了Detach()方法。調(diào)用這個(gè)方法后,CComBSTR對(duì)象不再管理它的BSTR字符串或者說它對(duì)應(yīng)的內(nèi)存。這就是bstr4需要調(diào)用SysFreeString()的原因。
              做一個(gè)補(bǔ)充說明:重載的&操作符意味著在一些STL容器中你不能直接使用CComBSTR變量,比如list。容器要求&操作符返回一個(gè)指向容器包含的類的指針,但是對(duì)CComBSTR變量使用&操作符返回的是BSTR*,而不是CComBSTR*。然而,有一個(gè)ATL類可以解決這個(gè)問題,這個(gè)類是CAdapt。例如,你可以這樣聲明一個(gè)CComBSTR的list:
            std::list< CAdapt > bstr_list;

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

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

            // 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,這里沒有提供針對(duì)VARIANT包含的各種類型的轉(zhuǎn)換操作符。正如上面介紹的,你必須直接訪問VARIANT的成員并且確保這個(gè)VARIANT變量保存著你期望的類型。如果你需要把一個(gè)CComVariant類型的數(shù)據(jù)轉(zhuǎn)換成一個(gè)BSTR類型的數(shù)據(jù),你可以調(diào)用ChangeType()方法。
            CComVariant v4 = ... // Init v4 from somewhere
                        CComBSTR bs3;
                        if ( SUCCEEDED( v4.ChangeType ( VT_BSTR ) ))
                        bs3 = v4.bstrVal;      
              像_variant_t一樣,CComVariant也沒有提供向MBCS字符串轉(zhuǎn)換的轉(zhuǎn)換操作。你需要?jiǎng)?chuàng)建一個(gè)_bstr_t類型的中間變量,使用提供從Unicode到MBCS轉(zhuǎn)換的另一個(gè)字符串類,或者使用一個(gè)ATL的轉(zhuǎn)換宏。

            ATL轉(zhuǎn)換宏

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

            MFC類

            CString
              因?yàn)橐粋€(gè)MFC CString類的對(duì)象包含TCHAR類型的字符,所以確切的字符類型取決于你所定義的預(yù)處理符號(hào)。大體來說,CString 很像STL string,這意味著你必須把它當(dāng)成不透明的對(duì)象,只能使用CString提供的方法來修改CString對(duì)象。CString有一個(gè)string所不具備的優(yōu)點(diǎn):CString具有接收MBCS和Unicode兩種字符串的構(gòu)造函數(shù),它還有一個(gè)LPCTSTR轉(zhuǎn)換符,所以你可以把CString對(duì)象直接傳給一個(gè)接收LPCTSTR的函數(shù)而不需要調(diào)用c_str()函數(shù)。
            // 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 );        
              你可以從你的字符串表中裝載一個(gè)字符串,CString的一個(gè)構(gòu)造函數(shù)和LoadString()函數(shù)可以完成它。Format()方法能夠從字符串表中隨意的讀取一個(gè)具有一定格式的字符串。     
            // 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, ... );  
              第一個(gè)構(gòu)造函數(shù)看起來有點(diǎn)奇怪,但是這實(shí)際上是文檔說明的裝入一個(gè)字符串的方法。 注意,對(duì)一個(gè)CString變量,你可以使用的唯一合法轉(zhuǎn)換符是LPCTSTR。轉(zhuǎn)換成LPTSTR(非常量指針)是錯(cuò)誤的。養(yǎng)成把一個(gè)CString變量轉(zhuǎn)換成LPTSTR的習(xí)慣將會(huì)給你帶來傷害,因?yàn)楫?dāng)你的程序后來崩潰時(shí),你可能不知道為什么,因?yàn)槟愕教幎际褂猛瑯拥拇a而那時(shí)它們都恰巧正常工作。正確的得到一個(gè)指向緩沖區(qū)的非常量指針的方法是調(diào)用GetBuffer()方法。下面是正確的用法的一個(gè)例子,這段代碼是給一個(gè)列表控件中的項(xiàng)設(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成員是一個(gè)LPTSTR變量,一個(gè)非常量指針,因此你需要對(duì)str調(diào)用GetBuffer()。GetBuffer()的參數(shù)是你需要CString為緩沖區(qū)分配的最小長度。如果因?yàn)槟承┰颍阈枰粋€(gè)可修改的緩沖區(qū)來存放1K TCHARs,你需要調(diào)用GetBuffer(1024)。把0作為參數(shù)時(shí),GetBuffer()返回的是指向字符串當(dāng)前內(nèi)容的指針。
              上面劃線的語句可以被編譯,在這種情況下,甚至可以正常起作用。但這并不意味著這行代碼是正確的。通過使用非常量轉(zhuǎn)換,你已經(jīng)破壞了面向?qū)ο蟮姆庋b,并對(duì)CString的內(nèi)部實(shí)現(xiàn)作了某些假定。如果你有這樣的轉(zhuǎn)換習(xí)慣,你終將會(huì)陷入代碼崩潰的境地。你會(huì)想代碼為什么不能正常工作了,因?yàn)槟愕教幎际褂猛瑯拥拇a而那些代碼看起來是正確的。
              你知道人們總是抱怨現(xiàn)在的軟件的bug是多么的多嗎?軟件中的bug是因?yàn)槌绦騿T寫了不正確的代碼。難道你真的想寫一些你知道是錯(cuò)誤的代碼來為所有的軟件都滿是bug這種認(rèn)識(shí)做貢獻(xiàn)嗎?花些時(shí)間來學(xué)習(xí)使用CString的正確方法讓你的代碼在任何時(shí)間都正常工作把。
              CString 有兩個(gè)函數(shù)來從一個(gè) CString 創(chuàng)建一個(gè) 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的函數(shù)。然而,不像CComVariant,COleVariant只有一個(gè)LPCTSTR構(gòu)造函數(shù)。沒有對(duì)LPCSTR 和LPCWSTR的構(gòu)造函數(shù)。在大多數(shù)情況下這不是一個(gè)問題,因?yàn)椴还茉鯓幽愕淖址芸赡苁荓PCTSTRs,但這是一個(gè)需要意識(shí)到的問題。COleVariant還有一個(gè)接收CString參數(shù)的構(gòu)造函數(shù)。
            // Constructing
                        CString s1 = _T("tchar string");
                        COleVariant v1 = _T("Bob"); // construct from an LPCTSTR
                        COleVariant v2 = s1; // copy from a CString      
              像CComVariant一樣,你必須直接訪問VARIANT的成員。如果需要把VARIANT轉(zhuǎn)換成一個(gè)字符串,你應(yīng)該使用ChangeType()方法。然而,COleVariant::ChangeType()如果失敗會(huì)拋出異常,而不是返回一個(gè)表示失敗的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完全一樣,所以你可以參考上面關(guān)于MFC的 CString的介紹。

            CLR 和 VC 7 類

              System::String是用來處理字符串的.NET類。在內(nèi)部,一個(gè)String對(duì)象包含一個(gè)不可改變的字符串序列。任何對(duì)String對(duì)象的操作實(shí)際上都是返回了一個(gè)新的String對(duì)象,因?yàn)樵嫉膶?duì)象是不可改變的。String的一個(gè)特性是如果你有不止一個(gè)String對(duì)象包含相同的字符序列,它們實(shí)際上是指向相同的對(duì)象的。相對(duì)于C++的使用擴(kuò)展是增加了一個(gè)新的字符串常量前綴S,S用來代表一個(gè)受控的字符串常量(a managed string literal)。
            // Constructing
                        String* ms = S"This is a nice managed string";      
              你可以傳遞一個(gè)非受控的字符串來創(chuàng)建一個(gè)String對(duì)象,但是樣會(huì)比使用受控字符串來創(chuàng)建String對(duì)象造成效率的微小損失。這是因?yàn)樗幸許作為前綴的相同的字符串實(shí)例都代表同樣的對(duì)象,但這對(duì)非受控對(duì)象是不適用的。下面的代碼清楚地闡明了這一點(diǎn):
            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) );      
              上面的兩行代碼都會(huì)打印0,0表示兩個(gè)字符串相等。 String和MFC 7 CString之間的轉(zhuǎn)換是很容易的。CString有一個(gè)向LPCTSTR的轉(zhuǎn)換操作,而String有兩個(gè)接收char* 和 wchar_t*的構(gòu)造函數(shù),因此你可以把一個(gè)CString變量直接傳給一個(gè)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 );      
              這也許會(huì)使你感到一點(diǎn)迷惑,但是它確實(shí)是起作用的。因?yàn)閺腣S.NET 開始,CString 有了一個(gè)接收String 對(duì)象的構(gòu)造函數(shù)。
              CStringT ( System::String* pString );      
            對(duì)于一些快速操作,你可能想訪問底層的字符串:
            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()返回一個(gè)指向底層字符串的const __wchar_t* ,我們需要固定它,否則垃圾收集器或許會(huì)在我們正在管理它的內(nèi)容的時(shí)候移動(dòng)了它。

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

              當(dāng)你在printf()或者類似的函數(shù)中使用字符串封裝類時(shí)你必須十分小心。這些函數(shù)包括sprintf()和它的變體,還有TRACE和ATLTRACE宏。因?yàn)檫@些函數(shù)沒有對(duì)添加的參數(shù)的類型檢查,你必須小心,只能傳給它們C語言風(fēng)格的字符串指針,而不是一個(gè)完整的字符串類。
              例如,要把一個(gè)_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)換符而把整個(gè)_bstr_t對(duì)象傳給了函數(shù),將會(huì)顯示一些毫無意義的輸出,因?yàn)開bstr_t保存的內(nèi)部數(shù)據(jù)會(huì)全部被輸出。

            所有類的總結(jié)

              兩個(gè)字符串類之間進(jìn)行轉(zhuǎn)換的常用方式是:先把源字符串轉(zhuǎn)換成一個(gè)C語言風(fēng)格的字符串指針,然后把這個(gè)指針傳遞給目的類型的構(gòu)造函數(shù)。下面這張表顯示了怎樣把一個(gè)字符串轉(zhuǎn)換成一個(gè)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_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. 1、即使 _bstr_t 提供了向非常量指針的轉(zhuǎn)換操作符,修改底層的緩沖區(qū)也會(huì)已引起GPF如果你溢出了緩沖區(qū)或者造成內(nèi)存泄漏。
          2. 2、_bstr_t 在內(nèi)部用一個(gè) wchar_t* 來保存 BSTR,所以你可以使用 const wchar_t* 來訪問BSTR。這是一個(gè)實(shí)現(xiàn)細(xì)節(jié),你可以小心的使用它,將來這個(gè)細(xì)節(jié)也許會(huì)改變。
          3. 3、如果數(shù)據(jù)不能轉(zhuǎn)換成BSTR會(huì)拋出一個(gè)異常。
          4. 4、使用 ChangeType(),然后訪問 VARIANT 的 bstrVal 成員。在MFC中,如果數(shù)據(jù)轉(zhuǎn)換不成功將會(huì)拋出異常。
          5. 5、這里沒有轉(zhuǎn)換 BSTR 函數(shù),然而 AllocSysString() 返回一個(gè)新的BSTR。
          6. 6、使用 GetBuffer() 方法,你可以暫時(shí)地得到一個(gè)非常量的TCHAR指針。

          7. 作者簡介

            Michael Dunn:
              
            Michael Dunn居住在陽光城市洛杉磯。他是如此的喜歡這里的天氣以致于想一生都住在這里。他在4年級(jí)時(shí)開始編程,那時(shí)用的電腦是Apple //e。1995年,在UCLA獲得數(shù)學(xué)學(xué)士學(xué)位,隨后在Symantec公司做QA工程師,在 Norton AntiVirus 組工作。他自學(xué)了 Windows 和 MFC 編程。1999-2000年,他設(shè)計(jì)并實(shí)現(xiàn)了 Norton AntiVirus的新界面。
              Michael 現(xiàn)在在 Napster(一個(gè)提供在線訂閱音樂服務(wù)的公司)做開發(fā)工作,他還開發(fā)了UltraBar,一個(gè)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年開始編碼。現(xiàn)在,Nish為作為合同雇員在家里為 CodeProject 工作。   
              他還寫了一部浪漫戲劇《Summer Love and Some more Cricket》和一本編程書籍《Extending MFC applications with the .NET Framework》。他還管理者M(jìn)VP的一個(gè)網(wǎng)站http://www.voidnish.com/ 。在這個(gè)網(wǎng)站上,你可以看到他的很多關(guān)于編程方面的思想和文章。
            Nish 還計(jì)劃好旅游,他希望自一生中能夠到達(dá)地球上盡可能多的地方。




            posted on 2009-04-04 18:20 李陽 閱讀(276) 評(píng)論(0)  編輯 收藏 引用

            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            亚洲va久久久噜噜噜久久天堂| 国产精品免费福利久久| 人妻无码久久精品| 亚洲AV日韩精品久久久久久| 国产成人久久精品区一区二区| 2020久久精品国产免费| 久久99精品久久久久久水蜜桃| 亚洲第一永久AV网站久久精品男人的天堂AV | 国产亚洲婷婷香蕉久久精品| 99久久成人国产精品免费| 久久免费99精品国产自在现线 | 久久国产劲爆AV内射—百度| 久久久久久无码Av成人影院 | 久久夜色精品国产噜噜麻豆 | 青青草国产精品久久| 久久久久亚洲AV无码专区首JN | 久久99久久无码毛片一区二区| 中文字幕乱码人妻无码久久| 国内精品久久久久国产盗摄| 99久久人妻无码精品系列| 久久综合色老色| 欧美无乱码久久久免费午夜一区二区三区中文字幕 | 久久久久久久97| 久久久久亚洲?V成人无码| 国产一久久香蕉国产线看观看| 久久精品国产久精国产一老狼| 精品久久久久久无码国产| 久久综合综合久久97色| 久久国产色AV免费看| 亚洲狠狠婷婷综合久久蜜芽| 亚洲一级Av无码毛片久久精品| 国内精品久久久久久久久| 亚洲国产精品久久久久婷婷软件| 无码精品久久久天天影视| 无码人妻久久一区二区三区蜜桃 | 国产午夜精品理论片久久| 999久久久国产精品| 国产精品成人久久久久三级午夜电影 | 国产高潮国产高潮久久久| 久久精品国产亚洲77777| 国产精品久久久久久一区二区三区 |