分类: C/C++
2008-03-17 11:18:48
许多 C++ 爱好者已经对我最近的专栏中渗入了太多关于C#的内容表示关注。我承认这一点!我唯一的辩解是:由于 Microsoft® .NET Framework 已经获得广泛的认同,给我发送关于C#问题的读者越来越多,同时因为C# 和 C++ 如此类似,所以我就回答了一部分他们的问题。这不是我有意疏远 C++ 爱好者——上帝知道,我就是他们中的一员啊!不管怎样,为了突出重点,从这个月开始的 C++ 专栏将更多地专注于 C++ 的内容,包括托管扩展以及 MFC 这样的传统内容。因此提出你的 C++ 问题吧!我特别鼓励你提出关于托管 C++ 的问题。你使用它的时候有些什么体会? Jeff Partch 你说得完全正确!WM_USER 是为所有实现窗口类的人保留的——无论是你,还是友好的 Redmondtonians(译注:Microsoft), 仰或是 Gleepoid 行星上的叛逆者。Figure 1 展示了正式的 Windows 消息值的细目分类,对此每个人都应该至少每十年复习一次。WM_USER 到 0x7FFF 是为私有窗口类保留的。你可以将这个范围 认为是在特定的窗体类中有意义的专用消息。举个例子,状态栏控件的 SB_SETTEXTA 使用 WM_USER+1。同时正如你所指出的一样, 对话框的 DM_GETDEFID 和 DM_SETDEFID 使用 WM_USER+0 和 WM_USER+1。我在 2004 年 3 月的专栏中使用 WM_USER+1 是与 DM_GETDEFID 相冲突的。 #if (WINVER >= 0x0400) #define WM_APP 0x8000 #endif 正如每个 Windows 极客(Geek)所知道的那样,WINVER 0x0400 是指 Windows 95、Windows 98 和 Windows NT。所以 WM_APP 的使用还不到十年,这解释了为什么我没注意到它——在 2005 年之前,我不应该对下一个十年的消息范围妄加评论! UINT WM_SAYHELLO = RegisterWindowMessage(_T("WmSayHello")); 这时其它协作应用程序可以用这个特定的名称“WmSayHello”注册并获得相同的值,于是他们都可以互相通信。每个 Windows 会话实际的 WM_SAYHELLO 值 将会各不相同,但在某一个对话中,注册它的所有应用程序将具有相同的值。当然,该值将总在 0xC000 - 0xFFFF 范围之内。糟了——MSDN® 杂志有最精明的读者! Randy Goodwin 理解托管字符串文字量和普通C/C++ 字符串文字量之间差别的最好方法是用它们写相同的代码并考察其编译结果。我写了一个简单的托管 C++ 程序,StrLit.cpp,这个程序用C++和.NET风格创建了一些 String 文字量,代码如 Figure 2 所示。Figure 3 和 Figure 4 展示了用 ILDASM 反汇编的 Microsoft 中间语言(MSIL)代码。正如你可能知道的许多事情一样,其首要差别就是当你使用 C++ 文字量时,编译器产生一个对相应 String 构造函数的调用: // String* c2 = new String(_T("This is a TCHAR string")); ldsflda valuetype $ArrayType$0x11197cc2 modopt([Microsoft.VisualC]Microsoft.VisualC.IsConstModifier) ''?A0x1f1e2151.unnamed-global-0'' newobj instance void [mscorlib]System.String::.ctor(int8*)我会马上解释这段冗长而令人费解的文字,但其基本思路是编译器加载了 C++ 字符串文字量的地址,然后产生一个 newobj 指令,该指令将文字量地址到 String 的构造函数。另外,编译器为非托管字符串文字量创建了一个专门的 Array 类型——这就是前面代码段中样子奇怪的 $ArrayType$0x11197cc2,还有一个 被命名为?A0x1f1e2151.unnamed-global-0 的静态实例,如图 Figure 3 所示:
如果你花点时间看看 MSIL,你将发现 modopts 无处不在。一个 modopt 的完全描述已超出了本栏目范围,但是其基本思路是编译器 能用 modopt 提供有关公共语言运行时无法理解的类型的附加信息,但是该程序集的其它使用者可能理解——在这种情况下,该信息就是全局常量。CLR本身没有常量的概念,但是 程序集的其它使用者可能知道。如果该程序集被引入 C++,则 C++ 编译器会知道该变量是个常量;但是如果该程序集被 C# 使用,C# 编译器将会忽略 modopt 常量,因为 C# 没有常量的概念,正如你所能看到的,当你在托管代码中使用原生类型时有许多奇怪或迷惑发生。 // String* s1 = new String(S"This is a String literal"); ldstr "This is a String literal" 这里没有函数调用,仅有一个 ldstr 指令。这个指令将引用某个字符串文字量的新对象压入,储存在程序的元数据中。无论是用文字量创建 String 对象还是直接将它传递给需要字符串的 某些函数,都是如此: // Console::WriteLine(S"World"); ldstr "World" call void [mscorlib]System.Console::WriteLine(string) 了解所发生的事情的另外一种方法是用/FAs编译程序来生成一个程序集列表。(是的,它也可以用于 MSIL)Figure 5 展示了用/FAs编译 StrLit 后经过编辑的结果,。这里有趣的是你可以看到编译器为 TCHAR字符串 "This is a TCHAR string" 生成了两个字符串($SG1883 and $SG2266),即使它们有相同的内容。然而,此处针对 S"This is a String literal" 仅有一个托管字符串 文字量 ($StringLiteralTok$70000001$),它被使用了两次。大多数程序员调用 "uniquification” 进程,但是 出于某种原因微软的家伙们(Redmondtonians)称之为“字符串暂留”(string interning)”。正如说明文档所描述的:“公共语言基础架构(CLI)确保涉及具有相同字符序列的两个元数据符号的两个 ldstr 指令的结果精确地返回相同字符串对象。” _stprintf(msg, _T("Test 1: Allocate %d strings using LPCTSTR"),num); ShowTime st(msg); for (int i=0; i |
作者简介 Paul DiLascia 是自由作者,顾问,和 Web/UI整体设计师。他是 Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992)一书的作者。你可以通过 paul 取得联系。 |