C++字符串完全指南 - 各种字符串类

来源:互联网 发布:福鼎广电网络客服电话 编辑:程序博客网 时间:2024/04/27 23:46
 
C++字符串完全指南 - 各种字符串类(一)

前言
 
C语言的字符串容易出错,难以管理,并且往往是黑客到处寻找的目标。于是,出现了许多字符串包装类。可惜,人们并不很清楚什么情况下该用哪个类,也不清楚如何将C语言字符串转换到包装类。
本文涉及到Win32 API,MFC,STL,WTL和Visual C++运行库中使用到的所有的字符串类型。说明各个类的用法,如何构造对象,如何进行类转换等等。Nish为本文提供了Visual C++ 7的managed string 类的用法。

阅读本文之前,应完全理解本指南第一部分中阐述的字符类型和编码。


字符串类的首要原则:


不要随便使用类型强制转换,除非转换的类型是明确由文档规定的。

之所以撰写字符串指南这二篇文章,是因为常有人问到如何将X类型的字符串转换到Z类型。提问者使用了强制类型转换 (cast),但不知道为什么不能转换成功。各种各样的字符串类型,特别是BSTR,在任何场合都不是三言二语可以讲清的。因此,我以为这些提问者是想让强制类型转换来处理一切。

除非明确规定了转换算子,不要将任何其它类型数据强制转换为string。一个字符串不能用强制类型转换到string类。例如:


void SomeFunc ( LPCWSTR widestr );
main()
{

 SomeFunc ( (LPCWSTR) "C://foo.txt" );
// 错!
}

这段代码100%错误。它可以通过编译,因为类型强制转换超越了编译器的类型检验。但是,能够通过编译,并不证明代码是正确的。

下面,我将指出什么时候用类型强制转换是合理的。


C语言字符串与类型定义


如指南的第一部分所述,Windows API定义了TCHAR术语。它可用于MBCS或Unicode编码字符,取决于预处理设置为_MBCS 或 _UNICODE标记。关于TCHAR的详细说明请阅指南的第一部分。为便于叙述,下面给出字符类型定义:
 

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 (constchar*) LPWSTR string of WCHAR (WCHAR*) LPCWSTR constant string of WCHAR (const WCHAR*) LPTSTR string of TCHAR (TCHAR*) LPCTSTR constant string of TCHAR (const TCHAR*)

另外还有一个字符类型OLECHAR。这是一种对象链接与嵌入的数据类型(比如嵌入Word文档)。这个类型通常定义为wchar_t。如果将预处理设置定义为OLE2ANSI,OLECHAR将被定义为char类型。现在已经不再定义OLE2ANSI(它只在MFC 3以前版本中使用),所以我将OLECHAR作为Unicode字符处理。
下面是与OLECHAR相关的类型定义:

Type

Meaning

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

还有以下二个宏让相同的代码能够适用于MBCS和Unicode编码:

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声明字符串。BSTR有一些缺陷,所以我在这里让它独立成章。

BSTR 是Pascal类型字符串(字符串长度值显式地与数据存放在一起)和C类型字符串(字符串长度必须通过寻找到结尾零字符来计算)的混合型字符串。BSTR 属于Unicode字符串,字符串中预置了字符串长度值,并且用一个零字符来结尾。下面是一个"Bob"的 BSTR字符串: 



注意,字符串长度值是一个DWORD类型值,给出字符串的字节长度,但不包括结尾零。在上例,"Bob"含有3个 Unicode字符(不计结尾零),6个字节长。因为明确给出了字符串长度,所以当BSTR数据在不同的处理器和计算机之间传送时,COM库能够知道应该传送的数据量。

附带说一下,BSTR可以包含任何数据块,不单是字符。它甚至可以包容内嵌零字符数据。这些不在本文讨论范围。
C++中的BSTR变量其实就是指向字符串首字符的指针。BSTR是这样定义的:


typedef OLECHAR* BSTR;

