MFC类
CString
因为一个MFC CString类的对象包含TCHAR类型的字符,所以确切的字符类型取决于你所定义的预处理符号。大体来说,CString 很像STL string,这意味着你必须把它当成不透明的对象,只能使用CString提供的方法来修改CString对象。CString有一个string所不具备的优点:CString具有接收MBCS和Unicode两种字符串的构造函数,它还有一个LPCTSTR转换符,所以你可以把CString对象直接传给一个接收LPCTSTR的函数而不需要调用c_str()函数。
上面划线的语句可以被编译,在这种情况下,甚至可以正常起作用。但这并不意味着这行代码是正确的。通过使用非常量转换,你已经破坏了面向对象的封装,并对CString的内部实现作了某些假定。如果你有这样的转换习惯,你终将会陷入代码崩溃的境地。你会想代码为什么不能正常工作了,因为你到处都使用同样的代码而那些代码看起来是正确的。
你知道人们总是抱怨现在的软件的bug是多么的多吗?软件中的bug是因为程序员写了不正确的代码。难道你真的想写一些你知道是错误的代码来为所有的软件都满是bug这种认识做贡献吗?花些时间来学习使用CString的正确方法让你的代码在任何时间都正常工作把。
CString 有两个函数来从一个 CString 创建一个 BSTR。它们是 AllocSysString() 和SetSysString()。
COleVariant和CComVariant.很相似。COleVariant继承自VARIANT,所以它可以传给接收VARIANT的函数。然而,不像CComVariant,COleVariant只有一个LPCTSTR构造函数。没有对LPCSTR 和LPCWSTR的构造函数。在大多数情况下这不是一个问题,因为不管怎样你的字符串很可能是LPCTSTRs,但这是一个需要意识到的问题。COleVariant还有一个接收CString参数的构造函数。
WTL 类
CString
WTL的CString的行为和MFC的 CString完全一样,所以你可以参考上面关于MFC的 CString的介绍。
CLR 和 VC 7 类
System::String是用来处理字符串的.NET类。在内部,一个String对象包含一个不可改变的字符串序列。任何对String对象的操作实际上都是返回了一个新的String对象,因为原始的对象是不可改变的。String的一个特性是如果你有不止一个String对象包含相同的字符序列,它们实际上是指向相同的对象的。相对于C++的使用扩展是增加了一个新的字符串常量前缀S,S用来代表一个受控的字符串常量(a managed string literal)。
在 printf-style 格式函数中使用字符串类
当你在printf()或者类似的函数中使用字符串封装类时你必须十分小心。这些函数包括sprintf()和它的变体,还有TRACE和ATLTRACE宏。因为这些函数没有对添加的参数的类型检查,你必须小心,只能传给它们C语言风格的字符串指针,而不是一个完整的字符串类。
例如,要把一个_bstr_t 字符串传给ATLTRACE(),你必须使用显式转换(LPCSTR) 或者(LPCWSTR):
CString
因为一个MFC CString类的对象包含TCHAR类型的字符,所以确切的字符类型取决于你所定义的预处理符号。大体来说,CString 很像STL string,这意味着你必须把它当成不透明的对象,只能使用CString提供的方法来修改CString对象。CString有一个string所不具备的优点:CString具有接收MBCS和Unicode两种字符串的构造函数,它还有一个LPCTSTR转换符,所以你可以把CString对象直接传给一个接收LPCTSTR的函数而不需要调用c_str()函数。
// ConstructingCString s1 = "char string"; // construct from a LPCSTRCString s2 = L"wide char string"; // construct from a LPCWSTRCString s3 ( '' '', 100 ); // pre-allocate a 100-byte buffer, fill with spacesCString s4 = "New window text"; // You can pass a CString in place of an LPCTSTR: SetWindowText ( hwndSomeWindow, s4 ); // Or, equivalently, explicitly cast the CString: SetWindowText ( hwndSomeWindow, (LPCTSTR) s4 );你可以从你的字符串表中装载一个字符串,CString的一个构造函数和LoadString()函数可以完成它。Format()方法能够从字符串表中随意的读取一个具有一定格式的字符串。
// Constructing/loading from string tableCString s5 ( (LPCTSTR) IDS_SOME_STR ); // load from string tableCString s6, s7; // Load from string table. s6.LoadString ( IDS_SOME_STR ); // Load printf-style format string from the string table: s7.Format ( IDS_SOME_FORMAT, "bob", nSomeStuff, ... );第一个构造函数看起来有点奇怪,但是这实际上是文档说明的装入一个字符串的方法。 注意,对一个CString变量,你可以使用的唯一合法转换符是LPCTSTR。转换成LPTSTR(非常量指针)是错误的。养成把一个CString变量转换成LPTSTR的习惯将会给你带来伤害,因为当你的程序后来崩溃时,你可能不知道为什么,因为你到处都使用同样的代码而那时它们都恰巧正常工作。正确的得到一个指向缓冲区的非常量指针的方法是调用GetBuffer()方法。下面是正确的用法的一个例子,这段代码是给一个列表控件中的项设定文字:
CString str = _T("new text");LVITEM item = {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(); // return control of the buffer to strpszText成员是一个LPTSTR变量,一个非常量指针,因此你需要对str调用GetBuffer()。GetBuffer()的参数是你需要CString为缓冲区分配的最小长度。如果因为某些原因,你需要一个可修改的缓冲区来存放1K TCHARs,你需要调用GetBuffer(1024)。把0作为参数时,GetBuffer()返回的是指向字符串当前内容的指针。
上面划线的语句可以被编译,在这种情况下,甚至可以正常起作用。但这并不意味着这行代码是正确的。通过使用非常量转换,你已经破坏了面向对象的封装,并对CString的内部实现作了某些假定。如果你有这样的转换习惯,你终将会陷入代码崩溃的境地。你会想代码为什么不能正常工作了,因为你到处都使用同样的代码而那些代码看起来是正确的。
你知道人们总是抱怨现在的软件的bug是多么的多吗?软件中的bug是因为程序员写了不正确的代码。难道你真的想写一些你知道是错误的代码来为所有的软件都满是bug这种认识做贡献吗?花些时间来学习使用CString的正确方法让你的代码在任何时间都正常工作把。
CString 有两个函数来从一个 CString 创建一个 BSTR。它们是 AllocSysString() 和SetSysString()。
// Converting to BSTRCString s5 = "Bob!";BSTR bs1 = NULL, bs2 = NULL; bs1 = s5.AllocSysString(); s5.SetSysString ( &bs2 ); SysFreeString ( bs1 ); SysFreeString ( bs2 );COleVariant
COleVariant和CComVariant.很相似。COleVariant继承自VARIANT,所以它可以传给接收VARIANT的函数。然而,不像CComVariant,COleVariant只有一个LPCTSTR构造函数。没有对LPCSTR 和LPCWSTR的构造函数。在大多数情况下这不是一个问题,因为不管怎样你的字符串很可能是LPCTSTRs,但这是一个需要意识到的问题。COleVariant还有一个接收CString参数的构造函数。
// ConstructingCString s1 = _T("tchar string");COleVariant v1 = _T("Bob"); // construct from an LPCTSTRCOleVariant v2 = s1; // copy from a CString像CComVariant一样,你必须直接访问VARIANT的成员。如果需要把VARIANT转换成一个字符串,你应该使用ChangeType()方法。然而,COleVariant::ChangeType()如果失败会抛出异常,而不是返回一个表示失败的HRESULT代码。
// Extracting dataCOleVariant v3 = ...; // fill in v3 from somewhereBSTR bs = NULL; try { v3.ChangeType ( VT_BSTR ); bs = v3.bstrVal; } catch ( COleException* e ) { // error, couldn''t convert } SysFreeString ( bs );

CString
WTL的CString的行为和MFC的 CString完全一样,所以你可以参考上面关于MFC的 CString的介绍。

System::String是用来处理字符串的.NET类。在内部,一个String对象包含一个不可改变的字符串序列。任何对String对象的操作实际上都是返回了一个新的String对象,因为原始的对象是不可改变的。String的一个特性是如果你有不止一个String对象包含相同的字符序列,它们实际上是指向相同的对象的。相对于C++的使用扩展是增加了一个新的字符串常量前缀S,S用来代表一个受控的字符串常量(a managed string literal)。
// ConstructingString* ms = S"This is a nice managed string";你可以传递一个非受控的字符串来创建一个String对象,但是样会比使用受控字符串来创建String对象造成效率的微小损失。这是因为所有以S作为前缀的相同的字符串实例都代表同样的对象,但这对非受控对象是不适用的。下面的代码清楚地阐明了这一点:
String* ms1 = S"this is nice";String* ms2 = S"this is nice";String* ms3 = L"this is nice"; Console::WriteLine ( ms1 == ms2 ); // prints true Console::WriteLine ( ms1 == ms3); // prints false正确的比较可能没有使用S前缀的字符串的方法是使用String::CompareTo()
Console::WriteLine ( ms1->CompareTo(ms2) ); Console::WriteLine ( ms1->CompareTo(ms3) );上面的两行代码都会打印0,0表示两个字符串相等。 String和MFC 7 CString之间的转换是很容易的。CString有一个向LPCTSTR的转换操作,而String有两个接收char* 和 wchar_t*的构造函数,因此你可以把一个CString变量直接传给一个String的构造函数。
CString s1 ( "hello world" );String* s2 ( s1 ); // copy from a CString反方向的转换也很类似
String* s1 = S"Three cats";CString s2 ( s1 );这也许会使你感到一点迷惑,但是它确实是起作用的。因为从VS.NET 开始,CString 有了一个接收String 对象的构造函数。
CStringT ( System::String* pString );对于一些快速操作,你可能想访问底层的字符串:
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语言风格的字符串指针,而不是一个完整的字符串类。
例如,要把一个_bstr_t 字符串传给ATLTRACE(),你必须使用显式转换(LPCSTR) 或者(LPCWSTR):
_bstr_t bs = L"Bob!";ATLTRACE("The string is: %s in line %d/n", (LPCSTR) bs, nLine);
如果你忘了使用转换符而把整个_bstr_t对象传给了函数,将会显示一些毫无意义的输出,因为_bstr_t保存的内部数据会全部被输出。
所有类的总结
两个字符串类之间进行转换的常用方式是:先把源字符串转换成一个C语言风格的字符串指针,然后把这个指针传递给目的类型的构造函数。下面这张表显示了怎样把一个字符串转换成一个C语言风格的字符串指针以及哪些类具有接收C语言风格的字符串指针的构造函数。
Class | string type | convert to char*? | convert to const char*? | 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 cast 1 | 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 | yes 4 | yes | yes |
CString | TCHAR | no6 | in MBCS builds, cast | no 6 | in Unicode builds, cast | no5 | yes | yes |
COleVariant | BSTR | no | no | no | yes 4 | yes 4 |
in MBCS builds |
in Unicode builds |
|