C++字符串完全指南 第二部分-字符串封装类(String Wrapper Classes)
2008-05-31 09:33
615 查看
1.简介
因为C类型的字符串容易出错和难于管理,更不用提黑客经常利用这个进行缓冲区溢出攻击。现在存在很多字符的封装类。不幸的是,哪个类使用在哪种情况下不是很清楚,也不要把C类型的字符串直接转化成封装类。
这篇文章将覆盖字符串封装类包括Win32API,MFC,STL,WTL,和theVisualC++runtimelibrary。我将描述每个类的使用方法。怎么去构造对象,怎么从一种类型转化成另一个类。
为了更好的理解这篇文章,你必须理解不同字符的类型和编码,我在第一部分已经进行了介绍。
Rule#1ofstringclasses
直接转化是一个错误的想法,除非有明确的文档说明了这个情况。对一个字符进行转化对这个字符来说不会做任何改变,不要这么写:
voidSomeFunc(LPCWSTRwidestr);
main()
{
SomeFunc((LPCWSTR)"C://foo.txt");//WRONG!
}
这肯定会100%的失败,这个编译会通过,因为转化编译器不会进行类型检查,但是编译并不意味着代码正确。
在接下来的例子中,我将指出转化在什么情况下是没有问题的。
C-stylestringsandtypedefs
就像我第一部分所说的那样,WindowsAPIs是预先定义好的的,TCHARs,它是在编译的时候根据你是否定义了_MBCS或
Herearethe
TherearealsotwomacrosusedaroundstringandcharacterliteralssothatthesamecodecanbeusedforbothMBCSandUnicodebuilds:
Therearealsovariantson
StringsinCOM-BSTRandVARIANT
许多自动化和其它的COM接口用BSTR来代替strings,但是BSTR有一些缺陷,在这里我将介绍BSTR.
BSTR是Pascal类型(wherethelengthisstoredexplicitlyalongwiththedata)的字符串和C类型wherethestringlengthmustbecalculatedbylookingforaterminatingzerocharacter).的字符串的混合体。BSTR是一个Unicode类型的字符串,string预留了他它的长度,它也是以‘0’字符结尾的,这里有一个例子anexampleof"Bob"asa
注意字符串的长度(length)存储在前面。COM库知道传输多少个数据(Asasidenote,a
A
Thisisveryunfortunate,becauseinrealitya
有许多APIs是用来操作BSTRs,但是最重要的两个函数是为BSTR分配和销毁空间,他们是SysAllocString()和
underlyingC-stylestring.However,thereisnooperatortoaccessthe
soa
A
threecoincidences.First,
andthird,the
thatfollowsthe
Notethat
aquestionabledesign,becauseeventhoughthosearenon-constantstringpointers,
youmustnotusethosepointerstomodifythebuffer,becausethatcouldbreakthe
internal
andconversionfunctionstooperateonthemultitudeoftypesthata
contain.Iwillonlycoverthestring-relatedoperationshere.
Notethatthe
bemade,sobepreparedtocatch
Alsonotethatthereisnodirectconversionfrom
Youwillneedtomakeaninterim
providestheUnicodetoMBCSconversion,oruseanATLconversionmacro.
Unlike
method.
ofa
arrayofcharacters.Thecharactertypeisgiveninthe
Ingeneral,a
read-onlypointertotheinternalbuffer,butanywriteoperationsmustuse
Therearetwopredefinedspecializationsfor
contains
specialization,butyoucanusetheonelistedbelow.
Unlike
However,youcanpassthepointerreturnedby
iftheconstructoracceptsthecharactertype,forexample:
Mostnotably,
passa
managethe
interface:
Thereisalsoan
a
toanMBCSstring.Forthat,youcanuseanATLconversionmacro.
Notethatinthelastexample,the
method,the
That'swhythe
Asafootnote,the
STLcollections,suchas
apointertothecontainedclass,butapplying
a
tomakealistof
yourcode;youcanuse
isnothidden,andinfactyouneedtoaccessthemembersofthe
Unlike
Asshownabove,youmustaccessthe
youneedtoconverta
Aswith
tomakeaninterim
UnicodetoMBCSconversion,oruseanATLconversionmacro.
encodings,andareespeciallyusefulinfunctioncalls.Theyarenamedaccordingto
thescheme
thesecondformconverttoaconstantpointer(thusthe"C"inthename).Thetype
abbreviationsare:
A:MBCSstring,
W:Unicodestring,
T:
OLE:
BSTR:
So,forexample,
convertsa
Tousethemacros,firstincludetheatlconv.hheaderfile.Youcandothisevenin
non-ATLprojects,sincethatheaderfilehasnodependenciesonotherpartsofATL,
anddoesn'trequirea
macroinafunction,putthe
Thisdefinessomelocalvariablesusedbythemacros.
Whenthedestinationtypeisanythingotherthan
storedonthestack,soifyouwanttokeepthestringaroundforlongerthanthe
currentfunction,you'llneedtocopythestringintoanotherstringclass.When
thedestinationtypeis
assignthereturnvaluetoa
leaks.
Herearesomeexamplesshowingvariousconversionmacros:
Collapse
//Functionstakingvariousstrings:
voidFoo(LPCWSTRwstr);
voidBar(BSTRbstr);
//Functionsreturningstrings:
voidBaz(BSTR*pbstr);
#include<atlconv.h>
main()
{
usingstd::string;
USES_CONVERSION;//declarelocalsusedbytheATLmacros
//Example1:SendanMBCSstringtoFoo()
LPCSTRpsz1="Bob";
stringstr1="Bob";
Foo(A2CW(psz1));
Foo(A2CW(str1.c_str()));
//Example2:SendaMBCSandUnicodestringtoBar()
LPCSTRpsz2="Bob";
LPCWSTRwsz=L"Bob";
BSTRbs1;
CComBSTRbs2;
bs1=A2BSTR(psz2);//createaBSTR
bs2.Attach(W2BSTR(wsz));//ditto,assigntoaCComBSTR
Bar(bs1);
Bar(bs2);
SysFreeString(bs1);//freebs1memory
//Noneedtofreebs2sinceCComBSTRwilldoitforus.
//Example3:ConverttheBSTRreturnedbyBaz()
BSTRbs3=NULL;
stringstr2;
Baz(&bs3);//Baz()fillsinbs3
str2=W2CA(bs3);//converttoanMBCSstring
SysFreeString(bs3);//freebs3memory
}[/code]
Asyoucansee,themacrosareveryhandywhenpassingparameterstoafunctionif
youhaveastringinoneformatandthefunctiontakesadifferentformat.
symbolsyouhavedefined.Ingeneral,a
shouldtreatitasanopaqueobjectandmodifyitonlywith
niceadvantage
acceptbothMBCSandUnicodestrings,andithasaconverterto
canpassa
no
Youcanalsoloadastringfromyourstringtable.Thereisa
thatwilldoit,alongwith
formatstringfromthestringtableaswell.
Thatfirstconstructorlooksodd,butthatisactuallythedocumentedthatwayto
loadastring.
Notethattheonlylegalcastyoucanapplytoa
Castingtoan
ofcastinga
breaklateron,youmightnotseewhy,becauseyouusedthesamecodeelsewhereand
ithappenedtowork.Thecorrectwaytogetanon-constpointertothebufferis
the
Asanexampleofthecorrectusage,considerthecaseofsettingthetextofanitem
inalistcontrol:
The
on
allocateforthebuffer.Ifforsomereasonyouwantedamodifiablebufferlarge
enoughtohold1K
justreturnsapointertothecurrentcontentsofthestring.
Thecrossed-outlineabovewillcompile,anditwillevenwork,inthiscase.But
thatdoesn'tmeanthecodeiscorrect.Byusingthenon-
object-orientedencapsulationandassumingsomethingabouttheinternalimplementation
of
acasewherethecodebreaks,andyou'llwonderwhyitisn'tworking,becauseyou
usethesamecodeeverywhereelseandit(apparently)works.
Youknowhowpeoplearealwayscomplainingabouthowbuggysoftwareisthesedays?
Bugsarecausedbytheprogrammerswritingincorrectcode.Doyoureallywantto
writecodeyouknowiswrong,andthuscontributetotheperceptionthatallsoftware
isbuggy?Takethetimetolearnthecorrectwayofusinga
codework100%ofthetime.
convertingtoUnicodeifnecessary.Theyare
Asidefromthe
itcanbepassedtoafunctionthattakesa
for
willlikelybe
hasaconstructorthatacceptsa
Aswith
failure
MFC
holdsanimmutablesequenceofcharacters.Any
manipulatesthe
original
thanone
referthesameobject.TheManagedExtensionstoC++haveanewstringliteral
prefix
Youcanconstructa
slightlylessefficientthanwhenyouconstructa
managedstring.Thisisbecauseallinstancesofidentical
representthesameobject,butthisisnottrueforunmanagedstrings.Thefollowing
codewillmakethisclear:
Therightwaytocomparestringsthatmaynothavebeencreatedusing
stringsistousethe
Boththeabovelineswillprint0,whichmeansthestringsareequal.
Convertingbetweena
to
thereforeyoucanpassa
Convertingtheotherwayworkssimilarly:
Thismightpuzzleyouabit,butitworksbecausestartingwithVS.NET,
aconstructorthatacceptsa
Forsomespeedymanipulations,youmightsometimeswanttoaccesstheunderlying
string:
topindownasotherwisethegarbagecollectormightmovethestringinmemorywhile
wearemanipulatingitscontents.
anyfunctionthatworkstheway
variants,aswellasthe
doneontheadditionalparameterstothefunctions,youmustbecarefultoonlypassaC-stylestringpointer,notacompletestringobject.
Soforexample,topassastringina
writethe
Ifyouforgetthecastandpasstheentire
displaymeaninglessoutput,sincewhatwillbepushedonthestackiswhatever
internaldatathe
http://www.codeproject.com/KB/string/cppstringguide2.aspx
因为C类型的字符串容易出错和难于管理,更不用提黑客经常利用这个进行缓冲区溢出攻击。现在存在很多字符的封装类。不幸的是,哪个类使用在哪种情况下不是很清楚,也不要把C类型的字符串直接转化成封装类。
这篇文章将覆盖字符串封装类包括Win32API,MFC,STL,WTL,和theVisualC++runtimelibrary。我将描述每个类的使用方法。怎么去构造对象,怎么从一种类型转化成另一个类。
为了更好的理解这篇文章,你必须理解不同字符的类型和编码,我在第一部分已经进行了介绍。
Rule#1ofstringclasses
直接转化是一个错误的想法,除非有明确的文档说明了这个情况。对一个字符进行转化对这个字符来说不会做任何改变,不要这么写:
voidSomeFunc(LPCWSTRwidestr);
main()
{
SomeFunc((LPCWSTR)"C://foo.txt");//WRONG!
}
这肯定会100%的失败,这个编译会通过,因为转化编译器不会进行类型检查,但是编译并不意味着代码正确。
在接下来的例子中,我将指出转化在什么情况下是没有问题的。
C-stylestringsandtypedefs
就像我第一部分所说的那样,WindowsAPIs是预先定义好的的,TCHARs,它是在编译的时候根据你是否定义了_MBCS或
_UNICODE宏来选择是MBCS或Unicode字符。你可以通过第一部分得到TCHAR的全面描述,我将列出所有的字符类型定义。
Type | Meaning |
---|---|
WCHAR | Unicodecharacter(wchar_t) |
TCHAR | MBCSorUnicodecharacter,dependingonpreprocessorsettings |
LPSTR | stringofchar( char*) |
LPCSTR | constantstringofchar( constchar*) |
LPWSTR | stringofWCHAR( WCHAR*) |
LPCWSTR | constantstringofWCHAR( constWCHAR*) |
LPTSTR | stringofTCHAR( TCHAR*) |
LPCTSTR | constantstringofTCHAR( constTCHAR*) |
OLECHAR-relatedtypedefsyouwillsee:
Type | Meaning |
---|---|
OLECHAR | Unicodecharacter(wchar_t) |
LPOLESTR | stringofOLECHAR( OLECHAR*) |
LPCOLESTR | constantstringofOLECHAR( constOLECHAR*) |
Type | Meaning |
---|---|
_T(x) | PrependsLtotheliteralinUnicodebuilds. |
OLESTR(x) | PrependsLtotheliteraltomakeitan LPCOLESTR. |
_Tthatyoumightencounterindocumentationorsamplecode.Therearefourequivalentmacros--
TEXT,
_TEXT,
__TEXT,and
__T--thatalldothesamething.
StringsinCOM-BSTRandVARIANT
许多自动化和其它的COM接口用BSTR来代替strings,但是BSTR有一些缺陷,在这里我将介绍BSTR.
BSTR是Pascal类型(wherethelengthisstoredexplicitlyalongwiththedata)的字符串和C类型wherethestringlengthmustbecalculatedbylookingforaterminatingzerocharacter).的字符串的混合体。BSTR是一个Unicode类型的字符串,string预留了他它的长度,它也是以‘0’字符结尾的,这里有一个例子anexampleof"Bob"asa
BSTR:
06000000 | 4200 | 6F00 | 6200 | 0000 |
--length-- | B | o | b | EOS |
BSTRcanholdanyarbitraryblockofdata,notjustcharacters,andcancontainembeddedzerocharacters.However,forthepurposesofthisarticle,Iwillnotconsidersuchcases.)
A
BSTRvariableinC++isactuallyapointertothefirstcharacterofthestring.Infact,thetype
BSTRisdefinedthisway:
typedefOLECHAR*BSTR;
Thisisveryunfortunate,becauseinrealitya
BSTRisnotthesameasaUnicodestring.Thattypedefdefeatstype-checkingandallowsyoutofreelymix
LPOLESTRsand
BSTRs.Passinga
BSTRtoafunctionexpectinga
LPCOLESTR(or
LPCWSTR)issafe,howeverthereverseisnot.Therefore,it'simportanttobeawareoftheexacttypeofstringthatafunctionexpects,andpassthecorrecttypeofstring.
有许多APIs是用来操作BSTRs,但是最重要的两个函数是为BSTR分配和销毁空间,他们是SysAllocString()和
SysFreeString().SysAllocString()把一个Unicode字符串拷贝到一个BSTR变量中,
SysFreeString()是释放一个BSTR开辟的空间。
BSTRbstr=NULL; bstr=SysAllocString(L"HiBob!"); if(NULL==bstr) //outofmemoryerror //Usebstrhere... SysFreeString(bstr); 很自然的,BSTR封装类接管了内存管理。 另一个自动化的接口是VARIANT。它是用来在无类型的语言,比如JScript、VBScript传送数据的。一个 VARIANT可以包含许多的类型,比如long和IDispatch*。当一个VARIANT包含一个字符串,它就是一个BSTR. 对VARIANT我后面会讲的更详细。
isacompletewrapperaroundaStringwrapperclasses
NowthatI'vecoveredthevarioustypesofstrings,I'lldemonstratethewrapper
classes.Foreachone,I'llshowhowtoconstructanobjectandhowtoconvertitt
oaC-stylestringpointer.TheC-stylepointerisoftennecessaryforanAPIcall,
ortoconstructanobjectofadifferentstringclass.Iwillnotcoverother
operatorstheclassesprovide,suchassortingorcomparison.
Onceagain,donotblindlycastobjectsunlessyouunderstandexactlywhatthe
resultingcodewilldo.ClassesprovidedbytheCRT
_bstr_t
[code]_bstr_t
BSTR,andinfactithidestheunderlying
BSTR.Itprovidesvariousconstructors,aswellasoperatorstoaccessthe
underlyingC-stylestring.However,thereisnooperatortoaccessthe
BSTRitself,
soa
_bstr_tcannotbepassedasan
[out]parametertoCOMmethods.Ifyouneeda
BSTR*touseasaparameter,itiseasiertotheATLclass
CComBSTR.
A
_bstr_tcanbepassedtoafunctionthattakesa
BSTR,butonlybecauseof
threecoincidences.First,
_bstr_thasaconversionfunctionto
wchar_t*;second,
wchar_t*and
BSTRappearthesametothecompilerbecauseofthedefinitionof
BSTR;
andthird,the
wchar_t*thata
_bstr_tkeepsinternallypointstoablockofmemory
thatfollowsthe
BSTRformat.Soeventhoughthereisnodocumentedconversionto
BSTR,ithappenstowork.
//Constructing
_bstr_tbs1="charstring";//constructfromaLPCSTR
_bstr_tbs2=L"widecharstring";//constructfromaLPCWSTR
_bstr_tbs3=bs1;//copyfromanother_bstr_t
_variant_tv="Bob";
_bstr_tbs4=v;//constructfroma_variant_tthathasastring
//Extractingdata
LPCSTRpsz1=bs1;//automaticallyconvertstoMBCSstring
LPCSTRpsz2=(LPCSTR)bs1;//castOK,sameaspreviousline
LPCWSTRpwsz1=bs1;//returnstheinternalUnicodestring
LPCWSTRpwsz2=(LPCWSTR)bs1;//castOK,sameaspreviousline
BSTRbstr=bs1.copy();//copiesbs1,returnsitasaBSTR
//...
SysFreeString(bstr);
Notethat
_bstr_talsohasconversionoperatorsfor
char*and
wchar_t*.Thisis
aquestionabledesign,becauseeventhoughthosearenon-constantstringpointers,
youmustnotusethosepointerstomodifythebuffer,becausethatcouldbreakthe
internal
BSTRstructure.
_variant_t
_variant_tisacompletewrapperarounda
VARIANT,andprovidesmanyconstructors
andconversionfunctionstooperateonthemultitudeoftypesthata
VARIANTcan
contain.Iwillonlycoverthestring-relatedoperationshere.
//Constructing
_variant_tv1="charstring";//constructfromaLPCSTR
_variant_tv2=L"widecharstring";//constructfromaLPCWSTR
_bstr_tbs1="Bob";
_variant_tv3=bs1;//copyfroma_bstr_tobject
//Extractingdata
_bstr_tbs2=v1;//extractBSTRfromtheVARIANT
_bstr_tbs3=(_bstr_t)v1;//castOK,sameaspreviousline
Notethatthe
_variant_tmethodscanthrowexceptionsifthetypeconversioncannot
bemade,sobepreparedtocatch
_com_errorexceptions.
Alsonotethatthereisnodirectconversionfrom
_variant_ttoanMBCSstring.
Youwillneedtomakeaninterim
_bstr_tvariable,useanotherstringclassthat
providestheUnicodetoMBCSconversion,oruseanATLconversionmacro.
Unlike
_bstr_t,a
_variant_tcanbepasseddirectlyasaparametertoaCOM
method.
_variant_tderivesfromthe
VARIANTtype,sopassinga
_variant_tinplace
ofa
VARIANTisallowedbyC++languagerules.
STLclasses
STLjusthasonestringclass,basic_string.A
basic_stringmanagesazero-terminated
arrayofcharacters.Thecharactertypeisgiveninthe
basic_stringtemplateparameter.
Ingeneral,a
basic_stringshouldbetreatedasanopaqueobject.Youcangeta
read-onlypointertotheinternalbuffer,butanywriteoperationsmustuse
basic_stringoperatorsandmethods.
Therearetwopredefinedspecializationsfor
basic_string:
string,which
contains
chars,and
wstring,whichcontains
wchar_ts.Thereisnobuilt-in
TCHAR
specialization,butyoucanusetheonelistedbelow.
//Specializations
typedefbasic_string<TCHAR>tstring;//stringofTCHARs
//Constructing
stringstr="charstring";//constructfromaLPCSTR
wstringwstr=L"widecharstring";//constructfromaLPCWSTR
tstringtstr=_T("TCHARstring");//constructfromaLPCTSTR
//Extractingdata
LPCSTRpsz=str.c_str();//read-onlypointertostr'sbuffer
LPCWSTRpwsz=wstr.c_str();//read-onlypointertowstr'sbuffer
LPCTSTRptsz=tstr.c_str();//read-onlypointertotstr'sbuffer
Unlike
_bstr_t,a
basic_stringcannotdirectlyconvertbetweencharactersets.
However,youcanpassthepointerreturnedby
c_str()toanotherclass'sconstructor
iftheconstructoracceptsthecharactertype,forexample:
//Example,construct_bstr_tfrombasic_string
_bstr_tbs1=str.c_str();//constructa_bstr_tfromaLPCSTR
_bstr_tbs2=wstr.c_str();//constructa_bstr_tfromaLPCWSTR
ATLclasses
CComBSTR
CComBSTRisATL's
BSTRwrapper,andismoreusefulinsomesituationsthan
_bstr_t.
Mostnotably,
CComBSTRallowsaccesstotheunderlying
BSTR,whichmeansyoucan
passa
CComBSTRobjecttoCOMmethods,andthe
CComBSTRobjectwillautomatically
managethe
BSTRmemoryforyou.Forexample,sayyouwantedtocallmethodsofthis
interface:
//Sampleinterface:
structIStuff:publicIUnknown
{
//BoilerplateCOMstuffomitted...
STDMETHOD(SetText)(BSTRbsText);
STDMETHOD(GetText)(BSTR*pbsText);
};
CComBSTRhasan
operatorBSTRmethod,soitcanbepasseddirectlyto
SetText().
Thereisalsoan
operator&thatreturnsa
BSTR*,soyoucanusethe
&operatoron
a
CComBSTRobjecttopassittoafunctionthattakesa
BSTR*.
CComBSTRbs1;
CComBSTRbs2="newtext";
pStuff->GetText(&bs1);//ok,takesaddressofinternalBSTR
pStuff->SetText(bs2);//ok,callsBSTRconverter
pStuff->SetText((BSTR)bs2);//castok,sameaspreviousline
CComBSTRhassimilarconstructorsto
_bstr_t,howeverthereisnobuilt-inconverter
toanMBCSstring.Forthat,youcanuseanATLconversionmacro.
//Constructing
CComBSTRbs1="charstring";//constructfromaLPCSTR
CComBSTRbs2=L"widecharstring";//constructfromaLPCWSTR
CComBSTRbs3=bs1;//copyfromanotherCComBSTR
CComBSTRbs4;
bs4.LoadString(IDS_SOME_STR);//loadstringfromstringtable
//Extractingdata
BSTRbstr1=bs1;//returnsinternalBSTR,butdon'tmodifyit!
BSTRbstr2=(BSTR)bs1;//castok,sameaspreviousline
BSTRbstr3=bs1.Copy();//copiesbs1,returnsitasaBSTR
BSTRbstr4;
bstr4=bs1.Detach();//bs1nolongermanagesitsBSTR
//...
SysFreeString(bstr3);
SysFreeString(bstr4);
Notethatinthelastexample,the
Detach()methodisused.Aftercallingthat
method,the
CComBSTRobjectnolongermanagesits
BSTRortheassociatedmemory.
That'swhythe
SysFreeString()callisnecessaryon
bstr4.
Asafootnote,the
operator&overridemeansyoucan'tuse
CComBSTRdirectlyinsome
STLcollections,suchas
list.Thecollectionsrequirethatthe
&operatorreturn
apointertothecontainedclass,butapplying
&toa
CComBSTRreturnsa
BSTR*,not
a
CComBSTR*.However,thereisanATLclasstoovercomethis,
CAdapt.Forexample,
tomakealistof
CComBSTR,declareitlikethis:
std::list<CAdapt<CComBSTR>>bstr_list;
CAdaptprovidestheoperatorsrequiredbythecollection,butitisinvisibleto
yourcode;youcanuse
bstr_listjustasifitwerealistof
CComBSTR.
CComVariant
CComVariantisawrapperarounda
VARIANT.However,unlike
_variant_t,the
VARIANT
isnothidden,andinfactyouneedtoaccessthemembersofthe
VARIANTdirectly.
CComVariantprovidesmanyconstructorstooperateonthemultitudeoftypesthata
VARIANTcancontain.Iwillonlycoverthestring-relatedoperationshere.
//Constructing
CComVariantv1="charstring";//constructfromaLPCSTR
CComVariantv2=L"widecharstring";//constructfromaLPCWSTR
CComBSTRbs1="BSTRbob";
CComVariantv3=(BSTR)bs1;//copyfromaBSTR
//Extractingdata
CComBSTRbs2=v1.bstrVal;//extractBSTRfromtheVARIANT
Unlike
_variant_t,therearenoconversionoperatorstothevarious
VARIANTtypes.
Asshownabove,youmustaccessthe
VARIANTmembersdirectlyandensurethatthe
VARIANTholdsdataofthetypeyouexpect.Youcancallthe
ChangeType()methodif
youneedtoconverta
CComVariant'sdatatoa
BSTR.
CComVariantv4=...//Initv4fromsomewhere
CComBSTRbs3;
if(SUCCEEDED(v4.ChangeType(VT_BSTR)))
bs3=v4.bstrVal;
Aswith
_variant_t,thereisnodirectconversiontoanMBCSstring.Youwillneed
tomakeaninterim
_bstr_tvariable,useanotherstringclassthatprovidesthe
UnicodetoMBCSconversion,oruseanATLconversionmacro.
ATLconversionmacros
ATL'sstringconversionmacrosareaveryconvenientwaytoconvertbetweencharacterencodings,andareespeciallyusefulinfunctioncalls.Theyarenamedaccordingto
thescheme
[sourcetype]2[newtype]or
[sourcetype]2C[newtype].Macrosnamedwith
thesecondformconverttoaconstantpointer(thusthe"C"inthename).Thetype
abbreviationsare:
A:MBCSstring,
char*(AforANSI)
W:Unicodestring,
wchar_t*(Wforwide)
T:
TCHARstring,
TCHAR*
OLE:
OLECHARstring,
OLECHAR*(inpractice,equivalenttoW)
BSTR:
BSTR(usedasthedestinationtypeonly)
So,forexample,
W2A()convertsaUnicodestringtoanMBCSstring,and
T2CW()
convertsa
TCHARstringtoaconstantUnicodestring.
Tousethemacros,firstincludetheatlconv.hheaderfile.Youcandothisevenin
non-ATLprojects,sincethatheaderfilehasnodependenciesonotherpartsofATL,
anddoesn'trequirea
_Moduleglobalvariable.Then,whenyouuseaconversion
macroinafunction,putthe
USES_CONVERSIONmacroatthebeginningofthefunction.
Thisdefinessomelocalvariablesusedbythemacros.
Whenthedestinationtypeisanythingotherthan
BSTR,theconvertedstringis
storedonthestack,soifyouwanttokeepthestringaroundforlongerthanthe
currentfunction,you'llneedtocopythestringintoanotherstringclass.When
thedestinationtypeis
BSTR,thememoryisnotautomaticallyfreed,soyoumust
assignthereturnvaluetoa
BSTRvariableora
BSTRwrapperclasstoavoidmemory
leaks.
Herearesomeexamplesshowingvariousconversionmacros:
Collapse
//Functionstakingvariousstrings:
voidFoo(LPCWSTRwstr);
voidBar(BSTRbstr);
//Functionsreturningstrings:
voidBaz(BSTR*pbstr);
#include<atlconv.h>
main()
{
usingstd::string;
USES_CONVERSION;//declarelocalsusedbytheATLmacros
//Example1:SendanMBCSstringtoFoo()
LPCSTRpsz1="Bob";
stringstr1="Bob";
Foo(A2CW(psz1));
Foo(A2CW(str1.c_str()));
//Example2:SendaMBCSandUnicodestringtoBar()
LPCSTRpsz2="Bob";
LPCWSTRwsz=L"Bob";
BSTRbs1;
CComBSTRbs2;
bs1=A2BSTR(psz2);//createaBSTR
bs2.Attach(W2BSTR(wsz));//ditto,assigntoaCComBSTR
Bar(bs1);
Bar(bs2);
SysFreeString(bs1);//freebs1memory
//Noneedtofreebs2sinceCComBSTRwilldoitforus.
//Example3:ConverttheBSTRreturnedbyBaz()
BSTRbs3=NULL;
stringstr2;
Baz(&bs3);//Baz()fillsinbs3
str2=W2CA(bs3);//converttoanMBCSstring
SysFreeString(bs3);//freebs3memory
}[/code]
Asyoucansee,themacrosareveryhandywhenpassingparameterstoafunctionif
youhaveastringinoneformatandthefunctiontakesadifferentformat.
MFCclasses
CString
AnMFCCStringholds
TCHARs,sotheexactcharactertypedependsonthepreprocessor
symbolsyouhavedefined.Ingeneral,a
CStringislikeanSTL
string,inthatyou
shouldtreatitasanopaqueobjectandmodifyitonlywith
CStringmethods.One
niceadvantage
CStringhasovertheSTL
stringisthatithasconstructorsthat
acceptbothMBCSandUnicodestrings,andithasaconverterto
LPCTSTR,soyou
canpassa
CStringobjectdirectlytoafunctionthatacceptsan
LPCTSTR;thereis
no
c_str()methodyouhavetocall.
//Constructing
CStrings1="charstring";//constructfromaLPCSTR
CStrings2=L"widecharstring";//constructfromaLPCWSTR
CStrings3('',100);//pre-allocatea100-bytebuffer,fillwithspaces
CStrings4="Newwindowtext";
//YoucanpassaCStringinplaceofanLPCTSTR:
SetWindowText(hwndSomeWindow,s4);
//Or,equivalently,explicitlycasttheCString:
SetWindowText(hwndSomeWindow,(LPCTSTR)s4);
Youcanalsoloadastringfromyourstringtable.Thereisa
CStringconstructor
thatwilldoit,alongwith
LoadString().The
Format()methodcanoptionallyreada
formatstringfromthestringtableaswell.
//Constructing/loadingfromstringtable
CStrings5((LPCTSTR)IDS_SOME_STR);//loadfromstringtable
CStrings6,s7;
//Loadfromstringtable.
s6.LoadString(IDS_SOME_STR);
//Loadprintf-styleformatstringfromthestringtable:
s7.Format(IDS_SOME_FORMAT,"bob",nSomeStuff,...);
Thatfirstconstructorlooksodd,butthatisactuallythedocumentedthatwayto
loadastring.
Notethattheonlylegalcastyoucanapplytoa
CStringisacastto
LPCTSTR.
Castingtoan
LPTSTR(thatis,anon-
constpointer)iswrong.Gettinginthehabit
ofcastinga
CStringtoan
LPTSTRwillonlyhurtyourself,aswhenthecodedoes
breaklateron,youmightnotseewhy,becauseyouusedthesamecodeelsewhereand
ithappenedtowork.Thecorrectwaytogetanon-constpointertothebufferis
the
GetBuffer()method.
Asanexampleofthecorrectusage,considerthecaseofsettingthetextofanitem
inalistcontrol:
CStringstr=_T("newtext");
LVITEMitem={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();//returncontrolofthebuffertostr
The
pszTextmemberisan
LPTSTR,anon-
constpointer,thereforeyoucall
GetBuffer()
on
str.Theparameterto
GetBuffer()istheminimumlengthyouwant
CStringto
allocateforthebuffer.Ifforsomereasonyouwantedamodifiablebufferlarge
enoughtohold1K
TCHARs,youwouldcall
GetBuffer(1024).Passing0asthelength
justreturnsapointertothecurrentcontentsofthestring.
Thecrossed-outlineabovewillcompile,anditwillevenwork,inthiscase.But
thatdoesn'tmeanthecodeiscorrect.Byusingthenon-
constcast,you'rebreaking
object-orientedencapsulationandassumingsomethingabouttheinternalimplementation
of
CString.Ifyoumakeahabitofcastinglikethat,youwilleventuallyruninto
acasewherethecodebreaks,andyou'llwonderwhyitisn'tworking,becauseyou
usethesamecodeeverywhereelseandit(apparently)works.
Youknowhowpeoplearealwayscomplainingabouthowbuggysoftwareisthesedays?
Bugsarecausedbytheprogrammerswritingincorrectcode.Doyoureallywantto
writecodeyouknowiswrong,andthuscontributetotheperceptionthatallsoftware
isbuggy?Takethetimetolearnthecorrectwayofusinga
CStringandhaveyour
codework100%ofthetime.
CStringalsohastwofunctionsthatcreatea
BSTRfromthe
CStringcontents,
convertingtoUnicodeifnecessary.Theyare
AllocSysString()and
SetSysString().
Asidefromthe
BSTR*parameterthat
SetSysString()takes,theyworkidentically.
//ConvertingtoBSTR
CStrings5="Bob!";
BSTRbs1=NULL,bs2=NULL;
bs1=s5.AllocSysString();
s5.SetSysString(&bs2);
//...
SysFreeString(bs1);
SysFreeString(bs2);
COleVariant
COleVariantisprettysimilarto
CComVariant.
COleVariantderivesfrom
VARIANT,so
itcanbepassedtoafunctionthattakesa
VARIANT.However,unlike
CComVariant,
COleVariantonlyhasan
LPCTSTRconstructor.Therearenotseparateconstructors
for
LPCSTRand
LPCWSTR.Inmostcasesthisisnotaproblem,sinceyourstrings
willlikelybe
LPCTSTRsanyway,butitisapointtobeawareof.
COleVariantalso
hasaconstructorthatacceptsa
CString.
//Constructing
CStrings1=_T("tcharstring");
COleVariantv1=_T("Bob");//constructfromanLPCTSTR
COleVariantv2=s1;//copyfromaCString
Aswith
CComVariant,youmustaccessthe
VARIANTmembersdirectly,usingthe
ChangeType()methodifnecessarytoconvertthe
VARIANTtoastring.However,
COleVariant::ChangeType()throwsanexceptionifitfails,insteadofreturninga
failure
HRESULTcode.
//Extractingdata
COleVariantv3=...;//fillinv3fromsomewhere
BSTRbs=NULL;
try
{
v3.ChangeType(VT_BSTR);
bs=v3.bstrVal;
}
catch(COleException*e)
{
//error,couldn'tconvert
}
SysFreeString(bs);
WTLclasses
CString
WTL'sCStringbehavesexactlylikeMFC's
CString,sorefertothedescriptionofthe
MFC
CStringabove.
CLRandVC7classes
System::Stringisthe.NETclassforhandlingstrings.Internally,a
Stringobject
holdsanimmutablesequenceofcharacters.Any
Stringmethodthatsupposedly
manipulatesthe
Stringobjectactuallyreturnsanew
Stringobject,becausethe
original
Stringisimmutable.Apeculiarityof
Stringsisthatifyouhavemore
thanone
Stringcontainingthesameseries,ofcharactersallofthemactually
referthesameobject.TheManagedExtensionstoC++haveanewstringliteral
prefix
S,whichisusedtorepresentamanagedstringliteral.
//Constructing
String*ms=S"Thisisanicemanagedstring";
Youcanconstructa
Stringobjectbypassinganunmanagedstring,butthisis
slightlylessefficientthanwhenyouconstructa
Stringobjectbypassinga
managedstring.Thisisbecauseallinstancesofidentical
Sprefixedstrings
representthesameobject,butthisisnottrueforunmanagedstrings.Thefollowing
codewillmakethisclear:
String*ms1=S"thisisnice";
String*ms2=S"thisisnice";
String*ms3=L"thisisnice";
Console::WriteLine(ms1==ms2);//printstrue
Console::WriteLine(ms1==ms3);//printsfalse
Therightwaytocomparestringsthatmaynothavebeencreatedusing
Sprefixed
stringsistousethe
String::CompareTo()methodasshownbelow:
Console::WriteLine(ms1->CompareTo(ms2));
Console::WriteLine(ms1->CompareTo(ms3));
Boththeabovelineswillprint0,whichmeansthestringsareequal.
Convertingbetweena
StringandtheMFC7
CStringiseasy.
CStringhasaconverter
to
LPCTSTRand
Stringhastwoconstructorsthattakea
char*and
wchar_t*,
thereforeyoucanpassa
CStringstraighttoa
Stringconstructor.
CStrings1("helloworld");
String*s2(s1);//copyfromaCString
Convertingtheotherwayworkssimilarly:
String*s1=S"Threecats";
CStrings2(s1);
Thismightpuzzleyouabit,butitworksbecausestartingwithVS.NET,
CStringhas
aconstructorthatacceptsa
Stringobject:
CStringT(System::String*pString);
Forsomespeedymanipulations,youmightsometimeswanttoaccesstheunderlying
string:
String*s1=S"Threecats";
Console::WriteLine(s1);
const__wchar_t__pin*pstr=PtrToStringChars(s1);
for(inti=0;i<wcslen(pstr);i++)
(*const_cast<__wchar_t*>(pstr+i))++;
Console::WriteLine(s1);
PtrToStringChars()returnsa
const__wchar_t*totheunderlyingstringwhichweneed
topindownasotherwisethegarbagecollectormightmovethestringinmemorywhile
wearemanipulatingitscontents.
Usingstringclasseswithprintf-styleformattingfunctions
Youmustpaycarefulattentionwhenusingstringwrapperclasseswithprintf()or
anyfunctionthatworkstheway
printf()does.Thisincludes
sprintf()andits
variants,aswellasthe
TRACEand
ATLTRACEmacros.Becausethereisnotype-checking
doneontheadditionalparameterstothefunctions,youmustbecarefultoonlypassaC-stylestringpointer,notacompletestringobject.
Soforexample,topassastringina
_bstr_tto
ATLTRACE(),youmustexplicitly
writethe
(LPCSTR)or
(LPCWSTR)cast:
_bstr_tbs=L"Bob!";
ATLTRACE("Thestringis:%sinline%d/n",(LPCSTR)bs,nLine);
Ifyouforgetthecastandpasstheentire
_bstr_tobject,thetracemessagewill
displaymeaninglessoutput,sincewhatwillbepushedonthestackiswhatever
internaldatathe
_bstr_tvariablekeeps.
Summaryofalltheclasses
Theusualwayofconvertingbetweentwostringclassesistotakethesourcestring,convertittoaC-stylestringpointer,andthenpassthepointertoaconstructorinthedestinationtype.SohereisachartshowinghowtoconvertastringtoaC-stylepointer,andwhichclassescanbeconstructedfromC-stylepointers.Class | string type | convert to char*? | converttoconstchar*? | converttowchar_t*? | converttoconstwchar_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 | castto_bstr_t3 | castto_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 | inMBCS builds,cast | no6 | inUnicode builds,cast | no5 | yes | yes |
COleVariant | BSTR | no | no | no | yes4 | yes4 | inMBCSbuilds | inUnicodebuilds |
1Eventhough_bstr_tprovidesconversionoperatorstonon- constpointers,modifyingtheunderlyingbuffermaycauseaGPFifyouoverrunthebuffer,oraleakwhenthe BSTRmemoryisfreed. 2A _bstr_tholdsa BSTRinternallyina wchar_t*variable,soyoucanusethe constwchar_t*convertertoretrievethe BSTR.Thisisanimplementationdetail,sousethiswithcaution,asitmaychangeinthefuture. 3Thiswillthrowanexceptionifthedatacannotbeconvertedtoa BSTR. 4Use ChangeType()thenaccessthe bstrValmemberofthe VARIANT.InMFC,thiswillthrowanexceptionifthedatacannotbeconverted. 5Thereisno BSTRconversionfunction,howeverthe AllocSysString()methodreturnsanew BSTR. 6Youcantemporarilygetanon-const TCHARpointerusingthe GetBuffer()method. |
相关文章推荐
- C++字符串完全指南(2) - 总结
- C++字符串完全指南 - Win32字符编码(一)
- C++字符串完全指引之二 —— 字符串封装类
- C++字符串完全指南(2)
- C++字符串完全指南
- C++字符串完全指南
- C++字符串完全指南 - Win32字符编码(一)
- C++字符串完全指南
- C++字符串完全指南(2) - MFC类
- C++字符串完全指引之二 —— 字符串封装类(转)
- C++字符串完全指南 第一部分:Win32字符编码
- C++字符串完全指南 - MFC类
- C++字符串完全指引之二 —— 字符串封装类
- C++字符串完全指南(2) - MFC类
- C++字符串完全指南 - Win32字符编码
- C#基础系列(7)-- 第二部分 字符串相关 -- string与StringBuilder(2)
- C++字符串完全指引之二 —— 字符串封装类
- C++字符串完全指南(2) - 总结
- C++字符串完全指南
- 《C++字符串完全指南——第一部分:win32 字符编码》