威尼斯www.9778.com-威尼斯正版官方网站

关于CString CtrlList::GetItemText返回值和CString比较的问题

日期:2019-12-25编辑作者:编程人生

自己起头化了一个CString对象,CStringb("已审查批准"卡塔尔国;然后在listcontrol列表用GetItemText重返八个CString对象,内容也是“已检查核对”,理论上来讲这八个指标应该同样的,可那八个CString平素不匹配

原著:Joseph M. Newcomer

翻译:littleloach

最早的文章出处:codeproject:CString Management

透过阅读本文你可以学习怎样有效地接纳 CString。

  CString 是朝气蓬勃种很有用的数据类型。它们异常的大程度上简化了MFC中的非常多操作,使得MFC在做字符串操作的时候实惠了广大。不管怎么着,使用CString有无数非同平常的才能,特别是对此纯C背景下走出来的程序猿来讲有一点点麻烦学习。那篇小说就来谈谈那些能力。
  使用CString可以让你对字符串的操作更是干脆俐落。这篇随笔不是CString的一心手册,但包罗了绝大超级多布满基本难题。

那篇小说包涵以下内容:

  1. CString 对象的延续

  2. 格式化字符串(包括 int 型转变为 CString )

  3. CString 型转产生 int 型
  4. CString 型和 char* 类型的相互转化
  5. char* 转化成 CString
  6. CString 转化成 char* 之大器晚成:使用LPCTSTTucson强逼转变
  7. CString 转化成 char* 之二:使用CString对象的GetBuffer方法
  8. CString 转化成 char* 之三: 和控件的接口
  9. CString 型转形成 BST奥迪Q5型;
  10. BSTGL450 型转形成 CString 型;
  11. VAGL450IANT 型转产生 CString 型;
  12. 载入字符串表财富;
  13. CString 和有的时候对象;
  14. CString 的效率;
  15. 总结

下边小编分别探究。

图片 1 1、CString 对象的连年

  能突显出 CString 类型方便性特点的多个上边就字符串的接二连三,使用 CString 类型,你能很有益于地三番一回五个字符串,正如上边包车型地铁事例:

CString gray("Gray");
CString cat("Cat");
CString graycat = gray + cat;

要比用上面包车型大巴法子好得多:

char gray[] = "Gray";
char cat[] = "Cat";
char * graycat = malloc(strlen(gray) + strlen(cat) + 1);
strcpy(graycat, gray);
strcat(graycat, cat);

图片 2 2、格式化字符串

  与其用 sprintf(卡塔尔 函数或 wsprintf(卡塔尔(قطر‎ 函数来格式化三个字符串,还不及用 CString 对象的Format(卡塔尔方法:

CString s;
s.Format(_T("The total is %d"), total);

  用这种措施的利润是您不要顾忌用来寄放格式化后数据的缓冲区是不是充足大,这个干活儿由CString类替你完了。
  格式化是生机勃勃种把其他不是字符串类型的数码转载为CString类型的最常用技术,譬喻,把二个大背头转形成CString类型,可用如下方法:

CString s;
s.Format(_T("%d"), total);

  笔者三翻伍回对本身的字符串使用_T(卡塔尔国宏,那是为着让本人的代码至少有Unicode的意识,当然,关于Unicode的话题不在这里篇小说的评论范围。_T(卡塔尔(قطر‎宏在8位字符情状下是之类概念的:

#define _T(x) x // 非Unicode版本(non-Unicode version)

而在Unicode情状下是之类概念的:

#define _T(x) L##x // Unicode版本(Unicode version)

因而在Unicode境遇下,它的效力就也就是:

s.Format(L"%d", total);

  假如你以为你的顺序或者在Unicode的条件下运作,那么带头在乎用 Unicode 编码。比方说,不要用 sizeof(卡塔尔操作符来得到字符串的长短,因为在Unicode情形下就能够有2倍的截断误差。我们能够用部分方法来隐瞒Unicode的局部细节,比方在自家索要获得字符长度的时候,笔者会用一个名称为DIM的宏,那一个宏是在自己的dim.h文件中定义的,小编会在自己写的装有程序中都饱含那么些文件:

#define DIM(x) ( sizeof((x)) / sizeof((x)[0]) )

  那几个宏不仅可以够用来化解Unicode的字符串长度的难题,也足以用在编写翻译时定义的表格上,它能够收获表格的项数,如下:

class Whatever { ... };
Whatever data[] = {
   { ... },
    ...
   { ... },
};
for(int i = 0; i < DIM(data); i++) // 扫描表格寻找匹配项。

  这里要提醒您的正是迟早要专心这么些在参数中必要真实字节数的API函数调用,借使您传递字符个数给它,它将无法健康干活。如下:

TCHAR data[20];
lstrcpyn(data, longstring, sizeof(data) - 1); // WRONG!
lstrcpyn(data, longstring, DIM(data) - 1); // RIGHT
WriteFile(f, data, DIM(data), &bytesWritten, NULL); // WRONG!
WriteFile(f, data, sizeof(data), &bytesWritten, NULL); // RIGHT

引致上述原因是因为lstrcpyn供给二个字符个数作为参数,然而WriteFile却须求字节数作为参数。
长久以来供给静心的是有的时候候供给写出多少的兼具内容。假诺你仅仅只想写出多少的诚恳长度,你或者会以为你应好似此做:

WriteFile(f, data, lstrlen(data), &bytesWritten, NULL); // WRONG

可是在Unicode情状下,它不会符合规律事业。准确的做法应该是那般:

WriteFile(f, data, lstrlen(data) * sizeof(TCHAR), &bytesWritten, NULL); // RIGHT

  因为WriteFile须求的是三个以字节为单位的长短。(或者某人会想“在非Unicode的条件下运作那行代码,就表示总是在做叁个结余的乘1操作,那样不会减弱程序的功能呢?”这种主张是剩下的,你一定要要询问编写翻译器实际上做了何等,未有哪一个C或C++编写翻译器会把这种低俗的乘1操作留在代码中。在Unicode景况下运作的时候,你也不要顾忌那多少个乘2操作会减弱程序的频率,记住,这只是三个左移一个人的操作而已,编写翻译器也很愿意为你做这种替换。)
  使用_T宏并不是意味你曾经成立了二个Unicode的前后相继,你只是开创了贰个有Unicode意识的主次而已。要是您在暗许的8-bit情势下编写翻译你的次序的话,获得的将是二个平日的8-bit的应用程序(这里的8-bit指的只是8位的字符编码,而不是指8位的Computer种类);当您在Unicode情形下编写翻译你的顺序时,你才会拿到八个Unicode的前后相继。记住,CString 在 Unicode 境遇下,里面包涵的可都以拾三位的字符哦。

图片 3 3、CString 型转形成 int 型

  把 CString 类型的数额转变成整数类型最简易的主意正是运用正式的字符串到整数转变例程。
  尽管普通你可疑使用_atoi(卡塔尔函数是八个好的选用,它也非常少会是三个不易的抉择。要是您筹算使用 Unicode 字符,你应当用_ttoi(卡塔尔国,它在 ANSI 编码系统中被编写翻译成_atoi(卡塔尔(قطر‎,而在 Unicode 编码系统中编写翻译成_wtoi(卡塔尔(قطر‎。你也足以杜撰动用_tcstoul()或者_tcstol(卡塔尔国,它们都能把字符串转化成放肆进制的长整数(如二进制、八进制、十进制或十九进制),不一致点在于前面一个转变后的数据是无符号的(unsigned),而后人相反。看上面包车型大巴例子:

CString hex = _T("FAB");
CString decimal = _T("4011");
ASSERT(_tcstoul(hex, 0, 16) == _ttoi(decimal));

图片 4 4、CString 型和 char* 类型的互相转变

  那是初学者使用 CString 时最广大的主题素材。有了 C++ 的帮带,相当多标题你无需浓郁的去思忖它,直接拿来用就能够了,可是假若你无法深刻摸底它的运转坐飞机制,又会有超多标题让您吸引,特别是有些看起来没非凡的代码,却偏偏无法健康职业。
比方,你会奇异为啥不能够写向下边那样的代码呢:

CString graycat = "Gray" + "Cat";

照旧那样:

CString graycat("Gray" + "Cat");

  事实上,编写翻译器将抱怨上边的这一个尝试。为啥吗?因为针对CString 和 LPCTSTCRUISER数据类型的各个多样的组合,“ +” 运算符 被定义成三个重载操作符。并不是多少个 LPCTST中华V数据类型,它是底层数据类型。你无法对基本数据(如 int、char 只怕char*)类型重载 C++ 的运算符。你能够象上边那样做:

CString graycat = CString("Gray") + CString("Cat");

抑或那样:

CString graycat = CString("Gray") + "Cat";

探讨风度翩翩番就能够开采:“ +”总是接收在至少有八个 CString 对象和一个 LPCST陆风X8的场子。

瞩目,编写有 Unicode 意识的代码总是生龙活虎件善事,举例:

CString graycat = CString(_T("Gray")) + _T("Cat");

那将使得你的代码能够一贯移植。

char 转化为 CString*

  现在您有一个 char* 类型的多少,大概说四个字符串。怎么着创立 CString 对象啊?这里有一点点例子:

char * p = "This is a test";

照旧象上边那样更具备 Unicode 意识:

TCHAR * p = _T("This is a test")

LPTSTR p = _T("This is a test");

你能够行使上面大肆蓬蓬勃勃种写法:

CString s = "This is a test"; // 8-bit only
CString s = _T("This is a test"); // Unicode-aware
CString s("This is a test"); // 8-bit only
CString s(_T("This is a test")); // Unicode-aware
CString s = p;
CString s(p);

  用这个办法能够轻便将常量字符串或指针调换成CString。必要当心的是,字符的赋值总是被拷贝到 CString 对象中去的,所以您能够象上面那样操作:

TCHAR * p = _T("Gray");
CString s(p);
p = _T("Cat");
s += p;

结果字符串明确是“GrayCat”。

CString 类还应该有多少个别的的布局函数,但是这里我们不思谋它,假使您风乐趣能够协考查六柱预测关文档。

事实上,CString 类的布局函数比本人出示的要复杂,譬如:

CString s = "This is a test"; 

  那是十分大意的编码,然而其实它在 Unicode 碰着下能编写翻译通过。它在运维时调用结构函数的 MultiByteToWideChar 操作将 8 位字符串转变到 16 位字符串。不管如何,假若 char * 指针是互联网上传输的 8 位数据,这种转移是很有用的。

CString 转化成 char 之一:*强制类型转换为 LPCTST索罗德;

  那是生机勃勃种略略硬性的调换,有关“正确”的做法,大家在认识上还存在好多零乱,准确的施用办法有不菲,但错误的运用形式或然与不易的运用方法相近多。
  大家首先要打听 CString 是朝气蓬勃种很奇异的 C++ 对象,它里面含有了四个值:三个指向性有个别数据缓冲区的指针、叁个是该缓冲中央银卓有成效的字符记数以至一个缓冲镇长度。 有效字符数的分寸能够是从0到该缓冲最大尺寸值减1之间的任何数(因为字符串结尾有四个NULL字符)。字符记数和缓冲科长度被玄妙掩瞒。
  除非您做一些古怪的操作,否则你不容许清楚给CString对象分配的缓冲区的长度。那样,尽管你收获了该0缓冲的地址,你也无从校订当中的从头到尾的经过,不可能截短字符串,也 相对未有章程加长它的内容,不然第有时间就能够看出溢出。
  LPCTSTPRADO 操作符(或许更明了地说就是 TCHAOdyssey * 操作符)在 CString 类中被重载了,该操作符的概念是回到缓冲区的地址,因此,假设你须要一个针对性CString 的 字符串指针的话,能够如此做:

 

CString s("GrayCat");
LPCTSTR p = s;

  它能够准确地运作。那是由C语言的强逼类型转变准绳完结的。当需求抑遏类型转变时,C++规测容许这种接纳。举个例子,你能够将(浮点数)定义为将某些复数 (有局地浮点数)进行强迫类型转换后只回去该复数的率先个浮点数(也正是其实部)。能够象下边那样:

Complex c(1.2f, 4.8f);
float realpart = c;

借使(float卡塔尔操作符定义精确的话,那么实部的的值应该是1.2。
  这种强逼转变相符全数这种气象,比如,任何带有 LPCTST奇骏类型参数的函数都会强逼试行这种转移。 于是,你也会有那般三个函数(大概在有个别你买来的DLL中):

BOOL DoSomethingCool(LPCTSTR s);

你象上面那样调用它:

CString file("c://myfiles//coolstuff")
BOOL result = DoSomethingCool(file);

  它能精确运营。因为 DoSomethingCool 函数已经申明了要求八个 LPCTST本田CR-V类型的参数,因而 LPCTSTEnclave 被应用于该参数,在 MFC 中正是回去的串地址。

假使您要格式化字符串如何做呢?

CString graycat("GrayCat");
CString s;
s.Format("Mew! I love %s", graycat);

  注意由于在可变参数列表中的值(在函数表明中是以“...”表示的)并从未包蕴叁个勒迫类型转变操作符。你会赢得如何结果吗?
  一个让人奇怪的结果,大家获取的骨子里结果串是:

"Mew! I love GrayCat"。

  因为 MFC 的设计者们在兼顾 CString 数据类型时非常的小心, CString 类型表明式求值后指向了字符串,所以那边看不到任何象 Format 或 sprintf 中的免强类型转换,你如故能够获得正确的行为。描述 CString 的叠合数据实际上在 CString 名义地址然后。
  有意气风发件业务你是不可能做的,那正是改过字符串。举个例子,你只怕会尝试用“,”替代“.”(不要做这么的,借令你在乎国际化难题,你应有接收十进制转变的 National Language Support 天性,),下边是个简单的事例:

CString v("1.00"); // 货币金额,两位小数
LPCTSTR p = v;
p[lstrlen(p) - 3] = '','';

  那时编写翻译器会报错,因为您赋值了一个常量串。如若您做如下尝试,编写翻译器也会错:

strcat(p, "each");

  因为 strcat 的首先个参数应该是 LPTSTENCORE 类型的数额,而你却给了三个LPCTST奇骏。

  不要试图钻这些指鹿为马新闻的牛犄角,那只会使您本人沦为麻烦!

  原因是缓冲有三个计数,它是不可存取的(它身处 CString 地点之下的二个隐形区域),若是你改造那么些串,缓冲中的字符计数不会反映所做的更改。别的,假设字符串长度赶巧是该字符串物理约束的长短(梢后还或者会讲到这几个主题素材),那么增添该字符串将改写缓冲以外的别的数据,那是您无权举行写操作的内部存储器(不对吗?),你会毁换坏不归属您的内部存款和储蓄器。那是应用程序真正的一了百了处方。

CString转化成char 之二:*使用 CString 对象的 GetBuffer 方法;

  若是您要求更改 CString 中的内容,它有叁个破例的章程能够选用,那便是GetBuffer,它的效益是再次来到二个可写的缓冲指针。 若是您只是筹算改善字符恐怕截短字符串,你完全能够那样做:

CString s(_T("File.ext"));
LPTSTR p = s.GetBuffer();
LPTSTR dot = strchr(p, ''.''); // OK, should have used s.Find...
if(p != NULL)
*p = _T(''/0'');
s.ReleaseBuffer();

  那是 GetBuffer 的首先种用法,也是最简易的黄金时代种,不用给它传递参数,它利用暗中认可值 0,意思是:“给自个儿那个字符串的指针,笔者保险不加长它”。当你调用 ReleaseBuffer 时,字符串的骨子里尺寸会被再次计算,然后存入 CString 对象中。
  必得重申一点,在 GetBuffer 和 ReleaseBuffer 之间这些界定,绝不可运用你要操作的那些缓冲的 CString 对象的此外措施。因为 ReleaseBuffer 被调用以前,该 CString 对象的完整性得不到保险。钻探以下代码:

CString s(...);

LPTSTR p = s.GetBuffer();

//... 这个指针 p 发生了很多事情

int n = s.GetLength(); // 很糟D!!!!! 有可能给出错误的答案!!!

s.TrimRight(); // 很糟!!!!! 不能保证能正常工作!!!!

s.ReleaseBuffer(); // 现在应该 OK

int m = s.GetLength(); // 这个结果可以保证是正确的。

s.TrimRight(); // 将正常工作。

  假诺你想扩展字符串的尺寸,你首先要了然这些字符串恐怕会有多少长度,好比是宣称字符串数组的时候用:

char buffer[1024];

表示 1024 个字符空间能够让您做其余想做得事情。在 CString 中与之意义格外的表示法:

LPTSTR p = s.GetBuffer(1024);

  调用那一个函数后,你非但得到了字符串缓冲区的指针,並且同偶尔间还赢得了长短至少为 1024 个字符的半空中(注意,小编说的是“字符”,并不是“字节”,因为 CString 是以满含方式感知 Unicode 的)。
  同一时候,还相应注意的是,如若你有二个常量串指针,那么些串自己的值被积累在只读内部存款和储蓄器中,要是计划存款和储蓄它,纵然你曾经调用了 GetBuffer ,并拿到二个只读内部存储器的指针,存入操作会退步,并告知存取错误。作者从未在 CString 上表明那点,但小编见到过大把的 C 程序员常常犯那几个荒唐。
  C 技术员有一个缺陷是分配叁个固定长度的缓冲,对它举办 sprintf 操作,然后将它赋值给三个 CString:

char buffer[256];
sprintf(buffer, "%......", args, ...); // ... 部分省略许多细节
CString s = buffer;

就算更加好的样式得以如此做:

CString s;
s.Format(_T("%...."), args, ...);

若是您的字符串长度万大器晚成当先 256 个字符的时候,不会损坏饭店。

  其余二个宽广的怪诞是:既然固定大小的内部存款和储蓄器不坐班,那么就利用动态分配字节,这种做法缺欠更加大:

int len = lstrlen(parm1) + 13  lstrlen(parm2) + 10 + 100;

char * buffer = new char[len];

sprintf(buffer, "%s is equal to %s, valid data", parm1, parm2);

CString s = buffer;

......

delete [] buffer;

它可以能被略去地写成:

CString s;

s.Format(_T("%s is equal to %s, valid data"), parm1, parm2);

  须求当心 sprintf 例子都不是 Unicode 就绪的,固然你能够使用 tsprintf 以致用 _T(卡塔尔(英语:State of Qatar) 来包围格式化字符串,不过基本 思路仍然为在走弯路,这那样十分轻易出错。

CString to char 之三:*和控件的接口;

  大家平时索要把八个 CString 的值传递给叁个控件,比方,CTreeCtrl。MFC为大家提供了众多造福来重载那么些操作,可是在大好多景况下,你利用“原始”情势的换代,由此须求将墨有个别串指针存款和储蓄到 电视INSERTITEMSTRUCT 构造的 电视机ITEM 成员中。如下:

TVINSERTITEMSTRUCT tvi;
CString s;
// ... 为s赋一些值。
tvi.item.pszText = s; // Compiler yells at you here
// ... 填写tvi的其他域
HTREEITEM ti = c_MyTree.InsertItem(&tvi);

  为啥编写翻译器会报错呢?明明看起来很全面包车型大巴用法啊!可是事实上假诺您看看 TVITEM 结构的定义你就能够精晓,在 电视机ITEM 构造中 pszText 成员的注明如下:

LPTSTR pszText;
int cchTextMax;

  因而,赋值不是赋给叁个 LPCTST索罗德类型的变量,而且编写翻译器不能知晓怎么将赋值语句侧面免强调换来LPCTSTSportage。可以吗,你说,那本人就改成这么:

tvi.item.pszText = (LPCTSTR)s; //编译器依然会报错。

  编写翻译器之所以依旧报错是因为您策动把二个 LPCTSTTucson 类型的变量赋值给一个LPTSTPRADO 类型的变量,这种操作在C或C++中是被明令防止的。你不能够用这种艺术 来滥用常量指针与特别量指针概念,不然,会搅乱编写翻译器的优化学工业机械制,使之不知怎样优化你的次序。比方,借使你那样做:

const int i = ...;
//... do lots of stuff
... = a[i]; // usage 1
// ... lots more stuff
... = a[i]; // usage 2

  那么,编写翻译器会感觉既然 i 是 const ,所以 usage1和usage2的值是同等的,何况它竟然能事情未发生前总括好 usage1 处的 a[i] 的地址,然后保留着在后头的 usage2 处使用,并不是重新总括。假如你按如下格局写的话:

const int i = ...;
int * p = &i;
//... do lots of stuff
... = a[i]; // usage 1
// ... lots more stuff
(*p)++; // mess over compiler''s assumption
// ... and other stuff
... = a[i]; // usage 2

  编写翻译器将以为 i 是常量,从而 a[i] 的地点也是常量,这样间接地破坏了从前的比方。因而,你的主次将会在 debug 编写翻译方式(未有优化)和 release 编写翻译形式(完全优化)中呈现出分裂的作为,这种场馆可倒霉,所以当您希图把针对 i 的指针赋值给三个可修改的引用时,会被编写翻译器确诊为那是生龙活虎种杜撰。那正是怎么(LPCTSTHaval)勉强类型转变不起成效的案由。
  为啥不把该成员声称成 LPCTST大切诺基类型呢?因为这些布局被用于读写控件。当您向控件写多少时,文本指针实际上被当成 LPCTST翼虎,而当你从控件读数据 时,你不得不有贰个可写的字符串。那一个组织不可能区分它是用来读依然用来写。

故此,你会平时在自己的代码中看出如下的用法:

tvi.item.pszText = (LPTSTR)(LPCTSTR)s;

  它把 CString 强逼类型转形成LPCTSTXC60,也正是说先拿走改字符串之处,然后再强逼类型转产生LPTST大切诺基,以便能够对之进行赋值操作。 注意那唯有在应用 Set 或 Insert 之类的办法才使得!假设你希图获取数据,则无法这么做。
  假诺你筹算获取存款和储蓄在控件中的数据,则方法稍有两样,譬喻,对有个别CTreeCtrl 使用 GetItem 方法,作者想赢得项指标文本。小编知道那几个文本的长度不会超过 MY_LIMIT,因此小编得以这么写:

TVITEM tvi;
// ... assorted initialization of other fields of tvi
tvi.pszText = s.GetBuffer(MY_LIMIT);
tvi.cchTextMax = MY_LIMIT;
c_MyTree.GetItem(&tvi);
s.ReleaseBuffer();

  能够看出来,其实下边包车型客车代码对富有品种的 Set 方法都适用,可是并不必要这么做,因为具备的类 Set 方法(包含Insert方法)不会修改字符串的剧情。然则当你须要写 CString 对象时,必需确定保障缓冲是可写的,那多亏 GetBuffer 所做的作业。再一次重申: 生龙活虎旦做了叁回 GetBuffer 调用,那么在调用 ReleaseBuffer 以前毫无对这么些CString 对象做任何操作。

图片 5 5、CString 型转形成 BSTLX570型

  当大家运用 ActiveX 控件编制程序时,平日索要接受将有个别值表示成 BSTCR-V类型。BSTEscort 是后生可畏种记数字符串,AMD平台上的宽字符串(Unicode),并且能够分包嵌入的 NULL 字符。

您能够调用 CString 对象的 AllocSysString 方法将 CString 转化成 BSTLAND:

CString s;
s = ... ; // whatever
BSTR b = s.AllocSysString();

  以后指针 b 指向的正是一个新分配的 BST悍马H2 对象,该目的是 CString 的三个拷贝,包括终结 NULL字符。现在你能够将它传递给任何索要 BSTENCORE的接口。平时,BST昂科威 由选用它的机件来刑满释放解除劳教,假若您供给和谐释放 BSTCRUISER的话,能够这么做:

::SysFreeString(b);

  对于什么表示传递给 ActiveX 控件的字符串,在微软内部曾生龙活虎度争辩不休,最终 Visual Basic 的人占了上风,BST陆风X8(“Basic String”的首字母缩写)正是这一场争辨的结果。

图片 6 6、BSTMurano 型转形成 CString 型

  由于 BSTXC60 是记数 Unicode 字符串,你可以用专门的工作调换方法来创立 8 位的 CString。实际上,那是 CString 内建的功力。在 CString 中 有例外的构造函数能够把 ANSI 转产生 Unicode,也足以把Unicode 转变成ANSI。你一样能够从 VALacrosseIANT 类型的变量中得到 BSTEscort 类型的字符串,VACRUISERIANT 类型是 由各类 COM 和 Automation (自动化卡塔尔(英语:State of Qatar)调用重返的门类。

例如,在一个ANSI程序中:

BSTR b;
b = ...; // whatever
CString s(b == NULL ? L"" : b)

  对于单个的 BSTEvoque 串来讲,这种用法能够干活得很好,这是因为 CString 有二个破例的结构函数以LPCWST奥迪Q7(BSTRAV4就是那种类型) 为参数,并将它转变成ANSI 类型。特意检查是必得的,因为 BSTHaval 大概为空值,而 CString 的构造函数对于 NULL 值情形酌量的不是很周全,(感激 Brian Ross建议这点!)。这种用法也必须要管理包蕴 NUL 终结字符的字眼符串;若是要转账含有四个 NULL 字符 串,你得格外做一些干活才行。在 CString 中内嵌的 NULL 字符平日表现不顺遂,应该尽量制止。
  依据 C/C++ 法则,假诺你有叁个 LPWST奥迪Q5,那么它来之不易,只好和 LPCWSTR参数相配。

在 Unicode 情势下,它的布局函数是:

CString::CString(LPCTSTR);

正如下面所代表的,在 ANSI 方式下,它有八个异样的构造函数:

CString::CString(LPCWSTR); 

  它会调用叁个内部的函数将 Unicode 字符串调换到 ANSI 字符串。(在Unicode格局下,有三个专程的布局函数,该函数有叁个参数是LPCST君越类型——贰个8位 ANSI 字符串 指针,该函数将它加宽为 Unicode 的字符串!)再一次重申:必供给检查 BSTTucson 的值是或不是为 NULL。
  此外还应该有叁个主题素材,正如上文提到的:BSTTiggos能够分包三个内嵌的NULL字符,可是CString 的布局函数只可以管理某些串中单个 NULL 字符。 也等于说,假若串中满含嵌入的 NUL字节,CString 将会思索出荒诞的串长度。你必得本人管理它。就算您看看 strcore.cpp 中的布局函数,你会发觉 它们都调用了lstrlen,也正是计算字符串的尺寸。
  注意从 Unicode 到 ANSI 的转变使用带特地参数的 ::WideCharToMultiByte,即便您不想选用这种暗中认可的退换形式,则必须编写制定自身的转向代码。
  要是你在 UNICODE 情势下编写翻译代码,你能够大概地写成:

CString convert(BSTR b)
{
    if(b == NULL)
        return CString(_T(""));
    CString s(b); // in UNICODE mode
    return s;
}

  假若是 ANSI 形式,则须求更复杂的历程来退换。注意这些代码应用与 ::WideCharToMultiByte 相符的参数值。所以你 只好在想要更改这一个参数进行调换时利用该手艺。譬喻,内定不一样的暗中同意字符,不一致的标记集等。

CString convert(BSTR b)
{
    CString s;
    if(b == NULL)
       return s; // empty for NULL BSTR
#ifdef UNICODE
    s = b;
#else
    LPSTR p = s.GetBuffer(SysStringLen(b) + 1); 
    ::WideCharToMultiByte(CP_ACP,            // ANSI Code Page
                          0,                 // no flags
                          b,                 // source widechar string
                          -1,                // assume NUL-terminated
                          p,                 // target buffer
                          SysStringLen(b)+1, // target buffer length
                          NULL,              // use system default char
                          NULL);             // don''t care if default used
    s.ReleaseBuffer();
#endif
    return s;
}

  小编并不担忧黄金时代旦 BST奥迪Q7 包涵未有映射到 8 位字符集的 Unicode 字符时会生出怎么样,因为本人钦赐了::WideCharToMultiByte 的末梢三个参数为 NULL。那正是你或然必要转移的地点。 

图片 7 7、VACR-VIANT 型转变成CString 型

  事实上,笔者一直不曾这么做过,因为笔者还未有用 COM/OLE/ActiveX 编写进程序。可是本人在microsoft.public.vc.mfc 音讯组上看出了 罗Bert Quirk 的风度翩翩篇帖子聊起了这种转变,作者认为把她的稿子包蕴在笔者的稿子里是不太好的做法,所以在这里处多做一些疏解和演示。若是和他的篇章有相孛之处只怕是本人的忽略。
  VARAV4IANT 类型经常用来给 COM 对象传递参数,也许摄取从 COM 对象回来的值。你也能本身编辑重临 VA奥迪Q5IANT 类型的主意,函数再次来到什么项目 信赖或者(並且日常)方法的输入参数(比如,在自动化操作中,信任与你调用哪个方法。IDispatch::Invoke 可能回到(通过其二个参数)一个满含有BYTE、WO智跑D、float、double、date、BSTOdyssey 等等 VA大切诺基IANT 类型的结果,(详见 MSDN 上的 VAEvoqueIANT 构造的概念)。在下面包车型大巴例子中,若是类型是三个BST凯雷德的变体,也正是说在串中的值是由此 bsrtVal 来引用,其优点是在 ANSI 应用中,有四个布局函数会把 LPCWCHAHighlander引用的值转变为叁个 CString(见 BST奥迪Q7-to-CString 部分)。在 Unicode 情势中,将成为正式的 CString 布局函数,参见对缺省::WideCharToMultiByte 转换的劝说,以致你以为是还是不是能够采用(大比非常多景观下,你会白璧微瑕的)。

VARIANT vaData;
vaData = m_com.YourMethodHere();
ASSERT(vaData.vt == VT_BSTR);
CString strData(vaData.bstrVal);

你还足以依靠 vt 域的两样来树立更通用的调换例程。为此你可能会考虑:

CString VariantToString(VARIANT * va)
{
    CString s;
    switch(va->vt)
      { /* vt */
       case VT_BSTR:
          return CString(vaData->bstrVal);
       case VT_BSTR | VT_BYREF:
          return CString(*vaData->pbstrVal);
       case VT_I4:
          s.Format(_T("%d"), va->lVal);
          return s;
       case VT_I4 | VT_BYREF:
          s.Format(_T("%d"), *va->plVal);
       case VT_R8:
          s.Format(_T("%f"), va->dblVal);
          return s;
       ... 剩下的类型转换由读者自己完成
       default:
          ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional)
          return CString("");
      } /* vt */
}

图片 8 8、载入字符串表能源

  假使您想创制八个便于开展语言版本移植的应用程序,你就无法在你的源代码中一直满含本土语言字符串 (上面这个事例作者用的语言都以塞尔维亚共和国语,因为本人的本土语是德语),举例上边这种写法就很糟:

CString s = "There is an error";

  你应该把你抱有特定语言的字符串单独摆放(调节和测量检验新闻、在拆穿版本中不现身的音讯除此而外)。那表示向上边那样写比较好:

s.Format(_T("%d - %s"), code, text);

  在您的次第中,文字字符串不是语言敏感的。不管怎么样,你一定要十分的小心,不要使用上面那样的串:

// fmt is "Error in %s file %s"
// readorwrite is "reading" or "writing"
s.Format(fmt, readorwrite, filename); 

  那是小编的切身感知。在小编的首先个国际化的应用程序中本罪犯了这些荒诞,就算小编懂保加利亚共和国语,知道在希腊语的语法中动词放在句子的结尾面,大家的德意志方面包车型客车制片人依旧苦苦的抱怨他们只得提取那个匪夷所思的葡萄牙语错误提醒音信然后再度格式化以让它们能健康干活。相比好的法子(也是本身未来利用的法子)是接收七个字符串,多少个用 于读,贰个用以写,在使用时加载合适的版本,使得它们对字符串参数是非敏感的。也便是说加载整个格式,并不是加载串“reading”,“writing”:

// fmt is "Error in reading file %s"
// "Error in writing file %s"
s.Format(fmt, filename);

  应当要细心,假诺你有几许个地点须求替换,你早晚要担保替换后句子的构造不相会世难题,比如在保加利亚共和国语中,能够是主语-宾语,主语-谓语,动词-宾语的布局等等。
  在那间,我们并不商量 FormatMessage,其实它比 sprintf/Format 还要有优势,可是不太轻巧和CString 结合使用。消除这种难题的点子便是大家依据参数出以后参数表中之处给参数取名字,那样在您输出的时候就不会把他们的职位排错了。
  接下去大家谈谈我们那个独立的字符串放在如哪个地区方。我们得以把字符串的值归入能源文件中的叁个叫作 STLX570INGTABLE 的段中。过程如下:首先应用 Visual Studio 的能源编辑器创立一个字符串,然后给每三个字符串取一个ID,日常我们给它取名字都是IDS_起来。所以只要您有八个信息,你能够创立三个字符串财富然后取名字为IDS_READING_FILE,别的多少个就取名称为IDS_WRITING_FILE。它们以上面包车型客车款型出现在你的 .rc 文件中:

STRINGTABLE
IDS_READING_FILE "Reading file %s"
IDS_WRITING_FILE "Writing file %s"
END

注意:那些财富都是 Unicode 的格式保存,不管你是在如何条件下编写翻译。他们在Win9x系统上也是以Unicode 的情势存在,固然 Win9x 不可能真正管理 Unicode。
下一场你能够这么使用那几个财富:
// 在选择财富串表在此以前,程序是那般写的:

   CString fmt;
      if(...)
        fmt = "Reading file %s";
     else
       fmt = "Writing file %s";
  ...
    // much later
  CString s;
  s.Format(fmt, filename); 

// 使用财富串表之后,程序那样写:

    CString fmt;
        if(...)
           fmt.LoadString(IDS_READING_FILE);
        else
           fmt.LoadString(DS_WRITING_FILE);
    ...
      // much later
    CString s;
    s.Format(fmt, filename);

  以后,你的代码能够移植到此外语言中去。LoadString 方法需求二个字符串能源的 ID 作为参数,然后它从 STSportageINGTABLE 中抽取它对应的字符串,赋值给 CString 对象。 CString 对象的布局函数还会有二个特别精通的特性能够简化 STGL450INGTABLE 的应用。这么些用法在 CString::CString 的文书档案中尚无提议,可是在 布局函数的身体力行程序中接受了。(为何这几个天性未有成为正式文书档案的豆蔻梢头部分,而是放在了二个例子中,作者记不得了!)——【译者注:从那句话看,我大概是CString的设计者。其实前面还会有一句看似的话。说他从未对利用GetBuffer(0卡塔尔(قطر‎拿到的指针指向之处是或不是可读做有效检查 】。那天性情就是:要是你将三个字符串能源的ID逼迫类型转变为 LPCTSTOdyssey,将会满含调用 LoadString。因而,上面七个协会字符串的例子具备同样的效果与利益,并且其 ASSERT 在debug形式下不会被触发:

CString s;
s.LoadString(IDS_WHATEVER);
CString t( (LPCTSTR)IDS_WHATEVER );
ASSERT(s == t);//不会被触发,说明s和t是相同的。

  将来,你大概会想:那怎么大概专业吗?大家怎么可以把 STLX570INGTABLE ID 转产生多少个指针呢?很简短:全数的字符串 ID 都在1~65535那个节制内,也正是说,它具有的上位都以0,而小编辈在程序中所使用的指针是不容许低于65535的,因为程序的低 64K 内部存款和储蓄器长久也不容许存在的,倘令你准备访谈0x00000000到0x0000FFFF之间的内部存款和储蓄器,将会引发一个内部存款和储蓄器越界错误。所以说1~65535的值不容许是三个内存地址,所以我们得以用那个值来作为字符串能源的ID。
  作者赞成于选取 MAKEINTRESOURCE 宏显式地做这种转移。小编认为这么可以让代码越发便于阅读。那是个只相符在 MFC 中动用的标准宏。你要切记,大非常多的不二等秘书技即能够承担七个 UINT 型的参数,也得以接收四个 LPCTSTENVISION 型的参数,那是信赖 C++ 的重载功用做到的。C++重载函数带给的 缺欠就是造成全部的勒迫类型转变都亟待出示评释。相符,你也足以给很三种构造只传递三个财富名。

CString s;
s.LoadString(IDS_WHATEVER);
CString t( MAKEINTRESOURCE(IDS_WHATEVER));
ASSERT(s == t);

  告诉您啊:作者不光只是在这里处鼓吹,事实上小编也是那样做的。在小编的代码中,你大概不容许找到一个字符串,当然,这么些只是有的时候在调节和测量试验中冒出的还是和言语非亲非故的字符串除外。

图片 9 9、CString 和权且对象

  那是出现在 microsoft.public.vc.mfc 新闻组中的八个不成难题,小编轻巧的提一下,那么些标题是有个程序员须求往注册表中写入贰个字符串,他写道:
  小编试着用 RegSetValueEx(卡塔尔设置一个注册表键的值,可是它的结果三翻五次令自身纳闷。当作者用char[]宣示三个变量时它能常常办事,不过当本身用 CString 的时候,总是获得一些抛弃物:"ÝÝÝÝ...ÝÝÝÝÝÝ"为了确认是或不是小编的 CString 数据出了难题,小编试着用 GetBuffer,然后强迫转产生char*,LPCST奥德赛。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,呈现出来恰恰正是“Ý”符号。在这里个时候你向注册表中写多少,字符串的剧情自然全被磨损了。
  大家不应该立即把那些有的时候对象转变成 char* 类型,应该先把它保存到八个CString 对象中,这象征把临时对象复制了大器晚成份,所以当有的时候的 CString 对象被析构了后来,这几个 CString 对象中的值依旧保存着。这时再向注册表中写多少就不曾难点了。
  别的,笔者的代码是享有 Unicode 意识的。那几个操作注册表的函数需求叁个字节大小,使用lstrlen(Name+1卡塔尔(英语:State of Qatar)获得的莫过于结果对于 Unicode 字符来讲比 ANSI 字符要小二分之一,何况它也无法从那几个字符串的第一个字符起早先思索,也许你的原意是 lstrlen(Name卡塔尔(قطر‎ + 1(OK,小编认可,笔者也犯了扳平的大错特错!)。无论如何,在 Unicode 方式下,全体的字符都以2个字节大小,大家供给管理这几个主题材料。微软的文书档案令人愕然地对此保持缄默:REG_SZ 的值究竟是以字节计算照旧以字符总括呢?大家若是它指的是以字节为单位测算,你须求对您的代码做一些校正来计量这些字符串所包括的字节大小。

图片 10 10、CString 的效率

  CString 的三个主题材料是它真的潜藏了生机勃勃部分低功能的事物。从其它叁个上边讲,它也真的可以被完毕得更为高效,你可能会说上面包车型大巴代码:

CString s = SomeCString1;
s += SomeCString2;
s += SomeCString3;
s += ",";
s += SomeCString4;

比起上边包车型客车代码来,效能要低多了:

char s[1024];
lstrcpy(s, SomeString1);
lstrcat(s, SomeString2);
lstrcat(s, SomeString 3);
lstrcat(s, ",");
lstrcat(s, SomeString4);

  简单的说,你只怕会想,首先,它为 SomeCString1 分配一块内部存款和储蓄器,然后把 SomeCString1 复制到里面,然后开采它要做二个总是,则重新分配一块新的丰硕大的内部存款和储蓄器,大到能够放下当前的字符串加上SomeCString2,把内容复制到那块内部存款和储蓄器,然后把 SomeCString2 连接收前边,然后释放第一块内部存款和储蓄器,并把指针重新指向新内部存款和储蓄器。然后为各种字符串重复这一个进程。把这4 个字符串连接起来效能多低啊。事实上,在无尽场合下根本就无需复制源字符串(在 += 操作符左边包车型大巴字符串)。
  在 VC++6.0 中,Release 格局下,全部的 CString 中的缓存都是按预定义量子分配的。所谓量子,即明确为 64、128、256 可能 512 字节。这意味着除非字符串异常的短,连接字符串的操作实际便是 strcat 经过优化后的本子(因为它驾驭地方的字符串应该在如何地方截至,所以没有要求探求字符串的尾声;只须要把内部存款和储蓄器中的数码拷贝到钦点之处就可以)加上再次总计字符串的长度。所以它的实行功用和纯 C 的代码是均等的,然则它更便于写、更易于保证和更易于精晓。
  若是你要么无法明确到底产生了什么样的进程,请看看 CString 的源代码,strcore.cpp,在你 vc98的安装目录的 mfc/src 子目录中。看看 ConcatInPlace 方法,它被在全部的 += 操作符中调用。

啊哈!难道 CString 真的如此"高效"吗?举个例子,假若笔者创建

CString cat("Mew!");

  然后笔者并非拿到了一个飞快的、简练的5个字节大小的缓冲区(4个字符加二个停止字符),系统将给本身分配六14个字节,而内部六11个字节都被浪费了。
  假设你也是那样想的话,那么就请绸缪好选用再教育啊。大概在某些地点某一个人给你讲过玩命选择少的长空是件好事情。不错,这种说法的确不错,不过她忽略了实际中三个很首要的方面。
  倘若您编写的是运作在16K EPROMs下的嵌入式程序的话,你有理由尽量少使用空间,在此种条件下,它能使您的次序更健康。不过在 500MHz, 256MB的机械上写 Windows 程序,即便您要么如此做,它只会比你以为的“低效”的代码运营得更糟。
  比方来讲。字符串的大大小小被以为是影响作用的首要性因素,使字符串尽大概小能够提升效率,反之则稳中有降作用,那是大家一定的主见。但是这种主见是非寻常的,精确的内部存款和储蓄器分配的结局要在程序运维了少数个时辰后本领展示得出去,当时,程序的堆上校充满小片的内部存款和储蓄器,它们太小甚至于不可能用来做任何事,可是她们扩展了您程序的内部存储器用量,扩充了内部存款和储蓄器页面沟通的次数,当页面沟通的次数加多到系统能够经受的上限,系统则会为你的次序分配越多的页面,直到你的次第占用了具有的可用内部存款和储蓄器。简单的讲,即便内存碎片是决定功效的附带因素,但就是那么些因素实际决定了系统的一言一行,最终,它有毒了系统的可相信性,那是令人心有余而力不足经受的。
  记住,在 debug 方式下,内部存款和储蓄器往往是正确分配的,那是为着越来越好的排错。
  假如你的应用程序平时必要连接职业好几个月。比方,笔者常张开VC++,Word,PowerPoint,Frontpage,Outlook Express,Forté Agent,Internet Explorer和别的的某些顺序,并且平日不安歇它们。笔者早已牛角挂书地接连用 PowerPoint 职业了一点天(反之,借使您不幸一定要使用像 Adobe FrameMaker 那样的程序的话,你将会体会到可信性的尤为重要;那几个顺序机缘每日都要崩溃4~6次,每一趟都以因为用完了全部的上空并填满作者具有的置换页面)。所以正确内部存款和储蓄器分配是不可取的,它会十面埋伏到系统的可信性,并引起应用程序崩溃。
  按量子的翻番为字符串分配内存,内部存储器分配器就足以回笼用过的内部存款和储蓄器块,日常那一个回笼的内部存储器块即刻就足以被其它的 CString 对象重新行使,这样就可以保障碎片起码。分配器的效果巩固了,应用程序用到的内部存款和储蓄器就能够尽量保持最小,那样的次序就能够运作多少个礼拜或多少个月而不出新难点。
  题外话:比相当多年以前,大家在 CMU 写一位机联作式系统的时候,一些对内部存款和储蓄器分配器的商讨展现出它往往发生众多内部存款和储蓄器碎片。吉姆Mitchell,以往她在 Sun 七彩虹专门的工作,当时侯他创办了风流倜傥种内存分配器,它保留了一个内部存款和储蓄器分配处境的运维时总结表,这种技巧和即时的主流分配器所用的技巧都不如,且较为超过。当三个内部存储器块要求被划分得比某二个值小的话,他并不分开它,因而能够制止爆发太多小到怎么事都干不了的内部存款和储蓄器碎片。事实上他在内部存款和储蓄器分配器中利用了三个扭转指针,他感到:与其让指令做长时间的存取内部存款和储蓄器操作,还不及轻易的马虎那一个太小的内部存款和储蓄器块而只做一些变型指针的操作。(His observation was that the long-term saving in instructions by not having to ignore unusable small storage chunks far and away exceeded the additional cost of doing a few floating point operations on an allocation operation.)他是对的。
  永世不要感到所谓的“最优化”是树立在每后生可畏行代码都麻利且节省里部存款和储蓄器的底子上的,事实上,高速且节省里部存款和储蓄器应该是在一个应用程序的完整品位上思忖的。在软件的完全水平上,只使用最小内部存款和储蓄器的字符串分配政策或然是最不佳的风姿洒脱种艺术。
  倘令你认为优化是你在每生龙活虎行代码上做的那个拼命的话,你应当想风度翩翩想:在每后生可畏行代码中做的优化超级少能真的起效果。你能够看自身的另意气风发篇关于优化难点的篇章《Your Worst Enemy for some thought-provoking ideas》。
  记住,+= 运算符只是豆蔻梢头种特例,假若你写成上面那样:

CString s = SomeCString1 + SomeCString2 + SomeCString3 + "," + SomeCString4;

则每叁个 + 的应用会引致一个新的字符串被创建和一次复制操作。

图片 11 总结

  以上是使用 CString 的部分本事。小编天天写程序的时候都会用到这个。CString 而不是风流倜傥种很难使用的类,然而 MFC 未有很令人侧目标提出那么些特点,须求您本人去切磋、去开掘。

本文由威尼斯www.9778.com发布于编程人生,转载请注明出处:关于CString CtrlList::GetItemText返回值和CString比较的问题

关键词:

求助:MFC静态嵌套拆分窗口的问题

在初始分隔四个视图,然后左上视图是树状结构,通过节点改变左下和右下视图;;;;左下视图可以输入数字,并...

详细>>

一步一步学多线程-Timer,多线程-timer

if(g_cdma.ConnectCom(_T("COM"))){HRESULThr;IConnectionPointContainer*pConnectionPointContainer=NULL;IConnectionPoint*pConnectionPoint=NULL;hr=g_cdma.m_...

详细>>

使用Real vnc做远程协助,怎么将受控端返回的远程桌面显示到自

公司最近做远程协助,使用RealVNC的源代码做,源代码返回的是一个win32窗口,我想把win32的窗口,弄成MFC窗口的子窗口...

详细>>

matlab与vs混合编程问题

为什么点了之后只有哪里错了? 用matlab生成的mydecimate.h中有externLIB_myDecimate_C_APIboolMW_CALL_CONVmyDecimateInitialize(void)在主...

详细>>