这个定义很糟糕,因为事实上BSTR与Unicode字符串不一样。有了这个类型定义,就越过了类型检查,可以混合使用LPOLESTR和BSTR。向一个需要LPCOLESTR (或 LPCWSTR)类型数据的函数传递BSTR数据是安全的,反之则不然。所以要清楚了解函数所需的字符串类型,并向函数传递正确类型的字符串。

要知道为什么向一个需要BSTR类型数据的函数传递LPCWSTR类型数据是不安全的,就别忘了BSTR必须在字符串开头的四个字节保留字符串长度值。但 LPCWSTR字符串中没有这个值。当其它的处理过程(如Word)要寻找BSTR的长度值时就会找到一堆垃圾或堆栈中的其它数据或其它随机数据。这就导致方法失效,当长度值太大时将导致崩溃。

许多应用接口都使用BSTR,但都用到二个最重要的函数来构造和析构BSTR。就是SysAllocString ()和SysFreeString()函数。SysAllocString()将Unicode字符串拷贝到BSTR,SysFreeString()释放BSTR。示例如下:


BSTR bstr = NULL;
bstr = SysAllocString ( L"Hi Bob!" );
if ( NULL == bstr )
// 内存溢出
// 这里使用bstr

SysFreeString ( bstr );

当然,各种BSTR包装类都会小心地管理内存。
自动接口中的另一个数据类型是VARIANT。它用于在无类型语言,诸如JScript,VBScript,以及 Visual Basic,之间传递数据。VARIANT可以包容许多不用类型的数据,如long和IDispatch*。如果VARIANT包含一个字符串,这个字符串是BSTR类型。在下文的VARIANT包装类中我还会谈及更多的VARIANT。

_bstr_t

 

字符串包装类

  我已经说明了字符串的各种类型,现在讨论包装类。对于每个包装类,我都会说明它的对象构造过程和如何转换成C类型字符串指针。应用接口的调用,或构造另一个不同类型的字符串类,大多都要用到C类型指针。本文不涉及类的其它操作,如排序和比较等。

  再强调一下,在完全了解转换结果之前不要随意使用强制类型转换。

CRT类

_bstr_t

  _bstr_t 是BSTR的完全包装类。实际上,它隐含了BSTR。它提供多种构造函数,能够处理隐含的C类型字符串。但它本身却不提供BSTR的处理机制,所以不能作为COM方法的输出参数[out]。如果要用到BSTR* 类型数据,用ATL的CComBSTR类更为方便。

  _bstr_t 数据可以传递给需要BSTR数据的函数,但必须满足以下三个条件:

  首先,_bstr_t 具有能够转换为wchar_t*类型数据的函数。

  其次,根据BSTR定义,使得wchar_t* 和BSTR对于编译器来说是相同的。

  第三,_bstr_t内部保留的指向内存数据块的指针 wchar_t* 要遵循BSTR格式。

  满足这些条件,即使没有相应的BSTR转换文档,_bstr_t 也能正常工作。示例如下:

// 构造
_bstr_t bs2 = L"wide char string"; // 从LPCWSTR构造
_bstr_t bs1 = "char string";// 从LPCSTR构造
_variant_t v = "Bob";
_bstr_t bs3 = bs1;// 拷贝另一个 _bstr_t
_bstr_t bs4 = v;// 从一个含有字符串的 _variant_t 构造
// 数据萃取
LPCSTR psz1 = bs1;            // 自动转换到MBCS字符串
LPCSTR psz2 = (LPCSTR) bs1;    // cast OK, 同上
LPCWSTR pwsz1 = bs1;                 // 返回内部的Unicode字符串
LPCWSTR pwsz2 = (LPCWSTR) bs1;// cast OK, 同上
BSTR    bstr = bs1.copy();    // 拷贝bs1, 返回BSTR
// ...
SysFreeString ( bstr );

  注意,_bstr_t 也可以转换为char* 和 wchar_t*。这是个设计问题。虽然char* 和 wchar_t*不是常量指针,但不能用于修改字符串,因为可能会打破内部BSTR结构。

