﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-榕树驿站-随笔分类-编程技术</title><link>http://www.cppblog.com/xpzhou/category/4063.html</link><description>要是生命真的有如果，就不会有这么多的遗憾；要是等一分钟可以如愿，就不会有这么多的伤感...</description><language>zh-cn</language><lastBuildDate>Wed, 21 May 2008 00:05:32 GMT</lastBuildDate><pubDate>Wed, 21 May 2008 00:05:32 GMT</pubDate><ttl>60</ttl><item><title>VC6.0编译器参数设置 </title><link>http://www.cppblog.com/xpzhou/archive/2007/04/23/22672.html</link><dc:creator>榕树藤</dc:creator><author>榕树藤</author><pubDate>Mon, 23 Apr 2007 11:51:00 GMT</pubDate><guid>http://www.cppblog.com/xpzhou/archive/2007/04/23/22672.html</guid><wfw:comment>http://www.cppblog.com/xpzhou/comments/22672.html</wfw:comment><comments>http://www.cppblog.com/xpzhou/archive/2007/04/23/22672.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xpzhou/comments/commentRss/22672.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xpzhou/services/trackbacks/22672.html</trackback:ping><description><![CDATA[<p><font size=2>VC6.0编译器参数的设置主要通过VC的菜单项Project-&gt;Settings-&gt;C/C++页来完成。我们可以看到这一页的最下面Project Options中的内容，一般如下：</font></p>
<p><font size=2>/nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Fp"Debug/WritingDlgTest.pch" /Yu"stdafx.h" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c</font></p>
<p><font size=2>各个参数代表的意义，可以参考Msdn。比如/nologo表示编译时不在输出窗口显示这些设置（我们可以把这个参数去掉来看看效果）等等。一般我们不会直接修改这些设置，而是通过这一页最上面的Category中的各项来完成......<br>－转载－</font></p>
<p><br><font size=2>主要通过VC的菜单项Project-&gt;Settings-&gt;C/C++页来完成。我们可以看到这一页的最下面Project Options中的内容，一般如下：</font></p>
<p><font size=2>/nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Fp"Debug/WritingDlgTest.pch" /Yu"stdafx.h" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c</font></p>
<p><font size=2>各个参数代表的意义，可以参考Msdn。比如/nologo表示编译时不在输出窗口显示这些设置（我们可以把这个参数去掉来看看效果）等等。一般我们不会直接修改这些设置，而是通过这一页最上面的Category中的各项来完成。</font></p>
<p><font size=2>1) General：一些总体设置。</font></p>
<p><font size=2>Warning level　用来控制警告信息，其中Level 1是最严重的级别； <br>Warnings as errors　将警告信息当作错误处理； <br>Optimizations　代码优化，可以在Category的Optimizations项中进行更细的设置； <br>Generate browse info　用以生成.sbr文件，记录类、变量等符号信息，可以在Category的Listing Files项中进行更多的设置。 <br>Debug info　生成调试信息： <br>None，不产生任何调试信息（编译比较快）； <br>Line Numbers Only，仅生成全局的和外部符号的调试信息到.OBJ文件或.EXE文件，减小目标文件的尺寸； <br>C 7.0- Compatible，记录调试器用到的所有符号信息到.OBJ文件和.EXE文件； <br>Program Database，创建.PDB文件记录所有调试信息； <br>Program Database for "Edit &amp; Continue"，创建.PDB文件记录所有调试信息，并且支持调试时编辑。 </font></p>
<p><font size=2>2) C++ Language</font></p>
<p><font size=2>Pointer-to-member representation　用来设置类定义/引用的先后关系： <br>Best-Case Always，表示在引用类之前该类肯定已经定义； <br>General-Purpose Always，？ <br>Point to Any Class <br>Point to Single- and Multiple-Inheritance Classes <br>Point to Single-Inheritance Classes <br>Enable Exception Handling，进行同步的异常处理； <br>Enable Run-Time Type Information　迫使编译器增加代码在运行时进行对象类型检查； <br>Disable Construction Displacements　设置类构造/析构函数调用虚函数问题。 </font></p>
<p><font size=2>3) Code Generation</font></p>
<p><font size=2>Processor　表示代码指令优化，可以为80386、80486、Pentium、Pentium Pro，或者Blend表示混合以上各种优化。 <br>Use run-time library　用以指定程序运行时使用的运行时库，有一个原则就是，一个进程不要同时使用几个版本的运行时库。连接了单线程库就不支持多线程调用，连接了多线程库就要求创建多线程的应用程序。 <br>Single-Threaded，单线程Release版本，静态连接LIBC.LIB库； <br>Debug Single-Threaded，单线程Debug版本，静态连接LIBCD.LIB库； <br>Multithreaded，多线程Release版本，静态连接LIBCMT.LIB库； <br>Debug Multithreaded，多线程Debug版本，静态连接LIBCMTD.LIB库； <br>Multithreaded DLL，动态连接MSVCRT.DLL库； <br>Debug Multithreaded DLL，动态连接MSVCRTD.DLL库。 <br>Calling convention　可以用来设定调用约定，有三种：__cdecl、__fastcall和__stdcall。<br>　　各种调用约定的主要区别在于：1. 函数调用时，函数的参数是从左到右压入堆栈还是从右到左压入堆栈；2. 在函数返回时，由函数的调用者来清理压入堆栈的参数还是由函数本身来清理；3. 以及在编译时对函数名进行的命名修饰（可以通过Listing Files看到各种命名修饰方式）。 <br>Struct member alignment　用以指定数据结构中的成员变量在内存中是按几字节对齐的，根据计算机数据总线的位数，不同的对齐方式存取数据的速度不一样。这个参数对数据包网络传输等应用尤为重要，不是存取速度问题，而是数据位的精确定义问题，一般在程序中使用#pragma pack来指定。 </font></p>
<p><font size=2>4) Customize</font></p>
<p><font size=2>Disable Language Extensions，表示不使用微软为标准C做的语言扩展； <br>Eliminate Duplicate Strings，主要用于字符串优化（将字符串放到缓充池里以节省空间），使用这个参数，使得<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char *sBuffer = "This is a character buffer";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char *tBuffer = "This is a character buffer";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sBuffer 和tBuffer指向的是同一块内存空间； <br>Enable Function-Level Linking ，告诉编译器将各个函数按打包格式编译； <br>Enables minimal rebuild，通过保存关联信息到.IDB文件，使编译器只对最新类定义改动过的源文件进行重编译，提高编译速度； <br>Enable Incremental Compilation，同样通过.IDB文件保存的信息，只重编译最新改动过的函数； <br>Suppress Startup Banner and Information Messages，用以控制参数是否在output窗口输出。</font></p>
<p><br><font size=2>5) Listing Files</font></p>
<p><font size=2>Generate browse info 上面已经提到过。这里可以进行更多的设置。 <br>Exclude Local Variables from Browse Info　表示是否将局部变量的信息放到.SBR文件中。 <br>Listing file type　可以设置生成的列表信息文件的内容： <br>Assembly-Only Listing　仅生成汇编代码文件（.ASM扩展名）； <br>Assembly With Machine Code　生成机器代码和汇编代码文件（.COD扩展名）； <br>Assembly With Source Code　生成源代码和汇编代码文件（.ASM扩展名）； <br>Assembly, Machine Code, and Source　生成机器码、源代码和汇编代码文件（.COD扩展名）。 <br>Listing file name　生成的信息文件的路径，一般为Debug或Release目录下，生成的文件名自动取源文件的文件名。 </font></p>
<p><font size=2>6) Optimizations　代码优化设置。</font></p>
<p><font size=2>Maximize Speed　生成最快速的代码； <br>Minimize Size　生成最小尺寸的程序； <br>Customize　定制优化。定制的内容包括： <br>Assume No Aliasing，不使用别名（提高速度）； <br>Assume Aliasing Across Function Calls，仅函数内部不使用别名； <br>Global Optimizations，全局优化，比如经常用到的变量使用寄存器保存，或者循环内的计算优化，如i = -100;while( i &lt; 0 ){i += x + y;}会被优化为i = -100;t = x + y;while( i &lt; 0 ){i += t;}； <br>Generate Intrinsic Functions，使用内部函数替换一些函数调用（提高速度）； <br>Improve Float Consistency，浮点运算方面的优化； <br>Favor Small Code，程序（exe或dll）尺寸优化优先于代码速度优化； <br>Favor Fast Code，程序（exe或dll）代码速度优化优先于尺寸优化； <br>Frame-Pointer Omission，不使用帧指针，以提高函数调用速度； <br>Full Optimization，组合了几种参数，以生成最快的程序代码。 <br>Inline function expansion，内联函数扩展的三种优化（使用内联可以节省函数调用的开销，加快程序速度）： <br>Disable，不使用内联； <br>Only __inline，仅函数定义前有inline或__inline标记使用内联； <br>Any Suitable，除了inline或__inline标记的函数外，编译器"觉得"应该使用内联的函数，都使用内联。 </font></p>
<p><font size=2>7) Precompiled Headers　预编译头文件的设置。使用预编译可以提高重复编译的速度。VC一般将一些公共的、不大变动的头文件（比如afxwin.h等）集中放到stdafx.h中，这一部分代码就不必每次都重新编译（除非是Rebuild All）。</font></p>
<p><br><font size=2>8) Preprocessor　预编译处理。可以定义/解除定义一些常量。</font></p>
<p><font size=2>Additional include directories，可以指定额外的包含目录，一般是相对于本项目的目录，如..\Include。</font></p>
<p><br><font size=2>--------------------------------------------------------------------------------<br>连接参数的设置<br>主要通过VC的菜单项Project-&gt;Settings-&gt;Link页来完成。我们可以看到这一页的最下面Project Options中的内容，一般如下：</font></p>
<p><font size=2>/nologo /subsystem:windows /incremental:yes /pdb:"Debug/WritingDlgTest.pdb" /debug /machine:I386 /out:"Debug/WritingDlgTest.exe" /pdbtype:sept</font></p>
<p><font size=2>下面我们分别来看一下Category中的各项设置。</font></p>
<p><font size=2>1) General　一些总体设置。可以设置生成的文件路径、文件名；连接的库文件；</font></p>
<p><font size=2>Generate debug info，生成Debug信息到.PDB文件（具体格式可以在Category-&gt;Debug中设置）； <br>Ignore All Default Libraries，放弃所有默认的库连接； <br>Link Incrementally，通过生成. ILK文件实现递增式连接以提高后续连接速度，但一般这种方式下生成的文件（EXE或DLL）较大； <br>Generate Mapfile，生成.MAP文件记录模块相关信息； <br>Enable Profiling，这个参数通常与Generate Mapfile参数同时使用，而且如果产生Debug信息的话，不能用.PDB文件，而且必须用Microsoft Format。 </font></p>
<p><font size=2>2) Customize　这里可以进行使用程序数据库文件的设置。</font></p>
<p><font size=2>Force File Output，强制产生输出文件（EXE或DLL）； <br>Print Progress Messages，可以将连接过程中的进度信息输出到Output窗口。 </font></p>
<p><font size=2>3) Debug　设置是否生成调试信息，以及调试信息的格式。</font></p>
<p><font size=2>Dubug info，格式可以有Microsoft Format、COFF Format（Common Object File Format）和Both Formats三种选择； <br>Separate Types，表示将Debug格式信息以独立的.PDB文件存放，还是直接放在各个源文件的.PDB文件中。选中的话，表示采用后者的方式，这种方式调试启动比较快。</font></p>
<p><br><font size=2>4) Input　这里可以指定要连接的库文件，放弃连接的库文件。还可以增加额外的库文件目录，一般是相对于本项目的目录，如..\Lib。</font></p>
<p><font size=2>Force Symbol References，可以指定连接特定符号定义的库。 </font></p>
<p><font size=2>5) Output　</font></p>
<p><font size=2>Base Address　可以改变程序默认的基地址（EXE文件默认为0x400000，DLL默认为0x10000000），操作系统装载一个程序时总是试着先从这个基地址开始。 <br>Entry-Point Symbol　可以指定程序的入口地址，一般为一个函数名（且必须采用__stdcall调用约定）。一般Win32的程序，EXE的入口为 WinMain，DLL的入口为DllEntryPoint；最好让连接器自动设置程序的入口点。默认情况下，通过一个C的运行时库函数来实现：控制台程序采用mainCRTStartup (或wmainCRTStartup)去调用程序的main (或wmain)函数；Windows程序采用WinMainCRTStartup (或 wWinMainCRTStartup)调用程序的WinMain (或 wWinMain，必须采用__stdcall调用约定)；DLL采用_DllMainCRTStartup调用DllMain函数（必须采用 __stdcall调用约定）。 <br>Stack allocations　用以设置程序使用的堆栈大小（请使用十进制），默认为1兆字节。 <br>Version Information　告诉连接器在EXE或DLL文件的开始部分放上版本号。 </font></p>
<p><font size=2>值得注意的是：</font></p>
<p><font size=2>上面各个参数是大小写敏感的； <br>在参数后加上"-"表示该参数无效； <br>各个参数值选项有"*"的表示为该参数的默认值； <br>可以使用页右上角的"Reset"按钮来恢复该页的所有默认设置。 </font></p>
<p><font size=2>--------------------------------------------------------------------------------<br>其它一些参数设置。</font></p>
<p><font size=2>1) Project-&gt;Settings-&gt;General，可以设置连接MFC库的方式（静态或动态）。如果是动态连接，在MFC软件发布时不要忘了带上MFC的DLL。</font></p>
<p><font size=2>2) Project-&gt;Settings-&gt;Debug，可以设置调试时运行的可执行文件，以及命令行参数等。</font></p>
<p><font size=2>3) Project-&gt;Settings-&gt;Custom Build，可以设置编译/连接成功后自动执行一些操作。比较有用的是，写COM时希望VC对编译通过的COM文件自动注册，可以如下设置：</font></p>
<p><font size=2>Description: Register COM</font></p>
<p><font size=2>Commands: regsvr32 /s /c $(TargetPath) </font></p>
<p><font size=2>echo regsvr32 exe.time &gt; $(TargetDir)\$(TargetName).trg</font></p>
<p><font size=2>Outputs: $(TargetDir)\$(TargetName).trg</font></p>
<p><br><font size=2>4) Tools-&gt;Options-&gt;Directories，设置系统的Include、Library路径。</font></p>
<p><font size=2>--------------------------------------------------------------------------------<br>小窍门: 项目中的.ncb、.opt、.aps、.clw文件以及Debug、Release目录下的所有文件都可以删掉的。</font></p>
<img src ="http://www.cppblog.com/xpzhou/aggbug/22672.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xpzhou/" target="_blank">榕树藤</a> 2007-04-23 19:51 <a href="http://www.cppblog.com/xpzhou/archive/2007/04/23/22672.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC中基于 Windows 的精确定时</title><link>http://www.cppblog.com/xpzhou/archive/2007/04/20/22426.html</link><dc:creator>榕树藤</dc:creator><author>榕树藤</author><pubDate>Fri, 20 Apr 2007 13:03:00 GMT</pubDate><guid>http://www.cppblog.com/xpzhou/archive/2007/04/20/22426.html</guid><wfw:comment>http://www.cppblog.com/xpzhou/comments/22426.html</wfw:comment><comments>http://www.cppblog.com/xpzhou/archive/2007/04/20/22426.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xpzhou/comments/commentRss/22426.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xpzhou/services/trackbacks/22426.html</trackback:ping><description><![CDATA[在工业生产控制系统中，有许多需要定时完成的操作，如定时显示当前时间，定时刷新屏幕上的进度条，上位<wbr> 机定时向下位机发送命令和传送数据等。特别是在对控制性能要求较高的实时控制系统和数据采集系统中，就更需要精确定时操作。<br>　　众所周知，Windows 是基于消息机制的系统，任何事件的执行都是通过发送和接收消息来完成的。<wbr> 这样就带来了一些问题，如一旦计算机的CPU被某个进程占用，或系统资源紧张时，发送到消息队列<wbr> 中的消息就暂时被挂起，得不到实时处理。因此，不能简单地通过Windows消息引发一个对定时要求<wbr> 严格的事件。另外，由于在Windows中已经封装了计算机底层硬件的访问，所以，要想通过直接利用<wbr> 访问硬件来完成精确定时，也比较困难。所以在实际应用时，应针对具体定时精度的要求，采取相适 应的定时方法。<br>　　VC中提供了很多关于时间操作的函数，利用它们控制程序能够精确地完成定时和计时操作。本文详细介绍了<wbr> VC中基于Windows的精确定时的七种方式，如下图所示：<br><br><img src="http://www.vckbase.com/document/journal/vckbase37/images/MultiTimerDemoimg.gif" border=0><br>图一 图像描述 <br><br>　　方式一：VC中的WM_TIMER消息映射能进行简单的时间控制。首先调用函数SetTimer()设置定时<wbr> 间隔，如SetTimer(0,200,NULL)即为设置200ms的时间间隔。然后在应用程序中增加定时响应函数<wbr> OnTimer()，并在该函数中添加响应的处理语句，用来完成到达定时时间的操作。这种定时方法非常<wbr> 简单，可以实现一定的定时功能，但其定时功能如同Sleep()函数的延时功能一样，精度非常低，最小<wbr> 计时精度仅为30ms，CPU占用低，且定时器消息在多任务操作系统中的优先级很低，不能得到及时响<wbr> 应，往往不能满足实时控制环境下的应用。只可以用来实现诸如位图的动态显示等对定时精度要求不高的情况。如示例工程中的Timer1。 <br>　　方式二：VC中使用sleep()函数实现延时，它的单位是ms，如延时2秒，用sleep(2000)。精度非常<wbr> 低，最小计时精度仅为30ms，用sleep函数的不利处在于延时期间不能处理其他的消息，如果时间太<wbr> 长，就好象死机一样，CPU占用率非常高，只能用于要求不高的延时程序中。如示例工程中的Timer2。<br>　　方式三：利用COleDateTime类和COleDateTimeSpan类结合WINDOWS的消息处理过程来实现秒级延时。如示例工程中的Timer3和Timer3_1。以下是实现2秒的延时代码： <br>COleDateTime&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; start_time = COleDateTime::GetCurrentTime();<br>COleDateTimeSpan&nbsp; end_time= COleDateTime::GetCurrentTime()-start_time;<br>while(end_time.GetTotalSeconds()&lt; 2) //实现延时2秒<br>{<br>MSG&nbsp;&nbsp; msg;<br>GetMessage(&amp;msg,NULL,0,0);<br>TranslateMessage(&amp;msg);<br>DispatchMessage(&amp;msg);<br>//以上四行是实现在延时或定时期间能处理其他的消息，<br>//虽然这样可以降低CPU的占有率，<br>//但降低了延时或定时精度，实际应用中可以去掉。<br>end_time = COleDateTime::GetCurrentTime()-start_time;<br>}//这样在延时的时候我们也能够处理其他的消息。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 　　方式四：在精度要求较高的情况下，VC中可以利用GetTickCount()函数，该函数的返回值是<wbr> &nbsp;DWORD型，表示以ms为单位的计算机启动后经历的时间间隔。精度比WM_TIMER消息映射高，在较<wbr> 短的定时中其计时误差为15ms，在较长的定时中其计时误差较低，如果定时时间太长，就好象死机一样，CPU占用率非常高，只能用于要求不高的延时程序中。如示例工程中的Timer4和Timer4_1。下列代码可以实现50ms的精确定时：<br>
<pre>DWORD dwStart = GetTickCount();
DWORD dwEnd   = dwStart;
do
{
dwEnd = GetTickCount()-dwStart;
}while(dwEnd &lt;50);</pre>
为使GetTickCount()函数在延时或定时期间能处理其他的消息，可以把代码改为：<br>
<pre>DWORD dwStart = GetTickCount();
DWORD dwEnd   = dwStart;
do
{
MSG   msg;
GetMessage(&amp;msg,NULL,0,0);
TranslateMessage(&amp;msg);
DispatchMessage(&amp;msg);
dwEnd = GetTickCount()-dwStart;
}while(dwEnd &lt;50);</pre>
虽然这样可以降低CPU的占有率，并在延时或定时期间也能处理其他的消息，但降低了延时或定时精度。<br>　　方式五：与GetTickCount()函数类似的多媒体定时器函数DWORD timeGetTime(void)，该函数定时精<wbr> 度为ms级，返回从Windows启动开始经过的毫秒数。微软公司在其多媒体Windows中提供了精确定时器的底<wbr> 层API持，利用多媒体定时器可以很精确地读出系统的当前时间，并且能在非常精确的时间间隔内完成一<wbr> 个事件、函数或过程的调用。不同之处在于调用DWORD timeGetTime(void) 函数之前必须将 Winmm.lib&nbsp;<wbr> 和 Mmsystem.h 添加到工程中，否则在编译时提示DWORD timeGetTime(void)函数未定义。由于使用该<wbr> 函数是通过查询的方式进行定时控制的，所以，应该建立定时循环来进行定时事件的控制。如示例工程中的Timer5和Timer5_1。<br>　　方式六：使用多媒体定时器timeSetEvent()函数，该函数定时精度为ms级。利用该函数可以实现周期性的函数调用。如示例工程中的Timer6和Timer6_1。函数的原型如下： <br>
<pre>MMRESULT timeSetEvent（ UINT uDelay,
UINT uResolution,
LPTIMECALLBACK lpTimeProc,
WORD dwUser,
UINT fuEvent ）</pre>
　　该函数设置一个定时回调事件，此事件可以是一个一次性事件或周期性事件。事件一旦被激活，便调用指定的回调函数，<wbr> 成功后返回事件的标识符代码，否则返回NULL。函数的参数说明如下：<br>
<pre>uDelay：以毫秒指定事件的周期。
Uresolution：以毫秒指定延时的精度，数值越小定时器事件分辨率越高。缺省值为1ms。
LpTimeProc：指向一个回调函数。
DwUser：存放用户提供的回调数据。
FuEvent：指定定时器事件类型：
TIME_ONESHOT：uDelay毫秒后只产生一次事件
TIME_PERIODIC ：每隔uDelay毫秒周期性地产生事件。      </pre>
　　具体应用时，可以通过调用timeSetEvent()函数，将需要周期性执行的任务定义在LpTimeProc回调函数<wbr> 中(如：定时采样、控制等)，从而完成所需处理的事件。需要注意的是，任务处理的时间不能大于周期间隔时间。另外，在定时器使用完毕后，<wbr> 应及时调用timeKillEvent()将之释放。 <br>　　方式七：对于精确度要求更高的定时操作，则应该使用QueryPerformanceFrequency()和<wbr> QueryPerformanceCounter()函数。这两个函数是VC提供的仅供Windows 95及其后续版本使用的精确时间函数，并要求计算机从硬件上支持精确定时器。如示例工程中的Timer7、Timer7_1、Timer7_2、Timer7_3。<br>QueryPerformanceFrequency()函数和QueryPerformanceCounter()函数的原型如下：<br>
<pre>BOOL  QueryPerformanceFrequency(LARGE_INTEGER ＊lpFrequency);
BOOL  QueryPerformanceCounter(LARGE_INTEGER ＊lpCount);</pre>
　　数据类型ARGE_INTEGER既可以是一个8字节长的整型数，也可以是两个4字节长的整型数的联合结构，<wbr> 其具体用法根据编译器是否支持64位而定。该类型的定义如下：<br>
<pre>typedef union _LARGE_INTEGER
{
struct
{
DWORD LowPart ;// 4字节整型数
LONG  HighPart;// 4字节整型数
};
LONGLONG QuadPart ;// 8字节整型数
}LARGE_INTEGER ;</pre>
　　在进行定时之前，先调用QueryPerformanceFrequency()函数获得机器内部定时器的时钟频率，<wbr> 然后在需要严格定时的事件发生之前和发生之后分别调用QueryPerformanceCounter()函数，利用两次获得的计数之差及时钟频率，计算出事件经<wbr> 历的精确时间。下列代码实现1ms的精确定时：<br>
<pre>LARGE_INTEGER litmp;
LONGLONG QPart1,QPart2;
double dfMinus, dfFreq, dfTim;
QueryPerformanceFrequency(&amp;litmp);
dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
QueryPerformanceCounter(&amp;litmp);
QPart1 = litmp.QuadPart;// 获得初始值
do
{
QueryPerformanceCounter(&amp;litmp);
QPart2 = litmp.QuadPart;//获得中止值
dfMinus = (double)(QPart2-QPart1);
dfTim = dfMinus / dfFreq;// 获得对应的时间值，单位为秒
}while(dfTim&lt;0.001);</pre>
　　其定时误差不超过1微秒，精度与CPU等机器配置有关。 下面的程序用来测试函数Sleep(100)的精确持续时间：<br>
<pre>LARGE_INTEGER litmp;
LONGLONG QPart1,QPart2;
double dfMinus, dfFreq, dfTim;
QueryPerformanceFrequency(&amp;litmp);
dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
QueryPerformanceCounter(&amp;litmp);
QPart1 = litmp.QuadPart;// 获得初始值
Sleep(100);
QueryPerformanceCounter(&amp;litmp);
QPart2 = litmp.QuadPart;//获得中止值
dfMinus = (double)(QPart2-QPart1);
dfTim = dfMinus / dfFreq;// 获得对应的时间值，单位为秒     </pre>
　　由于Sleep()函数自身的误差，上述程序每次执行的结果都会有微小误差。下列代码实现1微秒的精确定时：<br>
<pre>LARGE_INTEGER litmp;
LONGLONG QPart1,QPart2;
double dfMinus, dfFreq, dfTim;
QueryPerformanceFrequency(&amp;litmp);
dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
QueryPerformanceCounter(&amp;litmp);
QPart1 = litmp.QuadPart;// 获得初始值
do
{
QueryPerformanceCounter(&amp;litmp);
QPart2 = litmp.QuadPart;//获得中止值
dfMinus = (double)(QPart2-QPart1);
dfTim = dfMinus / dfFreq;// 获得对应的时间值，单位为秒
}while(dfTim&lt;0.000001);</pre>
其定时误差一般不超过0.5微秒，精度与CPU等机器配置有关。<br>
<p><a href="http://www.vckbase.com/code/downcode.asp?id=2537">示例工程下载</a><br><br></p><img src ="http://www.cppblog.com/xpzhou/aggbug/22426.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xpzhou/" target="_blank">榕树藤</a> 2007-04-20 21:03 <a href="http://www.cppblog.com/xpzhou/archive/2007/04/20/22426.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>STL实践指南</title><link>http://www.cppblog.com/xpzhou/archive/2007/04/20/22341.html</link><dc:creator>榕树藤</dc:creator><author>榕树藤</author><pubDate>Fri, 20 Apr 2007 00:18:00 GMT</pubDate><guid>http://www.cppblog.com/xpzhou/archive/2007/04/20/22341.html</guid><wfw:comment>http://www.cppblog.com/xpzhou/comments/22341.html</wfw:comment><comments>http://www.cppblog.com/xpzhou/archive/2007/04/20/22341.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xpzhou/comments/commentRss/22341.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xpzhou/services/trackbacks/22341.html</trackback:ping><description><![CDATA[<p align=left><font color=red>介绍</font><br>这是一篇指导您如何在Microsoft Visual Studio下学习STL并进行实践的文章。这篇文章从STL的基础知识讲起，循序渐进，逐步深入，涉及到了STL编写代码的方法、STL代码的编译和调试、命名空间（namespace）、STL中的ANSI / ISO字符串、各种不同类型的容器（container）、模板（template）、游标（Iterator）、算法（Algorithms）、分配器（Allocator）、容器的嵌套等方面的问题，作者在这篇文章中对读者提出了一些建议，并指出了使用STL时应该注意的问题。这篇文章覆盖面广，视角全面。不仅仅适合初学者学习STL，更是广大读者使用STL编程的实践指南。
<p><br>这是一篇指导您如何在Microsoft Visual Studio下学习STL并进行实践的文章。这篇文章从STL的基础知识讲起，循序渐进，逐步深入，涉及到了STL编写代码的方法、STL代码的编译和调试、命名空间（namespace）、STL中的ANSI / ISO字符串、各种不同类型的容器（container）、模板（template）、游标（Iterator）、算法（Algorithms）、分配器（Allocator）、容器的嵌套等方面的问题，作者在这篇文章中对读者提出了一些建议，并指出了使用STL时应该注意的问题。这篇文章覆盖面广，视角全面。不仅仅适合初学者学习STL，更是广大读者使用STL编程的实践指南。<br><br>STL简介<br><br>STL (标准模版库，Standard Template Library)是当今每个从事C++编程的人需要掌握的一项不错的技术。我觉得每一个初学STL的人应该花费一段时间来熟悉它，比如，学习STL时会有急剧升降的学习曲线，并且有一些命名是不太容易凭直觉就能够记住的（也许是好记的名字已经被用光了），然而如果一旦你掌握了STL，你就不会觉得头痛了。和MFC相比，STL更加复杂和强大。<br>STL有以下的一些优点：<br><br>可以方便容易地实现搜索数据或对数据排序等一系列的算法；<br><br>调试程序时更加安全和方便；<br><br>即使是人们用STL在UNIX平台下写的代码你也可以很容易地理解（因为STL是跨平台的）。<br><br>背景知识<br><br>写这一部分是让一些初学计算机的读者在富有挑战性的计算机科学领域有一个良好的开端，而不必费力地了解那无穷无尽的行话术语和沉闷的规则，在这里仅仅把那些行话和规则当作STLer们用于自娱的创造品吧。<br><br>使用代码<br>本文使用的代码在STL实践中主要具有指导意义。<br><br>一些基础概念的定义<br><br>模板（Template）——类（以及结构等各种数据类型和函数）的宏（macro）。有时叫做甜饼切割机（cookie cutter），正规的名称应叫做范型（generic）——一个类的模板叫做范型类（generic class），而一个函数的模板也自然而然地被叫做范型函数（generic function）。<br>STL——标准模板库，一些聪明人写的一些模板，现在已成为每个人所使用的标准C++语言中的一部分。<br>容器（Container）——可容纳一些数据的模板类。STL中有vector，set，map，multimap和deque等容器。<br>向量（Vector）——基本数组模板，这是一个容器。<br>游标（Iterator）——这是一个奇特的东西，它是一个指针，用来指向STL容器中的元素，也可以指向其它的元素。<br><br>Hello World程序<br><br>我愿意在我的黄金时间在这里写下我的程序：一个hello world程序。这个程序将一个字符串传送到一个字符向量中，然后每次显示向量中的一个字符。向量就像是盛放变长数组的花园，大约所有STL容器中有一半是基于向量的，如果你掌握了这个程序，你便差不多掌握了整个STL的一半了。<br><br><br>//程序：vector演示一<br>//目的：理解STL中的向量<br><br>// #include "stdafx.h" -如果你使用预编译的头文件就包含这个头文件<br>#include &lt;vector&gt;&nbsp;&nbsp;// STL向量的头文件。这里没有".h"。<br>#include &lt;iostream&gt;&nbsp;&nbsp;// 包含cout对象的头文件。<br>using namespace std;&nbsp;&nbsp;//保证在程序中可以使用std命名空间中的成员。<br><br>char* szHW = "Hello World";&nbsp;&nbsp;<br>//这是一个字符数组，以&#8221;\0&#8221;结束。<br><br>int main(int argc, char* argv[])<br>{<br>&nbsp;&nbsp;vector &lt;char&gt; vec;&nbsp;&nbsp;//声明一个字符向量vector (STL中的数组) <br><br>&nbsp;&nbsp;//为字符数组定义一个游标iterator。<br>&nbsp;&nbsp;vector &lt;char&gt;::iterator vi;<br><br>&nbsp;&nbsp;//初始化字符向量，对整个字符串进行循环，<br>&nbsp;&nbsp;//用来把数据填放到字符向量中，直到遇到&#8221;\0&#8221;时结束。<br>&nbsp;&nbsp;char* cptr = szHW;&nbsp;&nbsp;// 将一个指针指向&#8220;Hello World&#8221;字符串<br>&nbsp;&nbsp;while (*cptr != '\0')<br>&nbsp;&nbsp;{&nbsp;&nbsp;vec.push_back(*cptr);&nbsp;&nbsp;cptr++;&nbsp;&nbsp;}<br>&nbsp;&nbsp;// push_back函数将数据放在向量的尾部。<br><br>&nbsp;&nbsp;// 将向量中的字符一个个地显示在控制台<br>&nbsp;&nbsp;for (vi=vec.begin(); vi!=vec.end(); vi++)&nbsp;&nbsp;<br>&nbsp;&nbsp;// 这是STL循环的规范化的开始——通常是 "!=" ， 而不是 "&lt;"<br>&nbsp;&nbsp;// 因为"&lt;" 在一些容器中没有定义。 <br>&nbsp;&nbsp;// begin()返回向量起始元素的游标（iterator），end()返回向量末尾元素的游标（iterator）。<br>&nbsp;&nbsp;{&nbsp;&nbsp;cout &lt;&lt; *vi;&nbsp;&nbsp;}&nbsp;&nbsp;// 使用运算符 &#8220;*&#8221; 将数据从游标指针中提取出来。<br>&nbsp;&nbsp;cout &lt;&lt; endl;&nbsp;&nbsp;// 换行<br><br>&nbsp;&nbsp;return 0;<br>}<br><br><br>push_back是将数据放入vector（向量）或deque（双端队列）的标准函数。Insert是一个与之类似的函数，然而它在所有容器中都可以使用，但是用法更加复杂。end()实际上是取末尾加一（取容器中末尾的前一个元素），以便让循环正确运行——它返回的指针指向最靠近数组界限的数据。就像普通循环中的数组，比如for (i=0; i&lt;6; i++) {ar[i] = i;} ——ar[6]是不存在的，在循环中不会达到这个元素，所以在循环中不会出现问题。<br><br>STL的烦恼之一——初始化<br><br>STL令人烦恼的地方是在它初始化的时候。STL中容器的初始化比C/C++数组初始化要麻烦的多。你只能一个元素一个元素地来，或者先初始化一个普通数组再通过转化填放到容器中。我认为人们通常可以这样做：<br><br><br>//程序：初始化演示<br>//目的：为了说明STL中的向量是怎样初始化的。<br><br>#include &lt;cstring&gt;&nbsp;&nbsp;// &lt;cstring&gt;和&lt;string.h&gt;相同<br>#include &lt;vector&gt;<br>using namespace std;<br><br>int ar[10] = {&nbsp;&nbsp;12, 45, 234, 64, 12, 35, 63, 23, 12, 55&nbsp;&nbsp;};<br>char* str = "Hello World";<br><br>int main(int argc, char* argv[])<br>{<br>&nbsp;&nbsp;vector &lt;int&gt; vec1(ar, ar+10);<br>&nbsp;&nbsp;vector &lt;char&gt; vec2(str, str+strlen(str));<br>&nbsp;&nbsp;return 0;<br>}<br><br><br><br>在编程中，有很多种方法来完成同样的工作。另一种填充向量的方法是用更加熟悉的方括号，比如下面的程序：<br><br>//程序：vector演示二<br>//目的：理解带有数组下标和方括号的STL向量<br><br>#include &lt;cstring&gt;<br>#include &lt;vector&gt;<br>#include &lt;iostream&gt;<br>using namespace std;<br><br>char* szHW = "Hello World";<br>int main(int argc, char* argv[])<br>{<br>&nbsp;&nbsp;vector &lt;char&gt; vec(strlen(sHW)); //为向量分配内存空间<br>&nbsp;&nbsp;int i, k = 0;<br>&nbsp;&nbsp;char* cptr = szHW;<br>&nbsp;&nbsp;while (*cptr != '\0')<br>&nbsp;&nbsp;{&nbsp;&nbsp;vec[k] = *cptr;&nbsp;&nbsp;cptr++;&nbsp;&nbsp;k++;&nbsp;&nbsp;}<br>&nbsp;&nbsp;for (i=0; i&lt;vec.size(); i++)<br>&nbsp;&nbsp;{&nbsp;&nbsp;cout &lt;&lt; vec[i];&nbsp;&nbsp;}<br>&nbsp;&nbsp;cout &lt;&lt; endl;<br>&nbsp;&nbsp;return 0;<br>}<br><br><br>这个例子更加清晰，但是对游标（iterator）的操作少了，并且定义了额外的整形数作为下标，而且，你必须清楚地在程序中说明为向量分配多少内存空间。<br><br>命名空间（Namespace）<br><br>与STL相关的概念是命名空间（namespace）。STL定义在std命名空间中。有3种方法声明使用的命名空间：<br><br>1．用using关键字使用这个命名空间，在文件的顶部，但在声明的头文件下面加入：<br>using namespace std;<br>这对单个工程来说是最简单也是最好的方法，这个方法可以把你的代码限定在std命名空间中。<br><br>2．使用每一个模板前对每一个要使用的对象进行声明（就像原形化）：<br>using std::cout;<br>using std::endl;<br>using std::flush;<br>using std::set;<br>using std::inserter;<br>尽管这样写有些冗长，但可以对记忆使用的函数比较有利，并且你可以容易地声明并使用其他命名空间中的成员。<br><br>3．在每一次使用std命名空间中的模版时，使用std域标识符。比如：<br>typedef std::vector VEC_STR;<br>这种方法虽然写起来比较冗长，但是是在混合使用多个命名空间时的最好方法。一些STL的狂热者一直使用这种方法，并且把不使用这种方法的人视为异类。一些人会通过这种方法建立一些宏来简化问题。<br><br>除此之外，你可以把using namespace std加入到任何域中，比如可以加入到函数的头部或一个控制循环体中。<br><br>一些建议<br><br>为了避免在调试模式（debug mode）出现恼人的警告，使用下面的编译器命令：<br><br>#pragma warning(disable: 4786)<br><br>另一条需要注意的是，你必须确保在两个尖括号之间或尖括号和名字之间用空格隔开，因为是为了避免同&#8220;&gt;&gt;&#8221;移位运算符混淆。比如<br>vector &lt;list&lt;int&gt;&gt; veclis;<br>这样写会报错，而这样写：<br>vector &lt;list &lt;int&gt; &gt; veclis;<br>就可以避免错误。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;STL实践指南（中）<br><br>STL实践指南&nbsp;&nbsp;Practical Guide to STL<br>作者：Jeff Bogan <br>翻译：周翔<br><br><br>（接上篇）<br><br>另一种容器——集合（set）<br><br>这是微软帮助文档中对集合（set）的解释：&#8220;描述了一个控制变长元素序列的对象（注：set中的key和value是Key类型的，而map中的key和value是一个pair结构中的两个分量）的模板类，每一个元素包含了一个排序键（sort key）和一个值(value)。对这个序列可以进行查找、插入、删除序列中的任意一个元素，而完成这些操作的时间同这个序列中元素个数的对数成比例关系，并且当游标指向一个已删除的元素时，删除操作无效。&#8221;<br>而一个经过更正的和更加实际的定义应该是：一个集合（set）是一个容器，它其中所包含的元素的值是唯一的。这在收集一个数据的具体值的时候是有用的。集合中的元素按一定的顺序排列，并被作为集合中的实例。如果你需要一个键/值对（pair）来存储数据，map是一个更好的选择。一个集合通过一个链表来组织，在插入操作和删除操作上比向量（vector）快，但查找或添加末尾的元素时会有些慢。<br>下面是一个例子：<br><br>//程序：set演示<br>//目的：理解STL中的集合（set）<br><br>#include &lt;string&gt;<br>#include &lt;set&gt;<br>#include &lt;iostream&gt;<br>using namespace std;<br><br>int main(int argc, char* argv[])<br>{<br>&nbsp;&nbsp;set &lt;string&gt; strset;<br>&nbsp;&nbsp;set &lt;string&gt;::iterator si;<br>&nbsp;&nbsp;strset.insert("cantaloupes");<br>&nbsp;&nbsp;strset.insert("apple");<br>&nbsp;&nbsp;strset.insert("orange");<br>&nbsp;&nbsp;strset.insert("banana");<br>&nbsp;&nbsp;strset.insert("grapes");<br>&nbsp;&nbsp;strset.insert("grapes");&nbsp;&nbsp;<br>&nbsp;&nbsp;for (si=strset.begin(); si!=strset.end(); si++)&nbsp;&nbsp;<br>&nbsp;&nbsp;{&nbsp;&nbsp;cout &lt;&lt; *si &lt;&lt; " ";&nbsp;&nbsp;}<br>&nbsp;&nbsp;cout &lt;&lt; endl;<br>&nbsp;&nbsp;return 0;<br>}<br><br>// 输出： apple banana cantaloupes grapes orange<br>//注意：输出的集合中的元素是按字母大小顺序排列的，而且每个值都不重复。<br><br><br>如果你感兴趣的话，你可以将输出循环用下面的代码替换：<br><br>copy(strset.begin(), strset.end(), ostream_iterator&lt;string&gt;(cout, " "));<br><br>.集合（set）虽然更强大，但我个人认为它有些不清晰的地方而且更容易出错，如果你明白了这一点，你会知道用集合（set）可以做什么。<br><br>所有的STL容器<br><br>容器（Container）的概念的出现早于模板（template），它原本是一个计算机科学领域中的一个重要概念，但在这里，它的概念和STL混合在一起了。下面是在STL中出现的7种容器：<br><br>vector（向量）——STL中标准而安全的数组。只能在vector 的&#8220;前面&#8221;增加数据。<br>deque（双端队列double-ended queue）——在功能上和vector相似，但是可以在前后两端向其中添加数据。 <br>list（列表）——游标一次只可以移动一步。如果你对链表已经很熟悉，那么STL中的list则是一个双向链表（每个节点有指向前驱和指向后继的两个指针）。<br>set（集合）——包含了经过排序了的数据，这些数据的值(value)必须是唯一的。<br>map（映射）——经过排序了的二元组的集合，map中的每个元素都是由两个值组成，其中的key（键值，一个map中的键值必须是唯一的）是在排序或搜索时使用，它的值可以在容器中重新获取；而另一个值是该元素关联的数值。比如，除了可以ar[43] = "overripe"这样找到一个数据，map还可以通过ar["banana"] = "overripe"这样的方法找到一个数据。如果你想获得其中的元素信息，通过输入元素的全名就可以轻松实现。<br>multiset（多重集）——和集合（set）相似，然而其中的值不要求必须是唯一的（即可以有重复）。<br>multimap（多重映射）——和映射（map）相似，然而其中的键值不要求必须是唯一的（即可以有重复）。<br>注意：如果你阅读微软的帮助文档，你会遇到对每种容器的效率的陈述。比如：log(n*n)的插入时间。除非你要处理大量的数据，否则这些时间的影响是可以忽略的。如果你发现你的程序有明显的滞后感或者需要处理时间攸关（time critical）的事情，你可以去了解更多有关各种容器运行效率的话题。<br><br>怎样在一个map中使用类？<br><br>Map是一个通过key（键）来获得value(值)的模板类。<br>另一个问题是你希望在map中使用自己的类而不是已有的数据类型，比如现在已经用过的int。建立一个&#8220;为模板准备的（template-ready）&#8221;类，你必须确保在该类中包含一些成员函数和重载操作符。下面的一些成员是必须的：<br><br>缺省的构造函数（通常为空）<br><br>拷贝构造函数<br><br>重载的&#8221;=&#8221;运算符<br><br>你应该重载尽可能多的运算符来满足特定模板的需要，比如，如果你想定义一个类作为 map中的键（key），你必须重载相关的运算符。但在这里不对重载运算符做过多讨论了。<br><br>//程序：映射自定义的类。<br>//目的：说明在map中怎样使用自定义的类。<br><br>#include &lt;string&gt;<br>#include &lt;iostream&gt;<br>#include &lt;vector&gt;<br>#include &lt;map&gt;<br>using namespace std;<br><br>class CStudent<br>{<br>public :<br>&nbsp;&nbsp;int nStudentID;<br>&nbsp;&nbsp;int nAge;<br>public :<br>&nbsp;&nbsp;//缺省构造函数——通常为空<br>&nbsp;&nbsp;CStudent()&nbsp;&nbsp;{&nbsp;&nbsp;}<br>&nbsp;&nbsp;// 完整的构造函数<br>&nbsp;&nbsp;CStudent(int nSID, int nA)&nbsp;&nbsp;{&nbsp;&nbsp;nStudentID=nSID; nAge=nA;&nbsp;&nbsp;}<br>&nbsp;&nbsp;//拷贝构造函数<br>&nbsp;&nbsp;CStudent(const CStudent&amp; ob)&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;nStudentID=ob.nStudentID; nAge=ob.nAge;&nbsp;&nbsp;}<br>&nbsp;&nbsp;// 重载&#8220;=&#8221;<br>&nbsp;&nbsp;void operator = (const CStudent&amp; ob)&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;nStudentID=ob.nStudentID; nAge=ob.nAge;&nbsp;&nbsp;}<br>};<br><br>int main(int argc, char* argv[])<br>{<br>&nbsp;&nbsp;map &lt;string, CStudent&gt; mapStudent;<br><br>&nbsp;&nbsp;mapStudent["Joe Lennon"] = CStudent(103547, 22);<br>&nbsp;&nbsp;mapStudent["Phil McCartney"] = CStudent(100723, 22);<br>&nbsp;&nbsp;mapStudent["Raoul Starr"] = CStudent(107350, 24);<br>&nbsp;&nbsp;mapStudent["Gordon Hamilton"] = CStudent(102330, 22);<br><br>&nbsp;&nbsp;// 通过姓名来访问Cstudent类中的成员<br>&nbsp;&nbsp;cout &lt;&lt; "The Student number for Joe Lennon is " &lt;&lt; <br>&nbsp;&nbsp;&nbsp;&nbsp;(mapStudent["Joe Lennon"].nStudentID) &lt;&lt; endl;<br><br>&nbsp;&nbsp;return 0;<br>}<br><br><br><br>TYPEDEF<br><br>如果你喜欢使用typedef关键字，下面是个例子：<br>typedef set &lt;int&gt; SET_INT;<br>typedef SET_INT::iterator SET_INT_ITER<br><br><br>编写代码的一个习惯就是使用大写字母和下划线来命名数据类型。<br><br>ANSI / ISO字符串<br><br>ANSI/ISO字符串在STL容器中使用得很普遍。这是标准的字符串类，并得到了广泛地提倡，然而在缺乏格式声明的情况下就会出问题。你必须使用&#8220;&lt;&lt;&#8221;和输入输出流（iostream）代码（如dec, width等）将字符串串联起来。<br>可在必要的时候使用c_str()来重新获得字符指针。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;STL实践指南（下）<br>STL实践指南&nbsp;&nbsp;Practical Guide to STL<br>作者：Jeff Bogan <br>翻译：周翔<br><br><br>（接中篇）<br><br>游标（Iterator）<br><br>我说过游标是指针，但不仅仅是指针。游标和指针很像，功能很像指针，但是实际上，游标是通过重载一元的&#8221;*&#8221;和&#8221;-&gt;&#8221;来从容器中间接地返回一个值。将这些值存储在容器中并不是一个好主意，因为每当一个新值添加到容器中或者有一个值从容器中删除，这些值就会失效。在某种程度上，游标可以看作是句柄（handle）。通常情况下游标（iterator）的类型可以有所变化，这样容器也会有几种不同方式的转变：<br>iterator——对于除了vector以外的其他任何容器，你可以通过这种游标在一次操作中在容器中朝向前的方向走一步。这意味着对于这种游标你只能使用&#8220;++&#8221;操作符。而不能使用&#8220;--&#8221;或&#8220;+=&#8221;操作符。而对于vector这一种容器，你可以使用&#8220;+=&#8221;、&#8220;—&#8221;、&#8220;++&#8221;、&#8220;-=&#8221;中的任何一种操作符和&#8220;&lt;&#8221;、&#8220;&lt;=&#8221;、&#8220;&gt;&#8221;、&#8220;&gt;=&#8221;、&#8220;==&#8221;、&#8220;!=&#8221;等比较运算符。<br>reverse_iterator ——如果你想用向后的方向而不是向前的方向的游标来遍历除vector之外的容器中的元素，你可以使用reverse_iterator 来反转遍历的方向，你还可以用rbegin()来代替begin()，用rend()代替end()，而此时的&#8220;++&#8221;操作符会朝向后的方向遍历。 <br>const_iterator ——一个向前方向的游标，它返回一个常数值。你可以使用这种类型的游标来指向一个只读的值。<br>const_reverse_iterator ——一个朝反方向遍历的游标，它返回一个常数值。<br><br>Set和Map中的排序<br><br>除了类型和值外，模板含有其他的参数。你可以传递一个回调函数（通常所说的声明&#8220;predicate&#8221;——这是带有一个参数的函数返回一个布尔值）。例如，如果你想自动建立一个集合，集合中的元素按升序排列，你可以用简明的方法建立一个set类：<br><br>set &lt;int, greater&lt;int&gt; &gt; set1<br><br>greater 是另一个模板函数（范型函数），当值放置在容器中后，它用来为这些值排序。如果你想按降序排列这些值，你可以这样写：<br><br>set &lt;int, less&lt;int&gt; &gt; set1<br><br>在实现算法时，将声明（predicate）作为一个参数传递到一个STL模板类中时会遇到很多的其他情况，下面将会对这些情况进行详细描述。<br><br>STL 的烦恼之二——错误信息<br><br>这些模板的命名需要对编译器进行扩充，所以当编译器因某种原因发生故障时，它会列出一段很长的错误信息，并且这些错误信息晦涩难懂。我觉得处理这样的难题没有什么好办法。但最好的方法是去查找并仔细研究错误信息指明代码段的尾端。还有一个烦恼就是：当你双击错误信息时，它会将错误指向模版库的内部代码，而这些代码就更难读了。一般情况下，纠错的最好方法是重新检查一下你的代码，运行时忽略所有的警告信息。<br><br>算法（Algorithms）<br><br>算法是模板中使用的函数。这才真正开始体现STL的强大之处。你可以学习一些大多数模板容器中都会用到的一些算法函数，这样你可以通过最简便的方式进行排序、查找、交换等操作。STL中包含着一系列实现算法的函数。比如：sort(vec.begin()+1, vec.end()-1)可以实现对除第一个和最后一个元素的其他元素的排序操作。<br>容器自身不能使用算法，但两个容器中的游标可以限定容器中使用算法的元素。既然这样，算法不直接受到容器的限制，而是通过采用游标，算法才能够得到支持。此外，很多次你会遇到传递一个已经准备好了的函数（以前提到的声明：predicate）作为参数，你也可以传递以前的旧值。<br>下面的例子演示了怎样使用算法：<br><br>//程序：测试分数统计<br>//目的：通过对向量中保存的分数的操作说明怎样使用算法 <br><br>#include &lt;algorithm&gt;&nbsp;&nbsp;//如果要使用算法函数，你必须要包含这个头文件。<br>#include &lt;numeric&gt;&nbsp;&nbsp;// 包含accumulate（求和）函数的头文件<br>#include &lt;vector&gt;<br>#include &lt;iostream&gt;<br>using namespace std;<br><br>int testscore[] = {67, 56, 24, 78, 99, 87, 56};<br><br>//判断一个成绩是否通过了考试<br>bool passed_test(int n)<br>{<br>&nbsp;&nbsp;return (n &gt;= 60);<br>}<br><br>// 判断一个成绩是否不及格<br>bool failed_test(int n)<br>{<br>&nbsp;&nbsp;return (n &lt; 60);<br>}<br><br>int main(int argc, char* argv[])<br>{<br>&nbsp;&nbsp;int total;<br>&nbsp;&nbsp;// 初始化向量，使之能够装入testscore数组中的元素<br>&nbsp;&nbsp;vector &lt;int&gt; vecTestScore(testscore, <br>&nbsp;&nbsp;&nbsp;&nbsp; testscore + sizeof(testscore) / sizeof(int));<br>&nbsp;&nbsp;vector &lt;int&gt;::iterator vi;<br><br>&nbsp;&nbsp;// 排序并显示向量中的数据<br>&nbsp;&nbsp;sort(vecTestScore.begin(), vecTestScore.end());<br>&nbsp;&nbsp;cout &lt;&lt; "Sorted Test Scores:" &lt;&lt; endl;<br>&nbsp;&nbsp;for (vi=vecTestScore.begin(); vi != vecTestScore.end(); vi++)<br>&nbsp;&nbsp;{&nbsp;&nbsp;cout &lt;&lt; *vi &lt;&lt; ", ";&nbsp;&nbsp;}<br>&nbsp;&nbsp;cout &lt;&lt; endl;<br><br>&nbsp;&nbsp;// 显示统计信息<br><br>&nbsp;&nbsp;// min_element 返回一个 _iterator_ 类型的对象，该对象指向值最小的那个元素。<br>&nbsp;&nbsp;//&#8220;*&#8221;运算符提取元素中的值。<br>&nbsp;&nbsp;vi = min_element(vecTestScore.begin(), vecTestScore.end());<br>&nbsp;&nbsp;cout &lt;&lt; "The lowest score was " &lt;&lt; *vi &lt;&lt; "." &lt;&lt; endl;<br><br>&nbsp;&nbsp;//与min_element类似，max_element是选出最大值。<br>&nbsp;&nbsp;vi = max_element(vecTestScore.begin(), vecTestScore.end());<br>&nbsp;&nbsp;cout &lt;&lt; "The highest score was " &lt;&lt; *vi &lt;&lt; "." &lt;&lt; endl;<br><br>&nbsp;&nbsp;// 使用声明函数（predicate function，指vecTestScore.begin()和vecTestScore.end()）来确定通过考试的人数。<br>&nbsp;&nbsp;cout &lt;&lt; count_if(vecTestScore.begin(), vecTestScore.end(), passed_test) &lt;&lt; <br>&nbsp;&nbsp;&nbsp;&nbsp;" out of " &lt;&lt; vecTestScore.size() &lt;&lt; <br>&nbsp;&nbsp;&nbsp;&nbsp;" students passed the test" &lt;&lt; endl;<br><br>&nbsp;&nbsp;// 确定有多少人考试挂了<br>&nbsp;&nbsp;cout &lt;&lt; count_if(vecTestScore.begin(), <br>&nbsp;&nbsp;&nbsp;&nbsp;vecTestScore.end(), failed_test) &lt;&lt; <br>&nbsp;&nbsp;&nbsp;&nbsp;" out of " &lt;&lt; vecTestScore.size() &lt;&lt; <br>&nbsp;&nbsp;&nbsp;&nbsp;" students failed the test" &lt;&lt; endl;<br><br>&nbsp;&nbsp;//计算成绩总和<br>&nbsp;&nbsp;total = accumulate(vecTestScore.begin(), <br>&nbsp;&nbsp;&nbsp;&nbsp; vecTestScore.end(), 0);<br>&nbsp;&nbsp;// 计算显示平均成绩<br>&nbsp;&nbsp;cout &lt;&lt; "Average score was " &lt;&lt; <br>&nbsp;&nbsp;&nbsp;&nbsp;(total / (int)(vecTestScore.size())) &lt;&lt; endl;<br><br>&nbsp;&nbsp;return 0;<br>}<br><br><br>Allocator（分配器）<br><br>Allocator用在模板的初始化阶段，是为对象和数组进行分配内存空间和释放空间操作的模板类。它在各种情况下扮演着很神秘的角色，它关心的是高层内存的优化，而且对黑盒测试来说，使用Allocator是最好的选择。通常，我们不需要明确指明它，因为它们通常是作为不用添加的缺省的参数出现的。如果在专业的测试工作中出现了Allocator，你最好搞清楚它是什么。<br><br>Embed Templates（嵌入式模版）和Derive Templates（基模板）<br><br>每当你使用一个普通的类的时候，你也可以在其中使用一个STL类。它是可以被嵌入的：<br><br>class CParam<br>{<br>&nbsp;&nbsp;string name;<br>&nbsp;&nbsp;string unit;<br>&nbsp;&nbsp;vector &lt;double&gt; vecData;<br>};<br><br><br>或者将它作为一个基类：<br><br>class CParam : public vector &lt;double&gt;<br>{<br>&nbsp;&nbsp;string name;<br>&nbsp;&nbsp;string unit;<br>};<br><br><br>STL模版类作为基类时需要谨慎。这需要你适应这种编程方式。<br><br>模版中的模版<br><br>为构建一个复杂的数据结构，你可以将一个模板植入另一个模板中（即&#8220;模版嵌套&#8221;）。一般最好的方法是在程序前面使用typedef关键字来定义一个在另一个模板中使用的模版类型。<br><br>// 程序：在向量中嵌入向量的演示。<br>//目的：说明怎样使用嵌套的STL容器。<br><br>#include &lt;iostream&gt;<br>#include &lt;vector&gt;<br><br>using namespace std;<br><br>typedef vector &lt;int&gt; VEC_INT;<br><br>int inp[2][2] = {{1, 1}, {2, 0}};&nbsp;&nbsp;<br>&nbsp;&nbsp;// 要放入模板中的2x2的正则数组<br><br>int main(int argc, char* argv[])<br>{<br>&nbsp;&nbsp;int i, j;<br>&nbsp;&nbsp;vector &lt;VEC_INT&gt; vecvec;<br>&nbsp;&nbsp;// 如果你想用一句话实现这样的嵌套，你可以这样写：<br>&nbsp;&nbsp;// vector &lt;vector &lt;int&gt; &gt; vecvec;<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;// 将数组填入向量<br>&nbsp;&nbsp;VEC_INT v0(inp[0], inp[0]+2);&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;// 传递两个指针<br>&nbsp;&nbsp;&nbsp;&nbsp;// 将数组中的值拷贝到向量中<br>&nbsp;&nbsp;VEC_INT v1(inp[1], inp[1]+2);<br><br>&nbsp;&nbsp;vecvec.push_back(v0);<br>&nbsp;&nbsp;vecvec.push_back(v1);<br><br>&nbsp;&nbsp;for (i=0; i&lt;2; i++)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;for (j=0; j&lt;2; j++)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout &lt;&lt; vecvec[i][j] &lt;&lt; "&nbsp;&nbsp;";<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;cout &lt;&lt; endl;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;return 0;<br>}<br><br>// 输出：<br>// 1 1<br>// 2 0<br><br><br>虽然在初始化时很麻烦，一旦你将数据填如向量中，你就实现了一个变长的可扩充的二维数组（大小可扩充直到使用完内存）。根据实际需要，可以使用各种容器的嵌套组合。<br><br>总结<br><br>STL是有用的，但是使用过程中的困难和麻烦是再所难免的。就像中国人所说的：&#8220;如果你掌握了它，便犹如虎添翼。&#8221;<br><br><br>由于CSDN的文章编辑器可能将"&lt;"和"&gt;"之间的部分作为html标签过滤掉了，现贴出正确的&#8220;测试分数统计&#8221;程序如下： <br><br>//程序：测试分数统计 <br>//目的：通过对向量中保存的分数的操作说明怎样使用算法 <br><br>#include &lt;algorithm&gt; //如果要使用算法函数，你必须要包含这个头文件。 <br>#include &lt;numeric&gt; // 包含accumulate（求和）函数的头文件 <br>#include &lt;vector&gt; <br>#include &lt;iostream&gt; <br>using namespace std; <br><br>int testscore[] = {67, 56, 24, 78, 99, 87, 56}; <br><br>//判断一个成绩是否通过了考试 <br>bool passed_test(int n) <br>{ <br>return (n &gt;= 60); <br>} <br><br>// 判断一个成绩是否不及格 <br>bool failed_test(int n) <br>{ <br>return (n &lt; 60); <br>} <br><br>int main(int argc, char* argv[]) <br>{ <br>int total; <br>// 初始化向量，使之能够装入testscore数组中的元素 <br>vector &lt;int&gt; vecTestScore(testscore, <br>testscore + sizeof(testscore) / sizeof(int)); <br>vector &lt;int&gt;::iterator vi; <br><br>// 排序并显示向量中的数据 <br>sort(vecTestScore.begin(), vecTestScore.end()); <br>cout &lt;&lt; "Sorted Test Scores:" &lt;&lt; endl; <br>for (vi=vecTestScore.begin(); vi != vecTestScore.end(); vi++) <br>{ cout &lt;&lt; *vi &lt;&lt; ", "; } <br>cout &lt;&lt; endl; <br><br>// 显示统计信息 <br><br>// min_element 返回一个 _iterator_ 类型的对象，该对象指向值最小的那个元素。 <br>//&#8220;*&#8221;运算符提取元素中的值。 <br>vi = min_element(vecTestScore.begin(), vecTestScore.end()); <br>cout &lt;&lt; "The lowest score was " &lt;&lt; *vi &lt;&lt; "." &lt;&lt; endl; <br><br>//与min_element类似，max_element是选出最大值。 <br>vi = max_element(vecTestScore.begin(), vecTestScore.end()); <br>cout &lt;&lt; "The highest score was " &lt;&lt; *vi &lt;&lt; "." &lt;&lt; endl; <br><br>// 使用声明函数（predicate function，指vecTestScore.begin()和vecTestScore.end()）来确定通过考试的人数。 <br>cout &lt;&lt; count_if(vecTestScore.begin(), vecTestScore.end(), passed_test) &lt;&lt; <br>" out of " &lt;&lt; vecTestScore.size() &lt;&lt; <br>" students passed the test" &lt;&lt; endl; <br><br>// 确定有多少人考试挂了 <br>cout &lt;&lt; count_if(vecTestScore.begin(), <br>vecTestScore.end(), failed_test) &lt;&lt; <br>" out of " &lt;&lt; vecTestScore.size() &lt;&lt; <br>" students failed the test" &lt;&lt; endl; <br><br>//计算成绩总和 <br>total = accumulate(vecTestScore.begin(), <br>vecTestScore.end(), 0); <br>// 计算显示平均成绩 <br>cout &lt;&lt; "Average score was " &lt;&lt; <br>(total / (int)(vecTestScore.size())) &lt;&lt; endl; <br><br>return 0; <br>}<br></p>
<img src ="http://www.cppblog.com/xpzhou/aggbug/22341.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xpzhou/" target="_blank">榕树藤</a> 2007-04-20 08:18 <a href="http://www.cppblog.com/xpzhou/archive/2007/04/20/22341.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用VC6.0实现窗口的任意分割</title><link>http://www.cppblog.com/xpzhou/archive/2007/04/18/22244.html</link><dc:creator>榕树藤</dc:creator><author>榕树藤</author><pubDate>Wed, 18 Apr 2007 13:06:00 GMT</pubDate><guid>http://www.cppblog.com/xpzhou/archive/2007/04/18/22244.html</guid><wfw:comment>http://www.cppblog.com/xpzhou/comments/22244.html</wfw:comment><comments>http://www.cppblog.com/xpzhou/archive/2007/04/18/22244.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/xpzhou/comments/commentRss/22244.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xpzhou/services/trackbacks/22244.html</trackback:ping><description><![CDATA[<p><strong>一、关于CSplitterWnd类</strong><br>&nbsp;我们在使用CuteFtp或者NetAnt等工具的时候，一般都会被其复杂的界面所吸引，在这些界面中窗口被分割为若干的区域，真正做到了窗口的任意分割。 那么我们自己如何创建类似的界面，也实现窗口的任意的分割呢 ？在VC6.0中这就需要使用到CSplitterWnd类。CSplitterWnd看上去像是一种特殊的框架窗口，每个窗口都被相同的或者不同的视图所填充。当窗口被切分后用户可以使用鼠标移动切分条来调整窗口的相对尺寸。虽然VC6.0支持从AppWizard中创建分割窗口，但是自动加入的分割条总是不能让我们满意，因此我们还是通过手工增加代码来熟悉这个类。 <br>&nbsp;CSplitterWnd的构造函数主要包括下面三个。 <br><font color=#663333>BOOL Create(CWnd* pParentWnd,int nMaxRows,int nMaxCols,SIZE sizeMin,CCreateContext* pContext,DWORD dwStyle,UINT nID);</font> <br>&nbsp;&nbsp;&nbsp;&nbsp;功能描述：该函数用来创建动态切分窗口。<br>&nbsp;&nbsp;&nbsp;&nbsp;参数含义：pParentWnd 切分窗口的父框架窗口。<br>&nbsp;&nbsp;&nbsp;&nbsp;nMaxRows,nMaxCols是创建的最大的列数和行数。 <br>&nbsp;&nbsp;&nbsp;&nbsp;sizeMin是窗格的现实大小。 <br>&nbsp;&nbsp;&nbsp;&nbsp;pContext 大多数情况下传给父窗口。 <br>&nbsp;&nbsp;&nbsp;&nbsp;nID是字窗口的ID号. <br>&nbsp;&nbsp;<font color=#663333>BOOL CreateStatic(CWnd* pParentWnd,int nRows,int nCols,DWORD dwStyle,UINT nID) </font><br>&nbsp;&nbsp;功能描述：用来创建切分窗口。 <br>&nbsp;&nbsp;参数含义同上。 <br><font color=#663333>BOOL CreateView (int row,int col,CruntimeClass* pViewClass,SIZE sizeinit,CcreateContext* pContext);</font> <br>&nbsp;&nbsp;功能描述：为静态切分的窗口的网格填充视图。在将视图于切分窗口联系在一起的时候必 须先将切分窗口创建好。 <br>&nbsp;&nbsp;参数含义：同上。<br>&nbsp;&nbsp;从CSplitterWnd源程序可以看出不管是使用动态创建Create还是使用静态创建CreateStatic，在函数中都调用了一个保护函数CreateCommon，从下面的CreateCommon函数中的关键代码可以看出创建CSplitterWnd的实质是创建了一系列的MDI子窗口。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<font color=#663333>DWORD dwCreateStyle = dwStyle &amp; ~(WS_HSCROLL|WS_VSCROLL);<br>&nbsp;&nbsp;&nbsp;&nbsp;if (afxData.bWin4) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwCreateStyle &amp;= ~WS_BORDER; // create with the same wnd-class as MDI-Frame (no erase bkgnd) <br>&nbsp;&nbsp;&nbsp;&nbsp;if (!CreateEx(0, _afxWndMDIFrame, NULL, dwCreateStyle, 0, 0, 0, 0,pParentWnd-&gt;m_hWnd, (HMENU)nID, NULL)) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return FALSE; // create invisible </font><br><br><strong>二、创建嵌套分割窗口</strong> <br><strong>&nbsp;&nbsp;2.1创建动态分割窗口</strong><br>&nbsp;&nbsp;动态分割窗口使用Create方法。下面的代码将创建2x2的窗格。 <br>&nbsp;&nbsp;<font color=#663333>m_wndSplitter.Create(this,2,2,CSize(100,100),pContext);</font> <br>&nbsp;&nbsp;但是动态创建的分割窗口的窗格数目不能超过2x2，而且对于所有的窗格，都必须共享同一个视图，所受的限制也比较多，因此我们不将动态创建作为重点。我们的主要精力放在静态分割窗口的创建上。 <br>&nbsp;&nbsp;<strong>2.2创建静态分割窗口</strong><br>与动态创建相比，静态创建的代码要简单许多，而且可以最多创建16x16的窗格。不同的窗格我们可以使用CreateView填充不同的视图。 <br>在这里我们将创建CuteFtp的窗口分割。CuteFtp的分割情况如下：</p>
<div align=center>
<table height=223 cellSpacing=1 cellPadding=0 width=446 bgColor=#666666 border=0>
    <tbody>
        <tr bgColor=#eeeeee>
            <td colSpan=2 height=46>
            <div align=center><font face="Arial, Helvetica, sans-serif" size=2>CCuteFTPView</font></div>
            </td>
        </tr>
        <tr bgColor=#eeeeee>
            <td height=123>
            <div align=center><font face="Arial, Helvetica, sans-serif" size=2>CView2</font></div>
            </td>
            <td height=123>
            <div align=center><font face="Arial, Helvetica, sans-serif" size=2>CView3</font></div>
            </td>
        </tr>
        <tr bgColor=#eeeeee>
            <td colSpan=2 height=44>
            <div align=center><font face="Arial, Helvetica, sans-serif" size=2>CView4</font></div>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p>&nbsp;&nbsp;创建步骤： <br>&nbsp;&nbsp;▲ 在创建之前我们必须先用AppWizard生成单文档CuteFTP，生成的视类为 CCuteFTPView.同时在增加三个视类或者从视类继承而来的派生类CView2,CView3 CView4. <br>&nbsp;&nbsp;▲ <strong>增加成员：</strong> <br>&nbsp;&nbsp;在Cmainfrm.h中我们将增加下面的代码： <br>&nbsp;&nbsp;<font color=#663333>CSplitterWnd wndSplitter1;<br>&nbsp;&nbsp;CSplitterWnd wndSplitter2;</font><br>&nbsp;&nbsp;▲ <strong>重载CMainFrame::OnCreateClient()函数：</strong><br>&nbsp;&nbsp;<font color=#663333>BOOL CMainFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext) <br>&nbsp;&nbsp;{ //创建一个静态分栏窗口，分为三行一列 <br>&nbsp;&nbsp;&nbsp;&nbsp;if(m_wndSplitter1.CreateStatic(this,3,1)==NULL) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return FALSE;<br>&nbsp;&nbsp;&nbsp;&nbsp;//将CCuteFTPView连接到0行0列窗格上<br>&nbsp;&nbsp;&nbsp;&nbsp;m_wndSplitter1.CreateView(0,0,RUNTIME_CLASS(CCuteFTPView),CSize(100,100), pContext); <br>&nbsp;&nbsp;&nbsp;&nbsp;m_wndSplitter1.CreateView(2,0,RUNTIME_CLASS(CView4),CSize(100,100),pContext); //将CView4连接到0行2列<br>&nbsp;&nbsp;&nbsp;&nbsp;if(m_wndSplitter2.CreateStatic(&amp;m_wndSplitter,1,2,WS_CHILD|WS_VISIBLE, m_wndSplitter.IdFromRowCol(1, 0))==NULL) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return FALSE; //将第1行0列再分开1行2列 <br>&nbsp;&nbsp;&nbsp;&nbsp;//将CView2类连接到第二个分栏对象的0行0列<br>&nbsp;&nbsp;&nbsp;&nbsp;m_wndSplitter2.CreateView(0,0,RUNTIME_CLASS(CView2),CSize(400,300),pContext); //将CView3类连接到第二个分栏对象的0行1列<br>&nbsp;&nbsp;&nbsp;&nbsp;m_wndSplitter2.CreateView(0,1,RUNTIME_CLASS(CView3),CSize(400,300),pContext); <br>&nbsp;&nbsp;&nbsp;&nbsp;return TRUE; <br>&nbsp;&nbsp;} </font><br><strong>2.3实现各个分割区域的通信</strong> <br>■<strong>有文档相连的视图之间的通信<br></strong>由AppWizard生成的CCuteFTPView是与文档相连的，同时我们也让CView2与文档相连，因此我们需要修改CCuteFTPApp的InitInstance()函数，我们将增加下面的部分。<br><font color=#663333>AddDocTemplate (new CMultiDocTemplate(IDR_VIEW2TYPE, <br>RUNTIME_CLASS(CMainDoc), <br>RUNTIME_CLASS(CMDIChildWnd), <br>RUNTIME_CLASS(CView2))); </font><br>我们现在来实现CCuteFTPView与CView2之间的通信。由于跟文档类相连的视图类 是不能安全的与除文档类之外的其余的视图类通信的。因此我们只能让他们都与文档 类通信。在文档中我们设置相应的指针以用来获的各个视图。我们重载 CCuteFTPView::OnOpenDocument()函数； <br><font color=#663333>CCuteFTPView* pCuteFTPView;<br>CView2* pView2;<br>POSITION pos;<br>CView* pView;<br>while(pos!=NULL)<br>{<br>&nbsp;&nbsp;pView=GetNextView(pos); <br>&nbsp;&nbsp;if(pView-&gt;IsKindOf(RUNTIME_CLASS(CCuteFTPView))==NULL) <br>&nbsp;&nbsp;&nbsp;&nbsp;pCuteFTPView=(CCuteFTPView*)pView; <br>&nbsp;&nbsp;else(pView-&gt;IsKindOf(RUNTIME_CLASS(CCuteFTPView))==NULL) <br>&nbsp;&nbsp;&nbsp;&nbsp;pView2=(CView2*)pView; <br>} </font><br>这样我们在文档类中就获的了跟它相连的所有的视图的指针。<br>如果需要在 CCuteFTPView中调用CView2中的一个方法DoIt()则代码如下： <br><font color=#663333>CCuteFTPDoc* pDoc=GetDocument();<br>CView2* pView2=pDoc-&gt;pView3;<br>pView3.DoIt(); </font><br>■<strong>无文档视图与文档关联视图之间的通信<br></strong>CView3和CView4都是不与文档相关联的。我们现在实现CView3与CView2的通信.正如前面所说，CView2只能安全的与CCuteFTPDoc通信，因此，CView3如果需要跟CView2通信，也必须借助于文档类。因此程序的关键是如何在CView3中获得文档的指针。视图类中没有这样的类成员可以用来直接访问文档类。但是我们知道在主窗口类MainFrame中我们可以获得程序的任意窗口类的指针。因此我们只要获得程序主窗口了的指针，就可以解决问题了。代码实现在CView3中访问CView2中的DoIt()方法。<br><br>CView3中的代码如下： <br><br><font color=#663333>CMainFrame* MainFrame=(CMainFrame*)this-&gt;GetParent()-&gt;GetParent(); <br>CCuteFTPDoc* Doc=(CCuteFTPDoc*)MainFrame-&gt;GetActiveDocument();<br>if(Doc!=NULL) Doc-&gt;DoIt(); <br><br>CCuteFTPDoc中的相应的处理函数DoIt()代码如下： <br><br>CView2* pView2; <br>POSITION pos; <br>CView* pView; <br>while(pos!=NULL) <br>{ <br>&nbsp;&nbsp;pView=GetNextView(pos);<br>&nbsp;&nbsp;if(pView-&gt;IsKindOf(RUNTIME_CLASS(CView2))==NULL) <br>&nbsp;&nbsp;pView2=(CView2*)pView; <br>} <br>pView2-&gt;DoIt(); </font><br>■<strong>无文档关联视图之间的通信<br></strong>CView3和CView4都是不跟文档相连的，如何实现他们之间的通信呢。 正如我们在上面所说的那样，由于在主框架中我们可以访问任意的视图，因此我们的主要任 务还是在程序中获得主框架的指针。在CView3中访问CView4中的方法DoIt()。 <br><font color=#663333>CMainFrame* MainFrame=(CMainFrame*)this-&gt;GetParent()-&gt;GetParent(); <br>CView4* View4=(CView4*)MainFrame-&gt;m_wndSplitter1.GetPane(2,0); <br>View4-&gt;DoIt(); </font><br><br>到现在我们已经实现了CuteFTP的主窗口的框架并且能够实现他们之间相互通信的框架。 同样的我们可以实现其他的一些流行界面例如NetAnts，Foxmail的分割。 <br><br><strong>三、关于对话框的分割</strong> <br>到目前为止，只有基于文档/视图的程序才能使用CSplitterWnd，而基于对话框的应用程序却不支持CSplitterWnd,但是如果我们在继承类中重载一些虚拟方法，也能使CSplitterWnd 在对话框程序中使用。从MFC的源程序WinSplit.cpp中可以看出，为了获得父窗口的地方程序都调用了虚拟方法GetParentFrame(),因此如果在对话框中使用，我们必须将它改为GetParent();因此我们将CSplitterWnd的下面几个方法重载。<br>&nbsp;&nbsp;<font color=#663333>virtual void StartTracking(int ht); <br>&nbsp;&nbsp;virtual CWnd* GetActivePane(int* pRow = NULL, int* pCol = NULL); <br>&nbsp;&nbsp;virtual void SetActivePane( int row, int col, CWnd* pWnd = NULL ); <br>&nbsp;&nbsp;virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); <br>&nbsp;&nbsp;virtual BOOL OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult ); <br>&nbsp;&nbsp;virtual BOOL OnWndMsg( UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult ); </font><br>具体实现如下，实现中我将给出原有代码的主要部分以及修改后的代码以作对比。<br>在cpp文件中加入下面的枚举类型。 <br><font color=#663333>enum HitTestValue <br>{ <br>&nbsp;&nbsp;noHit = 0,//表示没有选中任何对象<br>&nbsp;&nbsp;vSplitterBox = 1,<br>&nbsp;&nbsp;hSplitterBox = 2,<br>&nbsp;&nbsp;bothSplitterBox = 3,<br>&nbsp;&nbsp;vSplitterBar1 = 101,//代表各个方向的水平分割条<br>&nbsp;&nbsp;vSplitterBar15 = 115,<br>&nbsp;&nbsp;hSplitterBar1 = 201,//代表垂直方向的各个分割条<br>&nbsp;&nbsp;hSplitterBar15 = 215,<br>&nbsp;&nbsp;splitterIntersection1 = 301,//代表各个交叉点<br>&nbsp;&nbsp;splitterIntersection225 = 525<br>};<br><br>CWnd* CxSplitterWnd::GetActivePane(int* pRow, int* pCol)<br>{<br>&nbsp;&nbsp;ASSERT_VALID(this); <br>&nbsp;&nbsp;//获得当前的获得焦点的窗口<br>&nbsp;&nbsp;//下面注释粗体的是原有的代码的主要部分。<br>&nbsp;&nbsp;// CWnd* pView = NULL;<br>&nbsp;&nbsp;//CFrameWnd* pFrameWnd = GetParentFrame();<br>&nbsp;&nbsp;//ASSERT_VALID(pFrameWnd);<br>&nbsp;&nbsp;//pView = pFrameWnd-&gt;GetActiveView();<br>&nbsp;&nbsp;//if (pView == NULL)<br>&nbsp;&nbsp;// pView = GetFocus();<br>&nbsp;&nbsp;CWnd* pView = GetFocus();<br>&nbsp;&nbsp;if (pView != NULL &amp;&amp; !IsChildPane(pView, pRow, pCol))<br>&nbsp;&nbsp;&nbsp;&nbsp;pView = NULL;<br>&nbsp;&nbsp;return pView; <br>} <br><br>void CxSplitterWnd::SetActivePane( int row, int col, CWnd* pWnd) <br>{<br>&nbsp;&nbsp;CWnd* pPane = pWnd == NULL ? GetPane(row, col) : pWnd; <br>&nbsp;&nbsp;//下面加注释粗体的是原有代码的主要部分。<br>&nbsp;&nbsp;//FrameWnd* pFrameWnd = GetParentFrame();<br>&nbsp;&nbsp;//ASSERT_VALID(pFrameWnd); <br>&nbsp;&nbsp;//pFrameWnd-&gt;SetActiveView((CView*)pPane); <br>&nbsp;&nbsp;pPane-&gt;SetFocus();//修改后的语句 <br>}<br><br>void CxSplitterWnd::StartTracking(int ht)<br>{<br>&nbsp;&nbsp;ASSERT_VALID(this); <br>&nbsp;&nbsp;if (ht == noHit) <br>&nbsp;&nbsp;&nbsp;&nbsp;return;<br>&nbsp;&nbsp;// GetHitRect will restrict 'm_rectLimit' as appropriate <br>&nbsp;&nbsp;GetInsideRect(m_rectLimit);<br>&nbsp;&nbsp;if (ht &gt;= splitterIntersection1 &amp;&amp; ht &lt;= splitterIntersection225) <br>&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;// split two directions (two tracking rectangles) <br>&nbsp;&nbsp;&nbsp;&nbsp;int row = (ht - splitterIntersection1) / 15; <br>&nbsp;&nbsp;&nbsp;&nbsp;int col = (ht - splitterIntersection1) % 15; <br>&nbsp;&nbsp;&nbsp;&nbsp;GetHitRect(row + vSplitterBar1, m_rectTracker); <br>&nbsp;&nbsp;&nbsp;&nbsp;int yTrackOffset = m_ptTrackOffset.y; <br>&nbsp;&nbsp;&nbsp;&nbsp;m_bTracking2 = TRUE; <br>&nbsp;&nbsp;&nbsp;&nbsp;GetHitRect(col + hSplitterBar1, m_rectTracker2); <br>&nbsp;&nbsp;&nbsp;&nbsp;m_ptTrackOffset.y = yTrackOffset; <br>&nbsp;&nbsp;} <br>&nbsp;&nbsp;else if (ht == bothSplitterBox) <br>&nbsp;&nbsp;{ <br>&nbsp;&nbsp;// hit on splitter boxes (for keyboard) <br>&nbsp;&nbsp;GetHitRect(vSplitterBox, m_rectTracker); <br>&nbsp;&nbsp;int yTrackOffset = m_ptTrackOffset.y; <br>&nbsp;&nbsp;m_bTracking2 = TRUE; <br>&nbsp;&nbsp;GetHitRect(hSplitterBox, m_rectTracker2); <br>&nbsp;&nbsp;m_ptTrackOffset.y = yTrackOffset; // center it <br>&nbsp;&nbsp;m_rectTracker.OffsetRect(0, m_rectLimit.Height()/2); m_rectTracker2.OffsetRect(m_rectLimit.Width()/2, 0); <br>&nbsp;&nbsp;} <br>&nbsp;&nbsp;else<br>&nbsp;&nbsp;{ <br>&nbsp;&nbsp;// only hit one bar <br>&nbsp;&nbsp;GetHitRect(ht, m_rectTracker); <br>&nbsp;&nbsp;} <br><br>//下面加注释的将从程序中删去。 <br>//CView* pView = (CView*)GetActivePane(); <br>//if (pView != NULL &amp;&amp; pView-&gt;IsKindOf(RUNTIME_CLASS(CView))) <br>//{ <br>// ASSERT_VALID(pView); <br>// CFrameWnd* pFrameWnd = GetParentFrame(); <br>//ASSERT_VALID(pFrameWnd); <br>//pView-&gt;OnActivateFrame(WA_INACTIVE, pFrameWnd); <br>// } <br>// steal focus and capture<br>&nbsp;&nbsp;SetCapture();<br>&nbsp;&nbsp;SetFocus();<br>&nbsp;&nbsp;// make sure no updates are pending <br>&nbsp;&nbsp;RedrawWindow(NULL, NULL, RDW_ALLCHILDREN | RDW_UPDATENOW); <br>&nbsp;&nbsp;// set tracking state and appropriate cursor<br>&nbsp;&nbsp;m_bTracking = TRUE;<br>&nbsp;&nbsp;OnInvertTracker(m_rectTracker); <br>&nbsp;&nbsp;if (m_bTracking2) <br>&nbsp;&nbsp;&nbsp;&nbsp;OnInvertTracker(m_rectTracker2); <br>&nbsp;&nbsp;m_htTrack = ht; <br>&nbsp;&nbsp;SetSplitCursor(ht); <br>&nbsp;&nbsp;}<br><br>BOOL CxSplitterWnd::OnCommand(WPARAM wParam, LPARAM lParam) <br>{ <br>&nbsp;&nbsp;if (CWnd::OnCommand(wParam, lParam)) <br>&nbsp;&nbsp;&nbsp;&nbsp;return TRUE; <br>&nbsp;&nbsp;//下面粗体的是原程序的语句 <br>//<strong>return GetParentFrame()-&gt;SendMessage(WM_COMMAND, wParam, lParam); </strong><br>&nbsp;&nbsp;return GetParent()-&gt;SendMessage(WM_COMMAND, wParam, lParam); <br>}<br>BOOL CxSplitterWnd::OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult )<br>{<br>&nbsp;&nbsp;if (CWnd::OnNotify(wParam, lParam, pResult)) <br>&nbsp;&nbsp;&nbsp;&nbsp;return TRUE; <br>&nbsp;&nbsp;//下面粗体的是源程序的语句<br>&nbsp;&nbsp;//<strong>*pResult = GetParentFrame()-&gt;SendMessage(WM_NOTIFY, wParam, lParam);</strong><br>&nbsp;&nbsp;*pResult = GetParent()-&gt;SendMessage(WM_NOTIFY, wParam, lParam);<br>&nbsp;&nbsp;return TRUE;<br>} <br><br>BOOL CxSplitterWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) <br>{ <br>&nbsp;&nbsp;// The code line below is necessary if using CxSplitterWnd in a regular dll <br>&nbsp;&nbsp;// AFX_MANAGE_STATE(AfxGetStaticModuleState());<br>&nbsp;&nbsp;return CWnd::OnWndMsg(message, wParam, lParam, pResult); <br>} </font><br>这样我们就可以在对话框中使用CxSplitterWnd类了。 <br><br><strong>四、CSplitterWnd的扩展</strong> <br>CSplitterWnd扩展话题是很多的，我们可以通过对原有方法的覆盖或者增加新的方法来扩展CSplitterWnd。我们在此仅举两个方面的例子。 <br><strong>4.1锁定切分条</strong><br>当用户创建好分割窗口后，有时并不希望通过拖动切分条来调节窗口的大小。这时就必须锁定切分条。锁定切分条的最简单的方法莫过于不让CSplitterWnd来处理WM_LBUTTONDOWN,WM_MOUSEMOVE,WM_SETCURSOR消息，而是将这些消息交给CWnd窗口进行处理，从而屏蔽掉这些消息。拿WM_LBUTTONDOWN处理过程来说。修改为如下： <br><font color=#663333>void CXXSplitterWnd::OnLButtonDown(UINT nFlags,CPoint point) <br>{ <br>&nbsp;&nbsp;CWnd::OnLButtonDown(nFlags,point);<br>} </font><br>其余的处理方法类似。 <br><strong>4.2切分条的定制</strong> <br>由Window自己生成的切分条总是固定的，没有任何的变化，我们在使用一些软件比如ACDSee的时候却能发现它们的切分条却是和自动生成的切分条不一样的。那么如何定制自己的切分条呢？通过重载CSplitterWnd的虚方法OnDrawSplitter和OnInvertTracker可以达到这样的目的。下面的代码生成的效果是分割窗口的边界颜色为红色，分割条的颜色为绿色.代码如下：<br><font color=#663333>void CSplitterWndEx::OnDrawSplitter(CDC *pDC, ESplitType nType, const CRect &amp;rectArg)<br>{<br>&nbsp;&nbsp;if(pDC==NULL) <br>&nbsp;&nbsp;{ <br>&nbsp;&nbsp;RedrawWindow(rectArg,NULL,RDW_INVALIDATE|RDW_NOCHILDREN);<br>&nbsp;&nbsp;return;<br>&nbsp;&nbsp;} <br>&nbsp;&nbsp;ASSERT_VALID(pDC);<br>&nbsp;&nbsp;CRect rc=rectArg;<br>&nbsp;&nbsp;switch(nType) <br>&nbsp;&nbsp;{ <br>&nbsp;&nbsp;case splitBorder:<br>&nbsp;&nbsp;//重画分割窗口边界,使之为红色 <br>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0));<br>&nbsp;&nbsp;&nbsp;&nbsp;rc.InflateRect(-CX_BORDER,-CY_BORDER); <br>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0)); <br>&nbsp;&nbsp;&nbsp;&nbsp;return; <br>&nbsp;&nbsp;case splitBox:<br>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));<br>&nbsp;&nbsp;&nbsp;&nbsp;rc.InflateRect(-CX_BORDER,-CY_BORDER); <br>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));<br>&nbsp;&nbsp;&nbsp;&nbsp;rc.InflateRect(-CX_BORDER,-CY_BORDER);<br>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;FillSolidRect(rc,RGB(0,0,0)); <br>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));<br>&nbsp;&nbsp;&nbsp;&nbsp;return; <br>&nbsp;&nbsp;case splitBar: <br>&nbsp;&nbsp;//重画分割条，使之为绿色 <br>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;FillSolidRect(rc,RGB(255,255,255));<br>&nbsp;&nbsp;&nbsp;&nbsp;rc.InflateRect(-5,-5); <br>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0)); <br>&nbsp;&nbsp;&nbsp;&nbsp;return; <br>&nbsp;&nbsp;default: <br>&nbsp;&nbsp;&nbsp;&nbsp;ASSERT(FALSE); <br>&nbsp;&nbsp;} <br>&nbsp;&nbsp;pDC-&gt;FillSolidRect(rc,RGB(0,0,255));<br>} <br>void CSplitterWndEx::OnInvertTracker(CRect &amp;rect) <br>{ <br>&nbsp;&nbsp;ASSERT_VALID(this);<br>&nbsp;&nbsp;ASSERT(!rect.IsRectEmpty()); <br>&nbsp;&nbsp;ASSERT((GetStyle()&amp;WS_CLIPCHILDREN)==0);<br>&nbsp;&nbsp;CRect rc=rect; <br>&nbsp;&nbsp;rc.InflateRect(2,2);<br>&nbsp;&nbsp;CDC* pDC=GetDC(); <br>&nbsp;&nbsp;CBrush* pBrush=CDC::GetHalftoneBrush();<br>&nbsp;&nbsp;HBRUSH hOldBrush=NULL;<br>&nbsp;&nbsp;if(pBrush!=NULL) hOldBrush=(HBRUSH)SelectObject(pDC-&gt;m_hDC,pBrush-&gt;m_hObject);<br>&nbsp;&nbsp;pDC-&gt;PatBlt(rc.left,rc.top,rc.Width(),rc.Height(),BLACKNESS); <br>&nbsp;&nbsp;if(hOldBrush!=NULL) <br>&nbsp;&nbsp;SelectObject(pDC-&gt;m_hDC,hOldBrush);<br>&nbsp;&nbsp;ReleaseDC(pDC); <br>} </font><br>同样我们只要继承CSplitterWnd中的其余的一些虚拟方法就可以生成具有自己个性的分割窗口了。<br></p>
