C++字符串完全指南(2) - 各种字符串类(一)

 

前言

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。
 

你可能感兴趣的:(C++字符串完全指南(2) - 各种字符串类(一))