_variant_t
_variant_t

  _variant_t 是VARIANT的完全包装类。它提供多种构造函数和数据转换函数。本文仅讨论与字符串有关的操作。

// 构造
_variant_t v1 = "char string"; // 从LPCSTR 构造
_variant_t v2 = L"wide char string"; // 从LPCWSTR 构造
_bstr_t bs1 = "Bob";
_variant_t v3 = bs1; // 拷贝一个 _bstr_t 对象
// 数据萃取
_bstr_t bs2 = v1; // 从VARIANT中提取BSTR
_bstr_t bs3 = ( _bstr_t ) v1; // cast OK, 同上

  注意,_variant_t 方法在转换失败时会抛出异常,所以要准备用catch 捕捉_com_error异常。

  另外要注意 _variant_t 不能直接转换成MBCS字符串。要建立一个过渡的_bstr_t 变量,用其它提供转换Unicode到MBCS的类函数,或ATL转换宏来转换。

  与_bstr_t 不同,_variant_t 数据可以作为参数直接传送给COM方法。_variant_t 继承了VARIANT类型,所以在需要使用VARIANT的地方使用_variant_t 是C++语言规则允许的。

STL类

 

STL类

STL只有一个字符串类,即basic_string。basic_string管理一个零结尾的字符数组。字符类型由模板参数决定。通常,basic_string被处理为不透明对象。可以获得一个只读指针来访问缓冲区,但写操作都是由basic_string的成员函数进行的。

basic_string预定义了二个特例:string,含有char类型字符;which,含有wchar_t类型字符。没有内建的TCHAR特例,可用下面的代码实现:

// 特例化typedef basic_string tstring; // TCHAR字符串// 构造string str = "char string"; // 从LPCSTR构造wstring wstr = L"wide char string"; // 从LPCWSTR构造tstring tstr = _T("TCHAR string"); // 从LPCTSTR构造// 数据萃取LPCSTR psz = str.c_str(); // 指向str缓冲区的只读指针LPCWSTR pwsz = wstr.c_str(); // 指向wstr缓冲区的只读指针LPCTSTR ptsz = tstr.c_str(); // 指向tstr缓冲区的只读指针

与_bstr_t 不同,basic_string不能在字符集之间进行转换。但是如果一个构造函数接受相应的字符类型,可以将由c_str()返回的指针传递给这个构造函数。例如:

// 从basic_string构造_bstr_t 
_bstr_t bs1 = str.c_str();
// 从LPCSTR构造 _bstr_t_bstr_t bs2 = wstr.c_str(); // 从LPCWSTR构造 _bstr_t
ATL类
CComBSTR

CComBSTR 是ATL的BSTR包装类。某些情况下比_bstr_t 更有用。最主要的是,CComBSTR允许操作隐含BSTR。就是说,传递一个CComBSTR对象给COM方法时,CComBSTR对象会自动管理BSTR内存。例如,要调用下面的接口函数:

// 简单接口struct IStuff : public IUnknown{  // 略去COM程序...  STDMETHOD(SetText)(BSTR bsText);  STDMETHOD(GetText)(BSTR* pbsText);};

CComBSTR 有一个BSTR操作方法,能将BSTR直接传递给SetText()。还有一个引用操作(operator &)方法,返回BSTR*,将BSTR*传递给需要它的有关函数。

CComBSTR bs1;
CComBSTR bs2 = "new text";
pStuff->GetText ( &bs1 );
// ok, 取得内部BSTR地址 pStuff->SetText ( bs2 ); // ok, 调用BSTR转换 pStuff->SetText ( (BSTR) bs2 ); // cast ok, 同上

CComVariant
CComBSTR有类似于 _bstr_t 的构造函数。但没有内建MBCS字符串的转换函数。可以调用ATL宏进行转换。