<img src ="http://www.cppblog.com/xpzhou/aggbug/22244.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xpzhou/" target="_blank">榕树藤</a> 2007-04-18 21:06 <a href="http://www.cppblog.com/xpzhou/archive/2007/04/18/22244.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>vc自定义消息的发送与接收的方法实现</title><link>http://www.cppblog.com/xpzhou/archive/2007/04/18/22185.html</link><dc:creator>榕树藤</dc:creator><author>榕树藤</author><pubDate>Wed, 18 Apr 2007 00:52:00 GMT</pubDate><guid>http://www.cppblog.com/xpzhou/archive/2007/04/18/22185.html</guid><wfw:comment>http://www.cppblog.com/xpzhou/comments/22185.html</wfw:comment><comments>http://www.cppblog.com/xpzhou/archive/2007/04/18/22185.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xpzhou/comments/commentRss/22185.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xpzhou/services/trackbacks/22185.html</trackback:ping><description><![CDATA[<div>
<p><span style="FONT-WEIGHT: bold">以下用一个自创的对话框类(MyMessageDlg)向视图类(MessageTestView)<br>发送自定义消息为例，说明这两种不同方法的自定义消息的</span></p>
<p><span style="COLOR: rgb(255,0,0)">消息传递的方法一：使用ON_MESSAGE<br></span>使用ON_MESSAGE响应消息，必须配合定义消息#define WM_MY_MESSAGE (WM_USER+100)</p>
<p>对于<span style="COLOR: rgb(0,0,255)">发送消息者</span>-MyMessageDlg，<br>在其MyMessageDlg.h中，定义#define WM_MY_MESSAGE (WM_USER+100)<br>在其MyMessageDlg.cpp中要先添加：＃i nclude "MainFrm.h"<br>因为使用了CMainFrame*定义对象。<br>并且要有测试消息的函数：<br>void MyMessageDlg::OnButtonMsg()<br>{<br>&nbsp;&nbsp;&nbsp; // TODO: Add your control notification handler code here<br>&nbsp;&nbsp;&nbsp; CMainFrame* pMF=(CMainFrame*)AfxGetApp()-&gt;m_pMainWnd;&nbsp; //先通过获取当前框架指针<br>&nbsp;&nbsp;&nbsp; CView * active = pMF-&gt;GetActiveView();//才能获取当前视类指针<br>&nbsp;&nbsp;&nbsp; if(active != NULL)&nbsp; //获取了当前视类指针才能发送消息<br>&nbsp;&nbsp;&nbsp; active-&gt;PostMessage(WM_MY_MESSAGE,0,0);&nbsp;&nbsp; //使用PostMessage发送消息<br>}</p>
<p>对于<span style="COLOR: rgb(51,0,255)">消息的接受者</span>-MessageTestView，<br>在其MessageTestView.h中，也要定义#define WM_MY_MESSAGE (WM_USER+100)<br>并定义消息映射函数-OnMyMessage()<br>protected:<br>&nbsp;//{{AFX_MSG(CMessageTestView)<br>&nbsp;afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);<br>&nbsp;//}}AFX_MSG<br>&nbsp;DECLARE_MESSAGE_MAP()<br>在其MessageTestView.cpp中，<br>先要声明响应消息：<br>BEGIN_MESSAGE_MAP(CMessageTestView, CEditView)<br>&nbsp;//{{AFX_MSG_MAP(CMessageTestView)<br>&nbsp;ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage)<br>&nbsp;//}}AFX_MSG_MAP<br>再添加消息响应的函数实现：<br>LRESULT CMessageTestView::OnMyMessage(WPARAM wParam, LPARAM lParam)<br>{<br>&nbsp;MessageBox("OnMyMessage!");<br>&nbsp;return 0;<br>}</p>
<p><br><span style="COLOR: rgb(255,0,0)">消息传递的方法二：使用ON_REGISTERED_MESSAGE<br></span>使用ON_REGISTERED_MESSAGE注册消息，必须配合<br>static UINT WM_MY_MESSAGE=RegisterWindowMessage("Message");</p>
<p>对于<span style="COLOR: rgb(213,43,111)">消息的发送者</span>-MyMessageDlg，<br>在其MyMessageDlg.h中，只要<br>定义static UINT WM_MY_MESSAGE=RegisterWindowMessage("Message");<br>就可以了。<br>在其MyMessageDlg.cpp中要先添加：＃i nclude "MainFrm.h"<br>因为使用了CMainFrame*定义对象。<br>并且要有测试消息的函数：<br>void MyMessageDlg::OnButtonMsg()<br>{<br>&nbsp;&nbsp;&nbsp; // TODO: Add your control notification handler code here<br>&nbsp;&nbsp;&nbsp; CMainFrame* pMF=(CMainFrame*)AfxGetApp()-&gt;m_pMainWnd;&nbsp; //先通过获取当前框架指针<br>&nbsp;&nbsp;&nbsp; CView * active = pMF-&gt;GetActiveView();//才能获取当前视类指针<br>&nbsp;&nbsp;&nbsp; if(active != NULL)&nbsp; //获取了当前视类指针才能发送消息<br>&nbsp;&nbsp;&nbsp; active-&gt;PostMessage(WM_MY_MESSAGE,0,0);&nbsp;&nbsp; //使用PostMessage发送消息<br>}</p>
<p>对于<span style="COLOR: rgb(204,51,112)">消息的接收者</span>-MessageTestView，<br>在其MessageTestView.h中不要定义<br>static UINT WM_MY_MESSAGE=RegisterWindowMessage("Message");<br>应该把这个定义放到MessageTestView.cpp中，要不会出现: redefinition<br>在其MessageTestView.h中只要定义消息映射函数<br>protected:<br>&nbsp;//{{AFX_MSG(CMessageTestView)<br>&nbsp;afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);<br>&nbsp;//}}AFX_MSG<br>&nbsp;DECLARE_MESSAGE_MAP()<br>在其MessageTestView.cpp中,先定义<br>static UINT WM_MY_MESSAGE=RegisterWindowMessage("Message");<br>接着注册消息：<br>BEGIN_MESSAGE_MAP(CMessageTestView, CEditView)<br>&nbsp;//{{AFX_MSG_MAP(CMessageTestView)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ON_REGISTERED_MESSAGE(WM_MY_MESSAGE,OnMyMessage)<br>&nbsp;//}}AFX_MSG_MAP<br>最后添加消息响应的函数实现：<br>LRESULT CMessageTestView::OnMyMessage(WPARAM wParam, LPARAM lParam)<br>{<br>&nbsp;MessageBox("OnMyMessage!");<br>&nbsp;return 0;<br>}<br>----------------------------------------------------------------<br>比较两种方法，只是略有不同。但也要小心谨慎，以免出现接收不到消息的情况。</p>
<p>-------------------------------------------------------------------</p>
<p>其他注意事项：</p>
<p>发送消息的-MyMessageDlg.cpp前也要定义<br>static UINT WM_MY_MESSAGE=RegisterWindowMessage("Message");</p>
<p>接受消息的-MessageTestView.cpp前也要定义<br>static UINT WM_MY_MESSAGE=RegisterWindowMessage("Message");</p>
<p>RegisterWindowMessage("Message")中""的内容是什么不重要，写什么都可以，但是<br>发送者与接受者必须是一样的内容，例如："Message"</p>
</div>
<img src ="http://www.cppblog.com/xpzhou/aggbug/22185.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xpzhou/" target="_blank">榕树藤</a> 2007-04-18 08:52 <a href="http://www.cppblog.com/xpzhou/archive/2007/04/18/22185.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>