CString( 八 )

你的代码可以移植到任何语言中去了 。LoadString 方法需要一个字元串资源的 ID 作为参数,然后它从 STRINGTABLE 中取出它对应的字元串,赋值给 CString 对象 。CString 对象的构造函式还有一个更加聪明的特徵可以简化 STRINGTABLE 的使用 。这个用法在 CString::CString 的文档中没有指出,但是在构造函式的示例程式中使用了 。(为什幺这个特性没有成为正式文档的一部分,而是放在了一个例子中,我记不得了!)——【译者注:从这句话看,作者可能是CString的设计者 。其实前面还有一句类似的话 。说他没有对使用GetBuffer(0)获得的指针指向的地址是否可读做有效性检查 】 。这个特徵就是:如果你将一个字元串资源的ID强制类型转换为 LPCTSTR,将会隐含调用 LoadString 。因此,下面两个构造字元串的例子具有相同的效果,而且其 ASSERT 在debug模式下不会被触发:CString s;s.LoadString(IDS_WHATEVER);CString t((LPCTSTR)IDS_WHATEVER);ASSERT(s == t);//不会被触发,说明s和t是相同的 。你可能会想:这怎幺可能工作呢?我们怎幺能把 STRINGTABLE ID 转化成一个指针呢?很简单:所有的字元串 ID 都在1~65535这个範围内,也就是说,它所有的高位都是0,而我们在程式中所使用的指针是不可能小于65535的,因为程式的低 64K 记忆体永远也不可能存在的,如果你试图访问0x00000000到0x0000FFFF之间的记忆体,将会引发一个记忆体越界错误 。所以说1~65535的值不可能是一个记忆体地址,所以我们可以用这些值来作为字元串资源的ID 。我倾向于使用 MAKEINTRESOURCE 宏显式地做这种转换 。我认为这样可以让代码更加易于阅读 。这是个只适合在 MFC 中使用的标準宏 。你要记住,大多数的方法即可以接受一个 UINT 型的参数,也可以接受一个 LPCTSTR 型的参数,这是依赖 C++ 的重载功能做到的 。C++重载函式带来的弊端就是造成所有的强制类型转化都需要显示声明 。同样,你也可以给很多种结构只传递一个资源名 。CString s;s.LoadString(IDS_WHATEVER);CString t(MAKEINTRESOURCE(IDS_WHATEVER));ASSERT(s == t);告诉你吧:我不仅只是在这里鼓吹,事实上我也是这幺做的 。在我的代码中,你几乎不可能找到一个字元串,当然,那些只是偶然在调试中出现的或者和语言无关的字元串除外 。临时对象这是出现在 microsoft.public.vc.mfc 新闻组中的一个小问题,我简单的提一下,这个问题是有个程式设计师需要往注册表中写入一个字元串,他写道:我试着用 RegSetValueEx() 设定一个注册表键的值,但是它的结果总是令我困惑 。当我用char[]声明一个变数时它能正常工作,但是当我用 CString 的时候,总是得到一些垃圾:"YYYY...YYYYYY"为了确认是不是我的 CString 数据出了问题,我试着用 GetBuffer,然后强制转化成 char*,LPCSTR 。GetBuffer 返回的值是正确的,但是当我把它赋值给 char* 时,它就变成垃圾了 。以下是我的程式段: char* szName = GetName().GetBuffer(20);RegSetValueEx(hKey,"Name",0,REG_SZ,(CONST BYTE *)szName,strlen(szName + 1));这个 Name 字元串的长度小于 20,所以我不认为是 GetBuffer 的参数的问题 。真让人困惑,请帮帮我 。亲爱的 Frustrated,你犯了一个相当微妙的错误,聪明反被聪明误,正确的代码应该象下面这样:CString Name = GetName();RegSetValueEx(hKey,_T("Name"),0,REG_SZ,(CONST BYTE *)(LPCTSTR)Name,(Name.GetLength()+ 1) * sizeof(TCHAR));为什幺我写的代码能行而你写的就有问题呢?主要是因为当你调用 GetName 时返回的 CString 对象是一个临时对象 。参见:《C++ Reference manual》§12.2在一些环境中,编译器有必要创建一个临时对象,这样引入临时对象是依赖于实现的 。如果编译器引入的这个临时对象所属的类有构造函式的话,编译器要确保这个类的构造函式被调用 。同样的,如果这个类声明有析构函式的话,也要保证这个临时对象的析构函式被调用 。编译器必须保证这个临时对象被销毁了 。被销毁的确切地点依赖于实现.....这个析构函式必须在退出创建该临时对象的範围之前被调用 。大部分的编译器是这样设计的:在临时对象被创建的代码的下一个执行步骤处隐含调用这个临时对象的析构函式,实现起来,一般都是在下一个分号处 。因此,这个 CString 对象在 GetBuffer 调用之后就被析构了(顺便提一句,你没有理由给 GetBuffer 函式传递一个参数,而且没有使用ReleaseBuffer 也是不对的) 。所以 GetBuffer 本来返回的是指向这个临时对象中字元串的地址的指针,但是当这个临时对象被析构后,这块记忆体就被释放了 。然后 MFC 的调试记忆体分配器会重新为这块记忆体全部填上 0xDD,显示出来刚好就是"Y"符号 。在这个时候你向注册表中写数据,字元串的内容当然全被破坏了 。我们不应该立即把这个临时对象转化成 char* 类型,应该先把它保存到一个 CString 对象中,这意味着把临时对象複製了一份,所以当临时的 CString 对象被析构了之后,这个 CString 对象中的值依然保存着 。这个时候再向注册表中写数据就没有问题了 。此外,我的代码是具有 Unicode 意识的 。那个操作注册表的函式需要一个位元组大小,使用lstrlen(Name+1) 得到的实际结果对于 Unicode字元来说比 ANSI 字元要小一半,而且它也不能从这个字元串的第二个字元起开始计算,也许你的本意是 lstrlen(Name) + 1(OK,我承认,我也犯了同样的错误!) 。不论如何,在 Unicode 模式下,所有的字元都是2个位元组大小,我们需要处理这个问题 。微软的文档令人惊讶地对此保持缄默:REG_SZ 的值究竟是以位元组计算还是以字元计算呢?我们假设它指的是以位元组为单位计算,你需要对你的代码做一些修改来计算这个字元串所含有的位元组大小 。效率CString 的一个问题是它确实掩藏了一些低效率的东西 。从另外一个方面讲,它也确实可以被实现得更加高效,你可能会说下面的代码: CString s = SomeCString1;s += SomeCString2;s += SomeCString3;s += ",";s += SomeCString4;