// 构造CComBSTR bs1 = "char string"; // 从LPCSTR构造CComBSTR bs2 = L"wide char string"; // 从LPCWSTR构造CComBSTR bs3 = bs1; // 拷贝CComBSTRCComBSTR bs4;bs4.LoadString ( IDS_SOME_STR ); // 从字符串表加载// 数据萃取BSTR bstr1 = bs1; // 返回内部BSTR,但不可修改!BSTR bstr2 = (BSTR) bs1; // cast ok, 同上BSTR bstr3 = bs1.Copy(); // 拷贝bs1, 返回BSTRBSTR bstr4;bstr4 = bs1.Detach(); // bs1不再管理它的BSTR// ...SysFreeString ( bstr3 );SysFreeString ( bstr4 );

上面的最后一个示例用到了Detach()方法。该方法调用后,CComBSTR对象就不再管理它的BSTR或其相应内存。所以bstr4就必须调用SysFreeString()。

最后讨论一下引用操作符(operator &)。它的超越使得有些STL集合(如list)不能直接使用CComBSTR。在集合上使用引用操作返回指向包容类的指针。但是在CComBSTR上使用引用操作,返回的是BSTR*,不是CComBSTR*。不过可以用ATL的CAdapt类来解决这个问题。例如,要建立一个CComBSTR的队列,可以声明为:

  std::list< CAdapt> bstr_list;

CAdapt 提供集合所需的操作,是隐含于代码的。这时使用bstr_list 就象在操作一个CComBSTR队列。

CComVariant

CComVariant 是VARIANT的包装类。但与 _variant_t 不同,它的VARIANT不是隐含的,可以直接操作类里的VARIANT成员。CComVariant 提供多种构造函数和多类型操作。这里只介绍与字符串有关的操作。

// 构造CComVariant v1 = "char string";       // 从LPCSTR构造CComVariant v2 = L"wide char string"; // 从LPCWSTR构造CComBSTR bs1 = "BSTR bob";CComVariant v3 = (BSTR) bs1;          // 从BSTR拷贝// 数据萃取CComBSTR bs2 = v1.bstrVal;            // 从VARIANT提取BSTR

跟_variant_t 不同,CComVariant没有不同VARIANT类型之间的转换操作。必须直接操作VARIANT成员,并确定该VARIANT的类型无误。调用ChangeType()方法可将CComVariant数据转换为BSTR。

CComVariant v4 = ... // 从某种类型初始化 v4CComBSTR bs3;if ( SUCCEEDED( v4.ChangeType ( VT_BSTR ) ))    bs3 = v4.bstrVal;

跟 _variant_t 一样,CComVariant不能直接转换为MBCS字符串。要建立一个过渡的_bstr_t 变量,用其它提供转换Unicode到MBCS的类函数,或ATL转换宏来转换。

ATL转换宏

ATL的字符串转换宏可以方便地转换不同编码的字符,用在函数中很有效。宏按照[source type]2[new type] 或 [source type]2C[new type]格式命名。后者转换为一个常量指针 (名字内含"C")。类型缩写如下:


 A:MBCS字符串,char* (A for ANSI)
 W:Unicode字符串,wchar_t* (W for wide)
 T:TCHAR字符串,TCHAR*
 OLE:OLECHAR字符串,OLECHAR* (实际等于W)
 BSTR:BSTR (只用于目的类型)

例如,W2A() 将Unicode字符串转换为MBCS字符串,T2CW()将TCHAR字符串转换为Unicode字符串常量。

要使用宏转换,程序中要包含atlconv.h头文件。可以在非ATL程序中使用宏转换,因为头文件不依赖其它的ATL,也不需要 _Module全局变量。如在函数中使用转换宏,在函数起始处先写上USES_CONVERSION宏。它表明某些局部变量由宏控制使用。

