C++字符串完全指引之二字符串封装类Word格式.docx
- 文档编号:16809898
- 上传时间:2022-11-26
- 格式:DOCX
- 页数:16
- 大小:28.42KB
C++字符串完全指引之二字符串封装类Word格式.docx
《C++字符串完全指引之二字符串封装类Word格式.docx》由会员分享,可在线阅读,更多相关《C++字符串完全指引之二字符串封装类Word格式.docx(16页珍藏版)》请在冰豆网上搜索。
C-stylestringsandtypedefs
正如我在第一部分中提到的,windowsAPIs是用TCHARs来定义的,在编译时,它可以根据你是否定义_MBCS或者_UNICODE被编译成MBCS或者Unicode字符。
你可以参看第一部分中对TCHAR的完整描述,这里为了方便,我列出了字符的typedefs
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*)
一个增加的字符类型是OLETYPE。
它表示自动化接口(如word提供的可以使你操作文档的接口)中使用的字符类型。
这种类型一般被定义成wchar_t,然而如果你定义了OLE2ANSI预处理标记,OLECHAR将会被定义成char类型。
我知道现在已经没有理由定义OLE2ANSI(从MFC3以后,微软已经不使用它了),所以从现在起我将把OLECHAR当作Unicode字符。
这里给出你将会看到的一些OLECHAR相关的typedefs:
OLECHAR
LPOLESTR
stringofOLECHAR(OLECHAR*)
LPCOLESTR
constantstringofOLECHAR(constOLECHAR*)
还有两个用于包围字符串和字符常量的宏定义,它们可以使同样的代码被用于MBCS和Unicodebuilds:
Type
_T(x)
PrependsLtotheliteralinUnicodebuilds.
OLESTR(x)
PrependsLtotheliteraltomakeitanLPCOLESTR.
在文档或例程中,你还会看到好多_T的变体。
有四个等价的宏定义,它们是TEXT,_TEXT,__TEXT和__T,它们都起同样的做用。
COM中的字符串——BSTR和VARIANT
很多自动化和COM接口使用BSTR来定义字符串。
BSTRs中有几个"
陷阱"
,所以这里我用单独的部分来说明它。
BSTR是Pascal-style字符串(字符串长度被明确指出)和C-style字符串(字符串的长度要通过寻找结束符来计算)的混合产物。
一个BSTR是一个Unicode字符串,它的长度是预先考虑的,并且它还有一个0字符作为结束标记。
下面是一个BSTR的示例:
06000000
4200
6F00
6200
0000
--length--
B
o
b
EOS
注意字符串的长度是如何被加到字符串数据中的。
长度是DWORD类型的,保存了字符串中包含的字节数,但不包括结束标记。
在这个例子中,"
Bob"
包含3个Unicode字符(不包括结束符),总共6个字节。
字符串的长度被预先存储好,以便当一个BSTR在进程或者计算机之间被传递时,COM库知道多少数据需要传送。
(另一方面,一个BSTR能够存储任意数据块,而不仅仅是字符,它还可以包含嵌入在数据中的0字符。
然而,由于这篇文章的目的,我将不考虑那些情况)。
在C++中,一个BSTR实际上就是一个指向字符串中第一个字符的指针。
它的定义如下:
BSTRbstr=NULL;
bstr=SysAllocString(L"
HiBob!
"
if(NULL==bstr)
//outofmemoryerror
//Usebstrhere...
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_tbs1="
charstring"
;
//constructfromaLPCSTR
_bstr_tbs2=L"
widecharstring"
//constructfromaLPCWSTR
_bstr_tbs3=bs1;
//copyfromanother_bstr_t
_variant_tv="
_bstr_tbs4=v;
//constructfroma_variant_tthathasastring
//Extractingdata
LPCSTRpsz1=bs1;
//automaticallyconvertstoMBCSstring
LPCSTRpsz2=(LPCSTR)bs1;
//castOK,sameaspreviousline
LPCWSTRpwsz1=bs1;
//returnstheinternalUnicodestring
LPCWSTRpwsz2=(LPCWSTR)bs1;
BSTRbstr=bs1.copy();
//copiesbs1,returnsitasaBSTR
//...
SysFreeString(bstr);
注意_bstr_t也提供char*和wchar_t*之间的转换操作符。
这是一个值得怀疑的设计,因为即使它们是非常量字符串指针,你也一定不能使用这些指针去修改它们指向的缓冲区的内容,因为那将破坏内部的BSTR结构。
_variant_t
_variant_t是一个对VARIANT的完整封装,它提供很多构造函数和转换函数来操作一个VARIANT可能包含的大量的数据类型。
这里,我将只介绍与字符串有关的操作。
_variant_tv1="
_variant_tv2=L"
_variant_tv3=bs1;
//copyfroma_bstr_tobject
_bstr_tbs2=v1;
//extractBSTRfromtheVARIANT
_bstr_tbs3=(_bstr_t)v1;
//castOK,sameaspreviousline
注意:
如果类型转换不能被执行,_variant_t方法能够抛出异常,所以应该准备捕获_com_error异常。
还需要注意的是:
没有从一个_variant_t变量到一个MBCS字符串的直接转换。
你需要创建一个临时的_bstr_t变量,使用提供Unicode到MBCS转换的另一个字符串类或者使用一个ATL转换宏。
不像_bstr_t,一个_variant_t变量可以被直接作为参数传递给一个COM方法。
继承自VARIANT类型,所以传递一个_variant_t来代替VARIANT变量是C++语言所允许的。
STL类
STL只有一个字符串类,basic_string。
一个basic_string管理一个以0做结束符的字符串数组。
字符的类型是basic_string模般的参数。
总的来说,一个basic_string类型的变量应该被当作不透明的对象。
你可以得到一个指向内部缓冲区的只读指针,但是任何写操作必须使用basic_string的操作符和方法。
basic_string有两个预定义的类型:
包含char的string类型和包含wchar_t的wstring类型。
这里没有内置的包含TCHAR的类型,但是你可以使用下面列出的代码来实现。
//Specializations
typedefbasic_stringtstring;
//stringofTCHARs
stringstr="
wstringwstr=L"
tstringtstr=_T("
TCHARstring"
);
//constructfromaLPCTSTR
LPCSTRpsz=str.c_str();
//read-onlypointertostr'
'
sbuffer
LPCWSTRpwsz=wstr.c_str();
//read-onlypointertowstr'
LPCTSTRptsz=tstr.c_str();
//read-onlypointertotstr'
不像_bstr_t,一个basic_string变量不能在字符集之间直接转换。
然而,你可以传递由c_str()返回的指针给另外一个类的构造函数(如果这个类的构造函数接受这种字符类型)。
例如:
//Example,construct_bstr_tfrombasic_string
_bstr_tbs1=str.c_str();
//constructa_bstr_tfromaLPCSTR
_bstr_tbs2=wstr.c_str();
//constructa_bstr_tfromaLPCWSTR
ATL类
CComBSTR
CComBSTR是ATL中的BSTR封装类,它在某些情况下比_bstr_t有用的多。
最引人注意的是CComBSTR允许访问底层的BSTR,这意味着你可以传递一个CComBSTR对象给COM的方法。
CComBSTR对象能够替你自动的管理BSTR的内存。
例如,假设你想调用下面这个接口的方法:
//Sampleinterface:
structIStuff:
publicIUnknown
//BoilerplateCOMstuffomitted...
STDMETHOD(SetText)(BSTRbsText);
STDMETHOD(GetText)(BSTR*pbsText);
};
CComBSTR有一个操作符--BSTR方法,所以它能直接被传给SetText()函数。
还有另外一个操作--&
,这个操作符返回一个BSTR*。
所以,你可以对一个CComBSTR对象使用&
操作符,然后把它传给需要BSTR*参数的函数。
CComBSTRbs1;
CComBSTRbs2="
newtext"
pStuff->
GetText(&
bs1);
//ok,takesaddressofinternalBSTR
SetText(bs2);
//ok,callsBSTRconverter
SetText((BSTR)bs2);
//castok,sameaspreviousline
CComBSTR有和_bstr_t相似的构造函数,然而却没有内置的向MBCS字符串转换的函数。
因此,你需要使用一个ATL转换宏。
CComBSTRbs1="
CComBSTRbs2=L"
CComBSTRbs3=bs1;
//copyfromanotherCComBSTR
CComBSTRbs4;
bs4.LoadString(IDS_SOME_STR);
//loadstringfromstringtable
BSTRbstr1=bs1;
//returnsinternalBSTR,butdon'
tmodifyit!
BSTRbstr2=(BSTR)bs1;
//castok,sameaspreviousline
BSTRbstr3=bs1.Copy();
BSTRbstr4;
bstr4=bs1.Detach();
//bs1nolongermanagesitsBSTR
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能够包含的多种类型进行处理。
这里,我将只介绍和字符串相关的操作。
CComVariantv1="
CComVariantv2=L"
BSTRbob"
CComVariantv3=(BSTR)bs1;
//copyfromaBSTR
CComBSTRbs2=v1.bstrVal;
//extractBSTRfromtheVARIANT
不像_variant_t,这里没有提供针对VARIANT包含的各种类型的转换操作符。
正如上面介绍的,你必须直接访问VARIANT的成员并且确保这个VARIANT变量保存着你期望的类型。
如果你需要把一个CComVariant类型的数据转换成一个BSTR类型的数据,你可以调用ChangeType()方法。
CComVariantv4=...//Initv4fromsomewhere
CComBSTRbs3;
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:
MBCSstring,char*(AforANSI)
W:
Unicodestring,wchar_t*(Wforwide)
T:
TCHARstring,TCHAR*
OLE:
OLECHARstring,OLECHAR*(inpractice,equivalenttoW)
BSTR:
BSTR(usedasthedestinationtypeonly)
所以,W2A()宏把一个Unicode字符串转换成一个MBCS字符串。
T2CW()宏把一个TCHAR字符串转转成一个Unicode字符串常量。
为了使用这些宏,需要先包含atlconv.h头文件。
你甚至可以在非ATL工程中包含这个头文件来使用其中定义的宏,因为这个头文件独立于ATL中的其他部分,不需要一个_Module全局变量。
当你在一个函数中使用转换宏时,需要把USES_CONVERSION宏放在函数的开头。
它定义了转换宏所需的一些局部变量。
当转换的目的类型是除了BSTR以外的其他类型时,被转换的字符串是存在栈中的。
所以,如果你想让字符串的生命周期比当前的函数长,你需要把这个字符串拷贝到其他的字符串类中。
当目的类型是BSTR时,内存不会自动被释放,你必须把返回值赋给一个BSTR变量或者一个BSTR封装类以避免内存泄漏。
下面是一些各种转换宏的使用例子:
//Functionstakingvariousstrings:
voidFoo(LPCWSTRwstr);
voidBar(BSTRbstr);
//Functionsreturningstrings:
voidBaz(BSTR*pbstr);
#include
usingstd:
string;
USES_CONVERSION;
//declarelocalsusedbytheATLmacros
//Example1:
SendanMBCSstringtoFoo()
LPCSTRpsz1="
stringstr
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+ 字符串 完全 指引 封装