Tuesday, April 12, 2011

C++字符串完全指南 - STL和ATL类


ref: 
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; // 拷贝CComBSTR
CComBSTR bs4;
bs4.LoadString ( IDS_SOME_STR ); // 从字符串表加载
// 数据萃取
BSTR bstr1 = bs1; // 返回内部BSTR,但不可修改!
BSTR bstr2 = (BSTR) bs1; // cast ok, 同上
BSTR bstr3 = bs1.Copy(); // 拷贝bs1, 返回BSTR
BSTR 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 = ... // 从某种类型初始化 v4
CComBSTR 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) ); // 同上,分配到CComBSTR
Bar ( bs1 );
  Bar ( bs2 );
SysFreeString ( bs1 );      // 释放bs1
  // 不必释放bs2,由CComBSTR释放。
// 示例3:转换由Baz()返回的BSTR
BSTR bs3 = NULL;
string str2;
Baz ( &bs3 );          // Baz() 填充bs3内容
str2 = W2CA(bs3);      // 转换为MBCS字符串
  SysFreeString ( bs3 ); // 释放bs3
}

可以看到,向一个需要某种类型参数的函数传递另一种类型的参数,用宏转换是非常方便的。

No comments:

Post a Comment