转换得到的结果字符串,只要不是BSTR,都存储在堆栈中。如果要在函数外使用这些字符串,就要将这些字符串拷贝到其它的字符串类。如果结果是BSTR,内存不会自动释放,因此必须将返回值分配给一个BSTR变量或BSTR的包装类,以避免内存泄露。

下面是若干宏转换示例:

// 带有字符串的函数:void Foo ( LPCWSTR wstr );void Bar ( BSTR bstr );// 返回字符串的函数:void Baz ( BSTR* pbstr );#include main(){using std::string;USES_CONVERSION;    // 声明局部变量由宏控制使用// 示例1:送一个MBCS字符串到Foo()LPCSTR psz1 = "Bob";string str1 = "Bob";Foo ( A2CW(psz1) );  Foo ( A2CW(str1.c_str()) );// 示例2:将MBCS字符串和Unicode字符串送到Bar()LPCSTR psz2 = "Bob";LPCWSTR wsz = L"Bob";BSTR bs1;CComBSTR bs2;bs1 = A2BSTR(psz2);         // 创建 BSTR  bs2.Attach ( W2BSTR(wsz) ); // 同上,分配到CComBSTRBar ( bs1 );  Bar ( bs2 );SysFreeString ( bs1 );      // 释放bs1  // 不必释放bs2,由CComBSTR释放。// 示例3:转换由Baz()返回的BSTRBSTR bs3 = NULL;string str2;Baz ( &bs3 );          // Baz() 填充bs3内容str2 = W2CA(bs3);      // 转换为MBCS字符串  SysFreeString ( bs3 ); // 释放bs3}可以看到,向一个需要某种类型参数的函数传递另一种类型的参数,用宏转换是非常方便的。

MFC类

 

CString

MFC的CString含有TCHAR,它的实际字符类型取决于预处理标记的设置。通常,CString象STL字符串一样是不透明对象,只能用CString的方法来修改。CString比STL字符串更优越的是它的构造函数接受MBCS和Unicode字符串。并且可以转换为LPCTSTR,因此可以向接受LPCTSTR的函数直接传递CString对象,不必调用c_str()方法。

// 构造
CString s1 = "char string"; // 从LPCSTR构造
CString s2 = L"wide char string"; // 从LPCWSTR构造
CString s3 ( ' ', 100 ); // 预分配100字节,填充空格
CString s4 = "New window text";
// 可以在LPCTSTR处使用CString:
SetWindowText ( hwndSomeWindow, s4 );
// 或者,显式地做强制类型转换:
SetWindowText ( hwndSomeWindow, (LPCTSTR) s4 );

也可以从字符串表加载字符串。CString通过LoadString()来构造对象。用Format()方法可有选择地从字符串表读取一定格式的字符串。
// 从字符串表构造/加载
CString s5 ( (LPCTSTR) IDS_SOME_STR ); // 从字符串表加载
CString s6, s7;
// 从字符串表加载
s6.LoadString ( IDS_SOME_STR );
// 从字符串表加载打印格式的字符串
s7.Format ( IDS_SOME_FORMAT, "bob", nSomeStuff, ... );

第一个构造函数看上去有点怪,但它的确是文档标定的字符串加载方式。

注意,CString只允许一种强制类型转换,即强制转换为LPCTSTR。强制转换为LPTSTR (非常量指针)是错误的。按照老习惯,将CString强制转换为LPTSTR只能伤害自己。有时在程序中没有发现出错,那只是碰巧。转换到非常量指针的正确方法是调用GetBuffer()方法。

下面以往队列加入元素为例说明如何正确地使用CString:

CString str = _T("new text");
LVITEM item = {0};
item.mask = LVIF_TEXT;
item.iItem = 1;
item.pszText = (LPTSTR)(LPCTSTR) str; // 错!
item.pszText = str.GetBuffer(0); // 正确
ListView_SetItem ( &item );
str.ReleaseBuffer(); // 将队列返回给str

pszText成员是LPTSTR,一个非常量指针,因此要用str的GetBuffer()。GetBuffer()的参数是CString分配的最小缓冲区。如果要分配一个1K的TCHAR,调用GetBuffer(1024)。参数为0,只返回指向字符串的指针。

上面示例的出错语句可以通过编译,甚至可以正常工作,如果恰好就是这个类型。但这不证明语法正确。进行非常量的强制类型转换,打破了面向对象的封装原则,并逾越了CString的内部操作。如果你习惯进行这样的强制类型转换,终会遇到出错,可你未必知道错在何处,因为你到处都在做这样的转换,而代码也都能运行。

 

知道为什么人们总在抱怨有缺陷的软件吗?不正确的代码就臭虫的滋生地。然道你愿意编写明知有错的代码让臭虫有机可乘?还是花些时间学习CString的正确用法让你的代码能够100%的正确吧。

CString还有二个函数能够从CString中得到BSTR,并在必要时转换成Unicode。那就是AllocSysString()和SetSysString()。除了SetSysString()使用BSTR*参数外,二者一样。

// 转换成BSTR
CString s5 = "Bob!";
BSTR bs1 = NULL, bs2 = NULL;
bs1 = s5.AllocSysString();
s5.SetSysString ( &bs2 );
// ...
SysFreeString ( bs1 );
SysFreeString ( bs2 );

COleVariant 与CComVariant 非常相似。COleVariant 继承于VARIANT,可以传递给需要VARIANT的函数。但又与CComVariant 不同,COleVariant 只有一个LPCTSTR的构造函数,不提供单独的LPCSTR和LPCWSTR的构造函数。在大多情况下,没有问题,因为总是愿意把字符串处理为LPCTSTR。但你必须知道这点。COleVariant 也有接受CString的构造函数。

// 构造
CString s1 = _T("tchar string");
COleVariant v1 = _T("Bob"); // 从LPCTSTR构造
COleVariant v2 = s1; // 从CString拷贝

对于CComVariant,必须直接处理VARIANT成员,用ChangeType()方法在必要时将其转换为字符串。但是,COleVariant::ChangeType() 在转换失败时会抛出异常,而不是返回HRESULT的出错码。

// 数据萃取
COleVariant v3 = ...; // 从某种类型构造v3
BSTR bs = NULL;
try
{
v3.ChangeType ( VT_BSTR );
bs = v3.bstrVal;
}
catch ( COleException* e )
{
// 出错,无法转换
}
SysFreeString ( bs );

WTL类

WTL类
 
CString

WTL的CString与MFC的CString的行为完全相同,参阅上面关于MFC CString的说明即可。

CLR 及 VC 7 类

System::String 是.NET的字符串类。在其内部,String对象是一个不变的字符序列。任何操作String对象的String方法都返回一个新的String对象,因为原有的String对象要保持不变。String类有一个特性,当多个String都指向同一组字符集时,它们其实是指向同一个对象。Managed Extensions C++ 的字符串有一个新的前缀S,用来表明是一个managed string字符串。

// 构造
String* ms = S"This is a nice managed string";

可以用unmanaged string字符串来构造String对象,但不如用managed string构造String对象有效。原因是所有相同的具有S前缀的字符串都指向同一个对象,而unmanaged string没有这个特点。下面的例子可以说明得更清楚些:

String* ms1 = S"this is nice";
String* ms2 = S"this is nice";
String* ms3 = L"this is nice";
Console::WriteLine ( ms1 == ms2 ); // 输出true
Console::WriteLine ( ms1 == ms3); // 输出false

要与没有S前缀的字符串做比较,用String::CompareTo()方法来实现,如:

  Console::WriteLine ( ms1->CompareTo(ms2) );
Console::WriteLine ( ms1->CompareTo(ms3) );

二者都输出0,说明字符串相等。

在String和MFC 7的CString之间转换很容易。CString可以转换为LPCTSTR,String有接受char* 和 wchar_t* 的二种构造函数。因此可以直接把CString传递给String的构造函数:

  CString s1 ( "hello world" );
String* s2 ( s1 ); // 从CString拷贝

反向转换的方法也类似:

  String* s1 = S"Three cats";
CString s2 ( s1 );

可能有点迷惑。从VS.NET开始,CString有一个接受String对象的构造函数,所以是正确的。

  CStringT ( System::String* pString );

为了加速操作,有时可以用基础字符串(underlying string):

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()或其它类似功能的函数时要特别小心。包括sprintf()函数及其变种,以及TRACE 和ATLTRACE 宏。它们的参数都不做类型检验,一定要给它们传递C语言字符串,而不是整个string对象。

例如,要向ATLTRACE()传递一个_bstr_t 里的字符串,必须显式用(LPCSTR)或 (LPCWSTR)进行强制类型转换:

_bstr_t bs = L"Bob!";
  ATLTRACE("The string is: %s in line %d/n", (LPCSTR) bs, nLine);

如果忘了用强制类型转换,直接把整个 _bstr_t 对象传递给ATLTRACE,跟踪消息将输出无意义的东西,因为_bstr_t 变量内的所有数据都进栈了。

所有类的总结

常用的字符串类之间的转换方法是:将源字符串转换为C类型字符串指针,然后将该指针传递给目标类的构造函数。下面列出将字符串转换为C类型指针的方法,以及哪些类的构造函数接受C类型指针。

Class

string
type

convert to char*?

convert to constchar*?

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 可以转换为非常量指针,但对内部缓冲区的修改可能导致内存溢出,或在释放BSTR时导致内存泄露。
  2. bstr_t 的BSTR内含 wchar_t* 变量,所以可将const wchar_t* 转换到BSTR。但这个用法将来可能会改变,使用时要小心。
  3. 如果转换到BSTR失败,将抛出异常。
  4. 用ChangeType()处理VARIANT的bstrVal。在MFC,转换失败将抛出异常。
  5. 虽然没有BSTR的转换函数,但AllocSysString()可返回一个新的BSTR。
  6. 用GetBuffer()方法可临时得到一个非常量TCHAR指针。
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 二年级孩子不爱学习总爱玩怎么办 初三孩子学习不积极怎么办 孩子学习不积极应该怎么办 小孩子贪玩不写作业怎么办 老公懒不帮忙分担家务怎么办 宝宝不喜欢早教课程单一怎么办 孩子上课不听话顶撞老师怎么办 孩子不喜欢吃水果蔬菜怎么办 中学生顶撞家长顶撞老师怎么办 .cn孩子初中上课不专心怎么办 娃儿小学二年级做不来怎么办 孩子不愿意看书沉迷电子产品怎么办 孩子自律和自控性差怎么办 婴儿7个月不喜欢吃东西怎么办 半岁宝宝太活泼怎么办 6个月宝宝太活泼怎么办 做nt宝宝太活跃怎么办 静不下心来学习怎么办 初中的孩子不爱学习怎么办 小孩不爱读书不做作业怎么办 母亲性格内向儿子也是内向怎么办 我儿子不爱吃怎么办啊 孩子练字就是记不住怎么办 孩子不愿意和小朋友玩怎么办 丈夫去世了婆婆不喜欢儿媳妇怎么办 高考看不下去书怎么办 在东莞读书读不成高中怎么办 嗓子哑了怎么办土方法 小孩舌头太长太大讲话不清楚怎么办 一周岁不喝奶粉怎么办 一岁的宝宝不吃奶粉怎么办 小孩不爱吃饭怎么办吃什么药 小孩这几天不爱吃饭怎么办 宝宝这几天不爱吃饭怎么办 想看书看不进去怎么办 宝宝3岁不爱看书怎么办 4岁宝宝不爱看书怎么办 1岁宝宝不爱看书怎么办 孩子一看书就哭怎么办 我不想读大专了怎么办 一年级的小孩不爱学习怎么办