﻿<?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++博客-一路向北-文章分类-C++</title><link>http://www.cppblog.com/deane/category/5615.html</link><description>                    追逐梦想，永不停步......</description><language>zh-cn</language><lastBuildDate>Thu, 20 Jan 2011 22:37:29 GMT</lastBuildDate><pubDate>Thu, 20 Jan 2011 22:37:29 GMT</pubDate><ttl>60</ttl><item><title>C++ const成员函数</title><link>http://www.cppblog.com/deane/articles/138956.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Thu, 20 Jan 2011 03:16:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/138956.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/138956.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/138956.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/138956.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/138956.html</trackback:ping><description><![CDATA[<p>一些成员函数改变对象，一些成员函数不改变对象。<br>例如：<br>int Point::GetY()<br>{<br>　return yVal;<br>}<br>　　这个函数被调用时，不改变Point对象，而下面的函数改变Point对象：<br>void Point:: SetPt (int x, int y)<br>{<br>　xVal=x;<br>　yVal=y;<br>}<br>　　</p>
<p>为了使成员函数的意义更加清楚，我们可在不改变对象的成员函数的函数原型中加上const说明：<br>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp; class Point<br>{<br>　public:<br>　　int GetX() const;<br>　　int GetY() const;<br>　　void SetPt (int, int);<br>　　void OffsetPt (int, int);<br>　private:<br>　　int xVal, yVal;<br>};<br>&nbsp;</p>
<p>　　const成员函数应该在函数原型说明和函数定义中都增加const限定：<br>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp; int Point::GetY() const<br>{<br>　return yVal;<br>}<br>class Set {<br>public:<br>　Set (void){ card = 0; }<br>　bool Member(const int) const;<br>　void AddElem(const int);<br>　//...<br>};<br>bool Set::Member (const int elem) const<br>{<br>　//...<br>}</p>
<p>&nbsp;</p>
<p>　　非常量成员函数不能被常量成员对象调用，因为它可能企图修改常量的数据成员：<br>　　const Set s;<br>　　s.AddElem(10); // 非法: AddElem不是常量成员函数<br>　　s.Member(10); // 正确<br>　　但构造函数和析构函数对这个规则例外，它们从不定义为常量成员，但可被常量对象调用（被自动调用）。它们也能给常量的数据成员赋值，除非数据成员本身是常量。</p>
<p>&nbsp;<br>为什么需要const成员函数？<br>　　我们定义的类的成员函数中，常常有一些成员函数不改变类的数据成员，也就是说，这些函数是"只读"函数，而有一些函数要修改类数据成员的值。如果把不改变数据成员的函数都加上const关键字进行标识，显然，可提高程序的可读性。其实，它还能提高程序的可靠性，已定义成const的成员函数，一旦企图修改数据成员的值，则编译器按错误处理。</p>
<p><br>const成员函数和const对象<br>　　实际上，const成员函数还有另外一项作用，即常量对象相关。对于内置的数据类型，我们可以定义它们的常量，用户自定义的类也一样，可以定义它们的常量对象。例如，定义一个整型常量的方法为：<br>　　const int i=1 ；<br>同样，也可以定义常量对象，假定有一个类classA，定义该类的常量对象的方法为：<br>　　const classA a(2)；<br>　　这里，a是类classA的一个const对象，"2"传给它的构造函数参数。const对象的数据成员在对象寿命期内不能改变。但是，如何保证该类的数据成员不被改变呢？<br>　　为了确保const对象的数据成员不会被改变，在C++中，const对象只能调用const成员函数。如果一个成员函数实际上没有对数据成员作任何形式的修改，但是它没有被const关键字限定的，也不能被常量对象调用。下面通过一个例子来说明这个问题：<br>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp; class C<br>{<br>　int X;<br>public:<br>　int GetX()<br>　{<br>　　return X;<br>　}<br>　void SetX(int X)<br>　{<br>　　this-&gt;X = X;<br>　}<br>};<br>void main()<br>{<br>　const C constC;<br>　cout&lt;&lt;constC.GetX();<br>}</p>
<p>&nbsp;</p>
<p>　　如果我们编译上面的程序代码，编译器会出现错误提示：constC是个常量对象，它只能调用const成员函数。虽然GetX( )函数实际上并没有改变数据成员X，由于没有const关键字限定，所以仍旧不能被constC对象调用。如果我们将上述加粗的代码：<br>　　int GetX()<br>改写成：<br>　　int GetX()const<br>再重新编译，就没有问题了。<br>const成员函数的使用<br>　　const成员函数表示该成员函数只能读类数据成员，而不能修改类成员数据。定义const成员函数时，把const关键字放在函数的参数表和函数体之间。有人可能会问：为什么不将const放在函数声明前呢？因为这样做意味着函数的返回值是常量，意义完全不同。下面是定义const成员函数的一个实例：<br>　　class X<br>　　{<br>　　　int i;<br>　　　public:<br>　　　int f() const;<br>　　};<br>　　关键字const必须用同样的方式重复出现在函数实现里，否则编译器会把它看成一个不同的函数：<br>　　int X::f() const<br>　　{<br>　　　return i;<br>　　}<br>　　如果f( )试图用任何方式改变i或调用另一个非const成员函数，编译器将给出错误信息。任何不修改成员数据的函数都应该声明为const函数，这样有助于提高程序的可读性和可靠性。</p>
<p>&nbsp; 对象.成员函数</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 对象&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 成员函数&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对/错<br>1、&nbsp; const&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对<br>2、&nbsp; const&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; non-const&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 错<br>3、&nbsp; non-const&nbsp;&nbsp;&nbsp;&nbsp; const&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对<br>4、&nbsp; not-const&nbsp;&nbsp;&nbsp;&nbsp; non-const&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 成员函数调用成员函数</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 成员函数&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 成员函数&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对/错<br>5、&nbsp; const&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对<br>6、&nbsp; const&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; non-const&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 错<br>7、&nbsp; non-const&nbsp;&nbsp;&nbsp;&nbsp; const&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对<br>8、&nbsp; non-const&nbsp;&nbsp;&nbsp;&nbsp; non-const&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/deane/aggbug/138956.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2011-01-20 11:16 <a href="http://www.cppblog.com/deane/articles/138956.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>调用规范与可变参数表</title><link>http://www.cppblog.com/deane/articles/137756.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Thu, 30 Dec 2010 10:38:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/137756.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/137756.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/137756.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/137756.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/137756.html</trackback:ping><description><![CDATA[<p>&nbsp;</p>
<p>作者：阿半</p>
<p>　　语言调用规范是指进行一次函数调用所采用的传递参数的方法，返回值的处理以及调用堆栈的清理。Microsoft C/C++ 语言中采用了五种调用规范，分别是__cdecl, __stdcall, __fastcall，thiscall和nake每一中调用规范都是利用eax作为返回值，如果函数返回值是64位的，则利用edx:eax对来返回值。Nake调用规范非常的灵活，足以独立的一篇文章描述，这里就不再描述nake调用规范。下表列出了前面四种规范调用的特点：<br>　 关键字 堆栈清理者 参数传递顺序 <br>__cdecl 调用者 从右至左 <br>__stdcall 被调用者 从右至左 <br>__fastcall 被调用者 从右至左，前两个参数由寄存器ecx，edx传递 <br>thiscall 被调用者或者调用者 从右至左 </p>
<p><br>　　__cdecl 最大好处在于由于是调用者清理栈，它可以处理可变参数，缺点则在于它增加了程序的大小，因为在每个调用返回的时候，需要多执行一条清理栈的指令。<br>　　__stdcall 是在windows程序设计中出现的最多的调用规则，所有的不可变参数的API调用都使用这个规则。<br>　　__fastcall 在windows内核设计中被广泛的使用，由于两个参数由寄存器直接传递，采用这种规则的函数效率要比以上两种规则高。<br>　　thiscall是C++成员函数的默认调用规范，编译期间，这种调用会根据函数是否支持可变参数表来决定采用什么方式清理堆栈。如果成员函数不支持可变参数，那么它就是用参数入栈，ecx保存this指针的方式进行调用，如果成员函数支持可变参数，那么它的调用和__cdecl类似，唯一不同的是将this指针最后压入栈中进行传递。<br>　　调用者和被调用者必须采用同样的规则才能保证程序的正常执行，曾经看到很多程序员犯的错误就是由于调用规范的不一样，致使程序异常，比如：</p>
<p>DWORD ThreadFunc(LPVOID lpParam)<br>{<br>//&#8230;<br>}</p>
<p>CreateThread(..,(LPTHREAD_START_ROUTINE)ThreadFunc, &#8230;);　　如果在编译期间没有指定编译选项/Gz（指定未指明调用规范的函数采用__stdcall方式），那么编译器自动将ThreadFunc处理成__cdecl调用规范（/Gd），这样可能在线程开始的时候正常执行，然而退出的时候由于堆栈没有正常清理，造成访问违例或者非法指令错误。<br>　　以上说了很多清理栈的问题，那么为什么清理栈很重要呢。堆栈是线程相关的，也就是说每一个线程含有一个堆栈，这个堆栈上保存了局部变量，调用返回地址等很多线程相关的数据，这也是为什么独立运行的线程可以调用同样一个函数而互不干扰的原因。堆栈的特点恐怕大家已经非常熟悉了，那么根据上面的每一种调用，我给出一个简单的图示来说明清理堆栈的重要性，以及为什么上面的例子代码会出错。</p>
<p><br>图一　这是线程堆栈在运行的时候的样子</p>
<p>　　调用前和后esp的差值中间包含了函数参数表，返回地址这样的重要信息，举个简单的调用例子．假设有某个函数定义是这样的：</p>
<p>Int __cdecl func（void* p）；再假设esp调用函数前的数值为0x1234，那么在进入这个函数体内看到的堆栈是这样的：</p>
<p>122C 1230 1234<br>Next p 这里的next指调用函数后的下一条指令的位置。调用函数的汇编码：</p>
<p>Push p<br>Call func<br>Add esp，4 《--注意这里，由于是cdecl调用，需要调用者清栈。而一个__stdcall调用的汇编码：</p>
<p>Push p<br>Call func　　这里没有了add esp，4这个指令，因为在func函数返回的时候自己将esp已经复原了。再来看刚才举的错误的例子，由于强制转换的作用，线程开始函数被设置成了stdcall调用，而实际的线程函数被编译后，并没有执行堆栈的清理工作，线程函数返回的时候，由于堆栈的不正确，当然会发生错误。修改这个bug的方法只要在线程函数的定义前把__cdecl改成_stdcall即可。<br>　　有了上面的例子做基础来理解可变参数表就简单的多了，由于各种调用规范的限定，致使只有__cdecl调用规范可以采用可变参数表。先来看看可变参数表的定义（可以参考sdk目录下src\crt\varargs.h）：</p>
<p>typedef char *va_list;<br>#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) &amp; ~(sizeof(int) - 1) )<br>#define va_dcl va_list va_alist;<br>#define va_start(ap) ap = (va_list)&amp;va_alist<br>#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )<br>#define va_end(ap) ap = (va_list)0　　va_list居然被定义成char* ？没错，这实际是用来定义了一个指针，指针的sizeof()就是操作系统可访问的地址空间的大小，也就是CPU相关的字长。_INTSIZEOF宏很简单，就是用来将数据以n的数据大小对齐。va_start宏有点模糊，可是如果你看懂了上面的堆栈数据结构，那么显然它就是获得最后一个固定参数的地址，也就是堆栈上的地址，va_arg先使得ap指向下一个参数，然后取得当前参数的值（注意，这个值正是堆栈上的值），va_end使得取参数过程结束。<br>　　这几个宏完成的动作很简单了，实际就是取得可变参数表在堆栈上的起始位置，然后根据参数类型，依次从堆栈上取出每一个参数。<br>　　本文简单的介绍了微软C/C++支持的调用类型，结合实例描述了规范的实际应用，最后根据CRT提供的源代码分析了可变参数表的实现。</p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="file:///E:/library/VC++%206_0%20">file:///E:/library/VC++%206_0%20</a>中如何使用%20CRT%20调试功能来检测内存泄漏（转）%20-%20bairny的专栏%20-%20CSDN博客.mht</p>
<img src ="http://www.cppblog.com/deane/aggbug/137756.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2010-12-30 18:38 <a href="http://www.cppblog.com/deane/articles/137756.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>堆和栈的区别</title><link>http://www.cppblog.com/deane/articles/137757.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Thu, 30 Dec 2010 10:38:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/137757.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/137757.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/137757.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/137757.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/137757.html</trackback:ping><description><![CDATA[<p>非本人作也!因非常经典,所以收归旗下,与众人阅之!原作者不祥!</p>
<div class=postbody>堆和栈的区别<br>一、预备知识—程序的内存分配<br>一个由c/C++编译的程序占用的内存分为以下几个部分<br>1、栈区（stack）—&nbsp;由编译器自动分配释放&nbsp;，存放函数的参数值，局部变量的值等。其操作方式类似于数据结构中的栈。<br>2、堆区（heap）&nbsp;—&nbsp;一般由程序员分配释放，&nbsp;若程序员不释放，程序结束时可能由OS回收&nbsp;。注意它与数据结构中的堆是两回事，分配方式倒是类似于链表，呵呵。<br>3、全局区（静态区）（static）—，全局变量和静态变量的存储是放在一块的，初始化的全局变量和静态变量在一块区域，&nbsp;未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。&nbsp;-&nbsp;程序结束后有系统释放&nbsp;<br>4、文字常量区—常量字符串就是放在这里的。&nbsp;程序结束后由系统释放<br>5、程序代码区—存放函数体的二进制代码。<br>二、例子程序&nbsp;<br>这是一个前辈写的，非常详细&nbsp;<br>//main.cpp&nbsp;<br>int&nbsp;a&nbsp;=&nbsp;0;&nbsp;全局初始化区&nbsp;<br>char&nbsp;*p1;&nbsp;全局未初始化区&nbsp;<br>main()&nbsp;<br>{&nbsp;<br>int&nbsp;b;&nbsp;栈&nbsp;<br>char&nbsp;s[]&nbsp;=&nbsp;"abc";&nbsp;栈&nbsp;<br>char&nbsp;*p2;&nbsp;栈&nbsp;<br>char&nbsp;*p3&nbsp;=&nbsp;"123456";&nbsp;123456\0在常量区，p3在栈上。&nbsp;<br>static&nbsp;int&nbsp;c&nbsp;=0；&nbsp;全局（静态）初始化区&nbsp;<br>p1&nbsp;=&nbsp;(char&nbsp;*)malloc(10);&nbsp;<br>p2&nbsp;=&nbsp;(char&nbsp;*)malloc(20);&nbsp;<br>分配得来得10和20字节的区域就在堆区。&nbsp;<br>strcpy(p1,&nbsp;"123456");&nbsp;123456\0放在常量区，编译器可能会将它与p3所指向的"123456"优化成一个地方。&nbsp;<br>}&nbsp;
<p>&nbsp;</p>
<p><br>二、堆和栈的理论知识&nbsp;<br>2.1申请方式&nbsp;<br>stack:&nbsp;<br>由系统自动分配。&nbsp;例如，声明在函数中一个局部变量&nbsp;int&nbsp;b;&nbsp;系统自动在栈中为b开辟空间&nbsp;<br>heap:&nbsp;<br>需要程序员自己申请，并指明大小，在c中malloc函数&nbsp;<br>如p1&nbsp;=&nbsp;(char&nbsp;*)malloc(10);&nbsp;<br>在C++中用new运算符&nbsp;<br>如p2&nbsp;=&nbsp;(char&nbsp;*)malloc(10);&nbsp;<br>但是注意p1、p2本身是在栈中的。&nbsp;</p>
<p><br>2.2&nbsp;<br>申请后系统的响应&nbsp;<br>栈：只要栈的剩余空间大于所申请空间，系统将为程序提供内存，否则将报异常提示栈溢出。&nbsp;<br>堆：首先应该知道操作系统有一个记录空闲内存地址的链表，当系统收到程序的申请时，&nbsp;<br>会遍历该链表，寻找第一个空间大于所申请空间的堆结点，然后将该结点从空闲结点链表中删除，并将该结点的空间分配给程序，另外，对于大多数系统，会在这块内存空间中的首地址处记录本次分配的大小，这样，代码中的delete语句才能正确的释放本内存空间。另外，由于找到的堆结点的大小不一定正好等于申请的大小，系统会自动的将多余的那部分重新放入空闲链表中。&nbsp;</p>
<p>2.3申请大小的限制&nbsp;<br>栈：在Windows下,栈是向低地址扩展的数据结构，是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的，在WINDOWS下，栈的大小是2M（也有的说是1M，总之是一个编译时就确定的常数），如果申请的空间超过栈的剩余空间时，将提示overflow。因此，能从栈获得的空间较小。&nbsp;<br>堆：堆是向高地址扩展的数据结构，是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的，自然是不连续的，而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见，堆获得的空间比较灵活，也比较大。&nbsp;</p>
<p><br>2.4申请效率的比较：&nbsp;<br>栈由系统自动分配，速度较快。但程序员是无法控制的。&nbsp;<br>堆是由new分配的内存，一般速度比较慢，而且容易产生内存碎片,不过用起来最方便.&nbsp;<br>另外，在WINDOWS下，最好的方式是用VirtualAlloc分配内存，他不是在堆，也不是在栈是直接在进程的地址空间中保留一快内存，虽然用起来最不方便。但是速度快，也最灵活。&nbsp;</p>
<p>2.5堆和栈中的存储内容&nbsp;<br>栈：&nbsp;在函数调用时，第一个进栈的是主函数中后的下一条指令（函数调用语句的下一条可执行语句）的地址，然后是函数的各个参数，在大多数的C编译器中，参数是由右往左入栈的，然后是函数中的局部变量。注意静态变量是不入栈的。&nbsp;<br>当本次函数调用结束后，局部变量先出栈，然后是参数，最后栈顶指针指向最开始存的地址，也就是主函数中的下一条指令，程序由该点继续运行。&nbsp;<br>堆：一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。&nbsp;</p>
<p>2.6存取效率的比较&nbsp;</p>
<p>char&nbsp;s1[]&nbsp;=&nbsp;"aaaaaaaaaaaaaaa";&nbsp;<br>char&nbsp;*s2&nbsp;=&nbsp;"bbbbbbbbbbbbbbbbb";&nbsp;<br>aaaaaaaaaaa是在运行时刻赋值的；&nbsp;<br>而bbbbbbbbbbb是在编译时就确定的；&nbsp;<br>但是，在以后的存取中，在栈上的数组比指针所指向的字符串(例如堆)快。&nbsp;<br>比如：&nbsp;<br>#include&nbsp;<br>void&nbsp;main()&nbsp;<br>{&nbsp;<br>char&nbsp;a&nbsp;=&nbsp;1;&nbsp;<br>char&nbsp;c[]&nbsp;=&nbsp;"1234567890";&nbsp;<br>char&nbsp;*p&nbsp;="1234567890";&nbsp;<br>a&nbsp;=&nbsp;c[1];&nbsp;<br>a&nbsp;=&nbsp;p[1];&nbsp;<br>return;&nbsp;<br>}&nbsp;<br>对应的汇编代码&nbsp;<br>10:&nbsp;a&nbsp;=&nbsp;c[1];&nbsp;<br>00401067&nbsp;8A&nbsp;4D&nbsp;F1&nbsp;mov&nbsp;cl,byte&nbsp;ptr&nbsp;[ebp-0Fh]&nbsp;<br>0040106A&nbsp;88&nbsp;4D&nbsp;FC&nbsp;mov&nbsp;byte&nbsp;ptr&nbsp;[ebp-4],cl&nbsp;<br>11:&nbsp;a&nbsp;=&nbsp;p[1];&nbsp;<br>0040106D&nbsp;8B&nbsp;55&nbsp;EC&nbsp;mov&nbsp;edx,dword&nbsp;ptr&nbsp;[ebp-14h]&nbsp;<br>00401070&nbsp;8A&nbsp;42&nbsp;01&nbsp;mov&nbsp;al,byte&nbsp;ptr&nbsp;[edx+1]&nbsp;<br>00401073&nbsp;88&nbsp;45&nbsp;FC&nbsp;mov&nbsp;byte&nbsp;ptr&nbsp;[ebp-4],al&nbsp;<br>第一种在读取时直接就把字符串中的元素读到寄存器cl中，而第二种则要先把指针值读到edx中，在根据edx读取字符，显然慢了。&nbsp;</p>
<p><br>2.7小结：&nbsp;<br>堆和栈的区别可以用如下的比喻来看出：&nbsp;<br>使用栈就象我们去饭馆里吃饭，只管点菜（发出申请）、付钱、和吃（使用），吃饱了就走，不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作，他的好处是快捷，但是自由度小。&nbsp;<br>使用堆就象是自己动手做喜欢吃的菜肴，比较麻烦，但是比较符合自己的口味，而且自由度大。&nbsp;<br><br><br><br></p>
<p>windows进程中的内存结构</p>
<p><br>在阅读本文之前，如果你连堆栈是什么多不知道的话，请先阅读文章后面的基础知识。&nbsp;</p>
<p>接触过编程的人都知道，高级语言都能通过变量名来访问内存中的数据。那么这些变量在内存中是如何存放的呢？程序又是如何使用这些变量的呢？下面就会对此进行深入的讨论。下文中的C语言代码如没有特别声明，默认都使用VC编译的release版。&nbsp;</p>
<p>首先，来了解一下&nbsp;C&nbsp;语言的变量是如何在内存分部的。C&nbsp;语言有全局变量(Global)、本地变量(Local)，静态变量(Static)、寄存器变量(Regeister)。每种变量都有不同的分配方式。先来看下面这段代码：&nbsp;</p>
<p>#include&nbsp;&lt;stdio.h&gt;&nbsp;</p>
<p>int&nbsp;g1=0,&nbsp;g2=0,&nbsp;g3=0;&nbsp;</p>
<p>int&nbsp;main()&nbsp;<br>{&nbsp;<br>static&nbsp;int&nbsp;s1=0,&nbsp;s2=0,&nbsp;s3=0;&nbsp;<br>int&nbsp;v1=0,&nbsp;v2=0,&nbsp;v3=0;&nbsp;</p>
<p>//打印出各个变量的内存地址&nbsp;</p>
<p>printf("0x%08x\n",&amp;v1);&nbsp;//打印各本地变量的内存地址&nbsp;<br>printf("0x%08x\n",&amp;v2);&nbsp;<br>printf("0x%08x\n\n",&amp;v3);&nbsp;<br>printf("0x%08x\n",&amp;g1);&nbsp;//打印各全局变量的内存地址&nbsp;<br>printf("0x%08x\n",&amp;g2);&nbsp;<br>printf("0x%08x\n\n",&amp;g3);&nbsp;<br>printf("0x%08x\n",&amp;s1);&nbsp;//打印各静态变量的内存地址&nbsp;<br>printf("0x%08x\n",&amp;s2);&nbsp;<br>printf("0x%08x\n\n",&amp;s3);&nbsp;<br>return&nbsp;0;&nbsp;<br>}&nbsp;</p>
<p>编译后的执行结果是：&nbsp;</p>
<p>0x0012ff78&nbsp;<br>0x0012ff7c&nbsp;<br>0x0012ff80&nbsp;</p>
<p>0x004068d0&nbsp;<br>0x004068d4&nbsp;<br>0x004068d8&nbsp;</p>
<p>0x004068dc&nbsp;<br>0x004068e0&nbsp;<br>0x004068e4&nbsp;</p>
<p>输出的结果就是变量的内存地址。其中v1,v2,v3是本地变量，g1,g2,g3是全局变量，s1,s2,s3是静态变量。你可以看到这些变量在内存是连续分布的，但是本地变量和全局变量分配的内存地址差了十万八千里，而全局变量和静态变量分配的内存是连续的。这是因为本地变量和全局/静态变量是分配在不同类型的内存区域中的结果。对于一个进程的内存空间而言，可以在逻辑上分成3个部份：代码区，静态数据区和动态数据区。动态数据区一般就是&#8220;堆栈&#8221;。&#8220;栈(stack)&#8221;和&#8220;堆(heap)&#8221;是两种不同的动态数据区，栈是一种线性结构，堆是一种链式结构。进程的每个线程都有私有的&#8220;栈&#8221;，所以每个线程虽然代码一样，但本地变量的数据都是互不干扰。一个堆栈可以通过&#8220;基地址&#8221;和&#8220;栈顶&#8221;地址来描述。全局变量和静态变量分配在静态数据区，本地变量分配在动态数据区，即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。&nbsp;</p>
<p><br>├———————┤低端内存区域&nbsp;<br>│&nbsp;&#8230;&#8230;&nbsp;│&nbsp;<br>├———————┤&nbsp;<br>│&nbsp;动态数据区&nbsp;│&nbsp;<br>├———————┤&nbsp;<br>│&nbsp;&#8230;&#8230;&nbsp;│&nbsp;<br>├———————┤&nbsp;<br>│&nbsp;代码区&nbsp;│&nbsp;<br>├———————┤&nbsp;<br>│&nbsp;静态数据区&nbsp;│&nbsp;<br>├———————┤&nbsp;<br>│&nbsp;&#8230;&#8230;&nbsp;│&nbsp;<br>├———————┤高端内存区域&nbsp;</p>
<p><br>堆栈是一个先进后出的数据结构，栈顶地址总是小于等于栈的基地址。我们可以先了解一下函数调用的过程，以便对堆栈在程序中的作用有更深入的了解。不同的语言有不同的函数调用规定，这些因素有参数的压入规则和堆栈的平衡。windows&nbsp;API的调用规则和ANSI&nbsp;C的函数调用规则是不一样的，前者由被调函数调整堆栈，后者由调用者调整堆栈。两者通过&#8220;__stdcall&#8221;和&#8220;__cdecl&#8221;前缀区分。先看下面这段代码：&nbsp;</p>
<p>#include&nbsp;&lt;stdio.h&gt;&nbsp;</p>
<p>void&nbsp;__stdcall&nbsp;func(int&nbsp;param1,int&nbsp;param2,int&nbsp;param3)&nbsp;<br>{&nbsp;<br>int&nbsp;var1=param1;&nbsp;<br>int&nbsp;var2=param2;&nbsp;<br>int&nbsp;var3=param3;&nbsp;<br>printf("0x%08x\n",&#182;m1);&nbsp;//打印出各个变量的内存地址&nbsp;<br>printf("0x%08x\n",&#182;m2);&nbsp;<br>printf("0x%08x\n\n",&#182;m3);&nbsp;<br>printf("0x%08x\n",&amp;var1);&nbsp;<br>printf("0x%08x\n",&amp;var2);&nbsp;<br>printf("0x%08x\n\n",&amp;var3);&nbsp;<br>return;&nbsp;<br>}&nbsp;</p>
<p>int&nbsp;main()&nbsp;<br>{&nbsp;<br>func(1,2,3);&nbsp;<br>return&nbsp;0;&nbsp;<br>}&nbsp;</p>
<p>编译后的执行结果是：&nbsp;</p>
<p>0x0012ff78&nbsp;<br>0x0012ff7c&nbsp;<br>0x0012ff80&nbsp;</p>
<p>0x0012ff68&nbsp;<br>0x0012ff6c&nbsp;<br>0x0012ff70&nbsp;</p>
<p><br>├———————┤&lt;—函数执行时的栈顶（ESP）、低端内存区域&nbsp;<br>│&nbsp;&#8230;&#8230;&nbsp;│&nbsp;<br>├———————┤&nbsp;<br>│&nbsp;var&nbsp;1&nbsp;│&nbsp;<br>├———————┤&nbsp;<br>│&nbsp;var&nbsp;2&nbsp;│&nbsp;<br>├———————┤&nbsp;<br>│&nbsp;var&nbsp;3&nbsp;│&nbsp;<br>├———————┤&nbsp;<br>│&nbsp;RET&nbsp;│&nbsp;<br>├———————┤&lt;—&#8220;__cdecl&#8221;函数返回后的栈顶（ESP）&nbsp;<br>│&nbsp;parameter&nbsp;1&nbsp;│&nbsp;<br>├———————┤&nbsp;<br>│&nbsp;parameter&nbsp;2&nbsp;│&nbsp;<br>├———————┤&nbsp;<br>│&nbsp;parameter&nbsp;3&nbsp;│&nbsp;<br>├———————┤&lt;—&#8220;__stdcall&#8221;函数返回后的栈顶（ESP）&nbsp;<br>│&nbsp;&#8230;&#8230;&nbsp;│&nbsp;<br>├———————┤&lt;—栈底（基地址&nbsp;EBP）、高端内存区域&nbsp;</p>
<p><br>上图就是函数调用过程中堆栈的样子了。首先，三个参数以从又到左的次序压入堆栈，先压&#8220;param3&#8221;，再压&#8220;param2&#8221;，最后压入&#8220;param1&#8221;；然后压入函数的返回地址(RET)，接着跳转到函数地址接着执行（这里要补充一点，介绍UNIX下的缓冲溢出原理的文章中都提到在压入RET后，继续压入当前EBP，然后用当前ESP代替EBP。然而，有一篇介绍windows下函数调用的文章中说，在windows下的函数调用也有这一步骤，但根据我的实际调试，并未发现这一步，这还可以从param3和var1之间只有4字节的间隙这点看出来）；第三步，将栈顶(ESP)减去一个数，为本地变量分配内存空间，上例中是减去12字节(ESP=ESP-3*4，每个int变量占用4个字节)；接着就初始化本地变量的内存空间。由于&#8220;__stdcall&#8221;调用由被调函数调整堆栈，所以在函数返回前要恢复堆栈，先回收本地变量占用的内存(ESP=ESP+3*4)，然后取出返回地址，填入EIP寄存器，回收先前压入参数占用的内存(ESP=ESP+3*4)，继续执行调用者的代码。参见下列汇编代码：&nbsp;</p>
<p>;--------------func&nbsp;函数的汇编代码-------------------&nbsp;</p>
<p>:00401000&nbsp;83EC0C&nbsp;sub&nbsp;esp,&nbsp;0000000C&nbsp;//创建本地变量的内存空间&nbsp;<br>:00401003&nbsp;8B442410&nbsp;mov&nbsp;eax,&nbsp;dword&nbsp;ptr&nbsp;[esp+10]&nbsp;<br>:00401007&nbsp;8B4C2414&nbsp;mov&nbsp;ecx,&nbsp;dword&nbsp;ptr&nbsp;[esp+14]&nbsp;<br>:0040100B&nbsp;8B542418&nbsp;mov&nbsp;edx,&nbsp;dword&nbsp;ptr&nbsp;[esp+18]&nbsp;<br>:0040100F&nbsp;89442400&nbsp;mov&nbsp;dword&nbsp;ptr&nbsp;[esp],&nbsp;eax&nbsp;<br>:00401013&nbsp;8D442410&nbsp;lea&nbsp;eax,&nbsp;dword&nbsp;ptr&nbsp;[esp+10]&nbsp;<br>:00401017&nbsp;894C2404&nbsp;mov&nbsp;dword&nbsp;ptr&nbsp;[esp+04],&nbsp;ecx&nbsp;</p>
<p>&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;（省略若干代码）&nbsp;</p>
<p>:00401075&nbsp;83C43C&nbsp;add&nbsp;esp,&nbsp;0000003C&nbsp;;恢复堆栈，回收本地变量的内存空间&nbsp;<br>:00401078&nbsp;C3&nbsp;ret&nbsp;000C&nbsp;;函数返回，恢复参数占用的内存空间&nbsp;<br>;如果是&#8220;__cdecl&#8221;的话，这里是&#8220;ret&#8221;，堆栈将由调用者恢复&nbsp;</p>
<p>;-------------------函数结束-------------------------&nbsp;</p>
<p><br>;--------------主程序调用func函数的代码--------------&nbsp;</p>
<p>:00401080&nbsp;6A03&nbsp;push&nbsp;00000003&nbsp;//压入参数param3&nbsp;<br>:00401082&nbsp;6A02&nbsp;push&nbsp;00000002&nbsp;//压入参数param2&nbsp;<br>:00401084&nbsp;6A01&nbsp;push&nbsp;00000001&nbsp;//压入参数param1&nbsp;<br>:00401086&nbsp;E875FFFFFF&nbsp;call&nbsp;00401000&nbsp;//调用func函数&nbsp;<br>;如果是&#8220;__cdecl&#8221;的话，将在这里恢复堆栈，&#8220;add&nbsp;esp,&nbsp;0000000C&#8221;&nbsp;</p>
<p>聪明的读者看到这里，差不多就明白缓冲溢出的原理了。先来看下面的代码：&nbsp;</p>
<p>#include&nbsp;&lt;stdio.h&gt;&nbsp;<br>#include&nbsp;&lt;string.h&gt;&nbsp;</p>
<p>void&nbsp;__stdcall&nbsp;func()&nbsp;<br>{&nbsp;<br>char&nbsp;lpBuff[8]="\0";&nbsp;<br>strcat(lpBuff,"AAAAAAAAAAA");&nbsp;<br>return;&nbsp;<br>}&nbsp;</p>
<p>int&nbsp;main()&nbsp;<br>{&nbsp;<br>func();&nbsp;<br>return&nbsp;0;&nbsp;<br>}&nbsp;</p>
<p>编译后执行一下回怎么样？哈，&#8220;"0x00414141"指令引用的"0x00000000"内存。该内存不能为"read"。&#8221;，&#8220;非法操作&#8221;喽！"41"就是"A"的16进制的ASCII码了，那明显就是strcat这句出的问题了。"lpBuff"的大小只有8字节，算进结尾的\0，那strcat最多只能写入7个"A"，但程序实际写入了11个"A"外加1个\0。再来看看上面那幅图，多出来的4个字节正好覆盖了RET的所在的内存空间，导致函数返回到一个错误的内存地址，执行了错误的指令。如果能精心构造这个字符串，使它分成三部分，前一部份仅仅是填充的无意义数据以达到溢出的目的，接着是一个覆盖RET的数据，紧接着是一段shellcode，那只要着个RET地址能指向这段shellcode的第一个指令，那函数返回时就能执行shellcode了。但是软件的不同版本和不同的运行环境都可能影响这段shellcode在内存中的位置，那么要构造这个RET是十分困难的。一般都在RET和shellcode之间填充大量的NOP指令，使得exploit有更强的通用性。&nbsp;</p>
<p><br>├———————┤&lt;—低端内存区域&nbsp;<br>│&nbsp;&#8230;&#8230;&nbsp;│&nbsp;<br>├———————┤&lt;—由exploit填入数据的开始&nbsp;<br>│&nbsp;│&nbsp;<br>│&nbsp;buffer&nbsp;│&lt;—填入无用的数据&nbsp;<br>│&nbsp;│&nbsp;<br>├———————┤&nbsp;<br>│&nbsp;RET&nbsp;│&lt;—指向shellcode，或NOP指令的范围&nbsp;<br>├———————┤&nbsp;<br>│&nbsp;NOP&nbsp;│&nbsp;<br>│&nbsp;&#8230;&#8230;&nbsp;│&lt;—填入的NOP指令，是RET可指向的范围&nbsp;<br>│&nbsp;NOP&nbsp;│&nbsp;<br>├———————┤&nbsp;<br>│&nbsp;│&nbsp;<br>│&nbsp;shellcode&nbsp;│&nbsp;<br>│&nbsp;│&nbsp;<br>├———————┤&lt;—由exploit填入数据的结束&nbsp;<br>│&nbsp;&#8230;&#8230;&nbsp;│&nbsp;<br>├———————┤&lt;—高端内存区域&nbsp;</p>
<p><br>windows下的动态数据除了可存放在栈中，还可以存放在堆中。了解C++的朋友都知道，C++可以使用new关键字来动态分配内存。来看下面的C++代码：&nbsp;</p>
<p>#include&nbsp;&lt;stdio.h&gt;&nbsp;<br>#include&nbsp;&lt;iostream.h&gt;&nbsp;<br>#include&nbsp;&lt;windows.h&gt;&nbsp;</p>
<p>void&nbsp;func()&nbsp;<br>{&nbsp;<br>char&nbsp;*buffer=new&nbsp;char[128];&nbsp;<br>char&nbsp;bufflocal[128];&nbsp;<br>static&nbsp;char&nbsp;buffstatic[128];&nbsp;<br>printf("0x%08x\n",buffer);&nbsp;//打印堆中变量的内存地址&nbsp;<br>printf("0x%08x\n",bufflocal);&nbsp;//打印本地变量的内存地址&nbsp;<br>printf("0x%08x\n",buffstatic);&nbsp;//打印静态变量的内存地址&nbsp;<br>}&nbsp;</p>
<p>void&nbsp;main()&nbsp;<br>{&nbsp;<br>func();&nbsp;<br>return;&nbsp;<br>}&nbsp;</p>
<p>程序执行结果为：&nbsp;</p>
<p>0x004107d0&nbsp;<br>0x0012ff04&nbsp;<br>0x004068c0&nbsp;</p>
<p>可以发现用new关键字分配的内存即不在栈中，也不在静态数据区。VC编译器是通过windows下的&#8220;堆(heap)&#8221;来实现new关键字的内存动态分配。在讲&#8220;堆&#8221;之前，先来了解一下和&#8220;堆&#8221;有关的几个API函数：&nbsp;</p>
<p>HeapAlloc&nbsp;在堆中申请内存空间&nbsp;<br>HeapCreate&nbsp;创建一个新的堆对象&nbsp;<br>HeapDestroy&nbsp;销毁一个堆对象&nbsp;<br>HeapFree&nbsp;释放申请的内存&nbsp;<br>HeapWalk&nbsp;枚举堆对象的所有内存块&nbsp;<br>GetProcessHeap&nbsp;取得进程的默认堆对象&nbsp;<br>GetProcessHeaps&nbsp;取得进程所有的堆对象&nbsp;<br>LocalAlloc&nbsp;<br>GlobalAlloc&nbsp;</p>
<p>当进程初始化时，系统会自动为进程创建一个默认堆，这个堆默认所占内存的大小为1M。堆对象由系统进行管理，它在内存中以链式结构存在。通过下面的代码可以通过堆动态申请内存空间：&nbsp;</p>
<p>HANDLE&nbsp;hHeap=GetProcessHeap();&nbsp;<br>char&nbsp;*buff=HeapAlloc(hHeap,0,8);&nbsp;</p>
<p>其中hHeap是堆对象的句柄，buff是指向申请的内存空间的地址。那这个hHeap究竟是什么呢？它的值有什么意义吗？看看下面这段代码吧：&nbsp;</p>
<p>#pragma&nbsp;comment(linker,"/entry:main")&nbsp;//定义程序的入口&nbsp;<br>#include&nbsp;&lt;windows.h&gt;&nbsp;</p>
<p>_CRTIMP&nbsp;int&nbsp;(__cdecl&nbsp;*printf)(const&nbsp;char&nbsp;*,&nbsp;...);&nbsp;//定义STL函数printf&nbsp;<br>/*---------------------------------------------------------------------------&nbsp;<br>写到这里，我们顺便来复习一下前面所讲的知识：&nbsp;<br>(*注)printf函数是C语言的标准函数库中函数，VC的标准函数库由msvcrt.dll模块实现。&nbsp;<br>由函数定义可见，printf的参数个数是可变的，函数内部无法预先知道调用者压入的参数个数，函数只能通过分析第一个参数字符串的格式来获得压入参数的信息，由于这里参数的个数是动态的，所以必须由调用者来平衡堆栈，这里便使用了__cdecl调用规则。BTW，Windows系统的API函数基本上是__stdcall调用形式，只有一个API例外，那就是wsprintf，它使用__cdecl调用规则，同printf函数一样，这是由于它的参数个数是可变的缘故。&nbsp;<br>---------------------------------------------------------------------------*/&nbsp;<br>void&nbsp;main()&nbsp;<br>{&nbsp;<br>HANDLE&nbsp;hHeap=GetProcessHeap();&nbsp;<br>char&nbsp;*buff=HeapAlloc(hHeap,0,0x10);&nbsp;<br>char&nbsp;*buff2=HeapAlloc(hHeap,0,0x10);&nbsp;<br>HMODULE&nbsp;hMsvcrt=LoadLibrary("msvcrt.dll");&nbsp;<br>printf=(void&nbsp;*)GetProcAddress(hMsvcrt,"printf");&nbsp;<br>printf("0x%08x\n",hHeap);&nbsp;<br>printf("0x%08x\n",buff);&nbsp;<br>printf("0x%08x\n\n",buff2);&nbsp;<br>}&nbsp;</p>
<p>执行结果为：&nbsp;</p>
<p>0x00130000&nbsp;<br>0x00133100&nbsp;<br>0x00133118&nbsp;</p>
<p>hHeap的值怎么和那个buff的值那么接近呢？其实hHeap这个句柄就是指向HEAP首部的地址。在进程的用户区存着一个叫PEB(进程环境块)的结构，这个结构中存放着一些有关进程的重要信息，其中在PEB首地址偏移0x18处存放的ProcessHeap就是进程默认堆的地址，而偏移0x90处存放了指向进程所有堆的地址列表的指针。windows有很多API都使用进程的默认堆来存放动态数据，如windows&nbsp;2000下的所有ANSI版本的函数都是在默认堆中申请内存来转换ANSI字符串到Unicode字符串的。对一个堆的访问是顺序进行的，同一时刻只能有一个线程访问堆中的数据，当多个线程同时有访问要求时，只能排队等待，这样便造成程序执行效率下降。&nbsp;</p>
<p>最后来说说内存中的数据对齐。所位数据对齐，是指数据所在的内存地址必须是该数据长度的整数倍，DWORD数据的内存起始地址能被4除尽，WORD数据的内存起始地址能被2除尽，x86&nbsp;CPU能直接访问对齐的数据，当他试图访问一个未对齐的数据时，会在内部进行一系列的调整，这些调整对于程序来说是透明的，但是会降低运行速度，所以编译器在编译程序时会尽量保证数据对齐。同样一段代码，我们来看看用VC、Dev-C++和lcc三个不同编译器编译出来的程序的执行结果：&nbsp;</p>
<p>#include&nbsp;&lt;stdio.h&gt;&nbsp;</p>
<p>int&nbsp;main()&nbsp;<br>{&nbsp;<br>int&nbsp;a;&nbsp;<br>char&nbsp;b;&nbsp;<br>int&nbsp;c;&nbsp;<br>printf("0x%08x\n",&amp;a);&nbsp;<br>printf("0x%08x\n",&amp;b);&nbsp;<br>printf("0x%08x\n",&amp;c);&nbsp;<br>return&nbsp;0;&nbsp;<br>}&nbsp;</p>
<p>这是用VC编译后的执行结果：&nbsp;<br>0x0012ff7c&nbsp;<br>0x0012ff7b&nbsp;<br>0x0012ff80&nbsp;<br>变量在内存中的顺序：b(1字节)-a(4字节)-c(4字节)。&nbsp;</p>
<p>这是用Dev-C++编译后的执行结果：&nbsp;<br>0x0022ff7c&nbsp;<br>0x0022ff7b&nbsp;<br>0x0022ff74&nbsp;<br>变量在内存中的顺序：c(4字节)-中间相隔3字节-b(占1字节)-a(4字节)。&nbsp;</p>
<p>这是用lcc编译后的执行结果：&nbsp;<br>0x0012ff6c&nbsp;<br>0x0012ff6b&nbsp;<br>0x0012ff64&nbsp;<br>变量在内存中的顺序：同上。&nbsp;</p>
<p>三个编译器都做到了数据对齐，但是后两个编译器显然没VC&#8220;聪明&#8221;，让一个char占了4字节，浪费内存哦。&nbsp;</p>
<p><br>基础知识：&nbsp;<br>堆栈是一种简单的数据结构，是一种只允许在其一端进行插入或删除的线性表。允许插入或删除操作的一端称为栈顶，另一端称为栈底，对堆栈的插入和删除操作被称为入栈和出栈。有一组CPU指令可以实现对进程的内存实现堆栈访问。其中，POP指令实现出栈操作，PUSH指令实现入栈操作。CPU的ESP寄存器存放当前线程的栈顶指针，EBP寄存器中保存当前线程的栈底指针。CPU的EIP寄存器存放下一个CPU指令存放的内存地址，当CPU执行完当前的指令后，从EIP寄存器中读取下一条指令的内存地址，然后继续执行。&nbsp;</p>
<p><br>参考：《Windows下的HEAP溢出及其利用》by:&nbsp;isno&nbsp;<br>《windows核心编程》by:&nbsp;Jeffrey&nbsp;Richter&nbsp;<br><br><br><br></p>
<p>摘要：&nbsp;讨论常见的堆性能问题以及如何防范它们。（共&nbsp;9&nbsp;页）</p>
<p>前言<br>您是否是动态分配的&nbsp;C/C++&nbsp;对象忠实且幸运的用户？您是否在模块间的往返通信中频繁地使用了&#8220;自动化&#8221;？您的程序是否因堆分配而运行起来很慢？不仅仅您遇到这样的问题。几乎所有项目迟早都会遇到堆问题。大家都想说，&#8220;我的代码真正好，只是堆太慢&#8221;。那只是部分正确。更深入理解堆及其用法、以及会发生什么问题，是很有用的。</p>
<p>什么是堆？<br>（如果您已经知道什么是堆，可以跳到&#8220;什么是常见的堆性能问题？&#8221;部分）</p>
<p>在程序中，使用堆来动态分配和释放对象。在下列情况下，调用堆操作：&nbsp;</p>
<p>事先不知道程序所需对象的数量和大小。</p>
<p><br>对象太大而不适合堆栈分配程序。<br>堆使用了在运行时分配给代码和堆栈的内存之外的部分内存。下图给出了堆分配程序的不同层。<br><a href="http://club.5ivb.net/UploadFile/2005311144027byUID16686.gif" target=_blank><img alt="" src="http://club.5ivb.net/UploadFile/2005311144027byUID16686.gif" onload="javascript:if(this.width>screen.width-333)this.width=screen.width-333" border=0 dypop="按此在新窗口浏览图片"></a></p>
<p>GlobalAlloc/GlobalFree：Microsoft&nbsp;Win32&nbsp;堆调用，这些调用直接与每个进程的默认堆进行对话。</p>
<p>LocalAlloc/LocalFree：Win32&nbsp;堆调用（为了与&nbsp;Microsoft&nbsp;Windows&nbsp;NT&nbsp;兼容），这些调用直接与每个进程的默认堆进行对话。</p>
<p>COM&nbsp;的&nbsp;IMalloc&nbsp;分配程序（或&nbsp;CoTaskMemAlloc&nbsp;/&nbsp;CoTaskMemFree）：函数使用每个进程的默认堆。自动化程序使用&#8220;组件对象模型&nbsp;(COM)&#8221;的分配程序，而申请的程序使用每个进程堆。</p>
<p>C/C++&nbsp;运行时&nbsp;(CRT)&nbsp;分配程序：提供了&nbsp;malloc()&nbsp;和&nbsp;free()&nbsp;以及&nbsp;new&nbsp;和&nbsp;delete&nbsp;操作符。如&nbsp;Microsoft&nbsp;Visual&nbsp;Basic&nbsp;和&nbsp;Java&nbsp;等语言也提供了新的操作符并使用垃圾收集来代替堆。CRT&nbsp;创建自己的私有堆，驻留在&nbsp;Win32&nbsp;堆的顶部。</p>
<p>Windows&nbsp;NT&nbsp;中，Win32&nbsp;堆是&nbsp;Windows&nbsp;NT&nbsp;运行时分配程序周围的薄层。所有&nbsp;API&nbsp;转发它们的请求给&nbsp;NTDLL。</p>
<p>Windows&nbsp;NT&nbsp;运行时分配程序提供&nbsp;Windows&nbsp;NT&nbsp;内的核心堆分配程序。它由具有&nbsp;128&nbsp;个大小从&nbsp;8&nbsp;到&nbsp;1,024&nbsp;字节的空闲列表的前端分配程序组成。后端分配程序使用虚拟内存来保留和提交页。</p>
<p>在图表的底部是&#8220;虚拟内存分配程序&#8221;，操作系统使用它来保留和提交页。所有分配程序使用虚拟内存进行数据的存取。</p>
<p>分配和释放块不就那么简单吗？为何花费这么长时间？</p>
<p>堆实现的注意事项<br>传统上，操作系统和运行时库是与堆的实现共存的。在一个进程的开始，操作系统创建一个默认堆，叫做&#8220;进程堆&#8221;。如果没有其他堆可使用，则块的分配使用&#8220;进程堆&#8221;。语言运行时也能在进程内创建单独的堆。（例如，C&nbsp;运行时创建它自己的堆。）除这些专用的堆外，应用程序或许多已载入的动态链接库&nbsp;(DLL)&nbsp;之一可以创建和使用单独的堆。Win32&nbsp;提供一整套&nbsp;API&nbsp;来创建和使用私有堆。有关堆函数（英文）的详尽指导，请参见&nbsp;MSDN。</p>
<p>当应用程序或&nbsp;DLL&nbsp;创建私有堆时，这些堆存在于进程空间，并且在进程内是可访问的。从给定堆分配的数据将在同一个堆上释放。（不能从一个堆分配而在另一个堆释放。）</p>
<p>在所有虚拟内存系统中，堆驻留在操作系统的&#8220;虚拟内存管理器&#8221;的顶部。语言运行时堆也驻留在虚拟内存顶部。某些情况下，这些堆是操作系统堆中的层，而语言运行时堆则通过大块的分配来执行自己的内存管理。不使用操作系统堆，而使用虚拟内存函数更利于堆的分配和块的使用。</p>
<p>典型的堆实现由前、后端分配程序组成。前端分配程序维持固定大小块的空闲列表。对于一次分配调用，堆尝试从前端列表找到一个自由块。如果失败，堆被迫从后端（保留和提交虚拟内存）分配一个大块来满足请求。通用的实现有每块分配的开销，这将耗费执行周期，也减少了可使用的存储空间。</p>
<p>Knowledge&nbsp;Base&nbsp;文章&nbsp;Q10758，&#8220;用&nbsp;calloc()&nbsp;和&nbsp;malloc()&nbsp;管理内存&#8221;&nbsp;（搜索文章编号）,&nbsp;包含了有关这些主题的更多背景知识。另外，有关堆实现和设计的详细讨论也可在下列著作中找到：&#8220;Dynamic&nbsp;Storage&nbsp;Allocation:&nbsp;A&nbsp;Survey&nbsp;and&nbsp;Critical&nbsp;Review&#8221;，作者&nbsp;Paul&nbsp;R.&nbsp;Wilson、Mark&nbsp;S.&nbsp;Johnstone、Michael&nbsp;Neely&nbsp;和&nbsp;David&nbsp;Boles；&#8220;International&nbsp;Workshop&nbsp;on&nbsp;Memory&nbsp;Management&#8221;,&nbsp;作者&nbsp;Kinross,&nbsp;Scotland,&nbsp;UK,&nbsp;1995&nbsp;年&nbsp;9&nbsp;月(<img alt="" src="http://club.5ivb.net/pic/url.gif" align=absMiddle border=0><a href="http://www.cs.utexas.edu/users/oops/papers.html" target=_blank><font color=#000000><u>http://www.cs.utexas.edu/users/oops/papers.html</u></font></a>)（英文）。</p>
<p>Windows&nbsp;NT&nbsp;的实现（Windows&nbsp;NT&nbsp;版本&nbsp;4.0&nbsp;和更新版本）&nbsp;使用了&nbsp;127&nbsp;个大小从&nbsp;8&nbsp;到&nbsp;1,024&nbsp;字节的&nbsp;8&nbsp;字节对齐块空闲列表和一个&#8220;大块&#8221;列表。&#8220;大块&#8221;列表（空闲列表[0]）&nbsp;保存大于&nbsp;1,024&nbsp;字节的块。空闲列表容纳了用双向链表链接在一起的对象。默认情况下，&#8220;进程堆&#8221;执行收集操作。（收集是将相邻空闲块合并成一个大块的操作。）收集耗费了额外的周期，但减少了堆块的内部碎片。</p>
<p>单一全局锁保护堆，防止多线程式的使用。（请参见&#8220;Server&nbsp;Performance&nbsp;and&nbsp;Scalability&nbsp;Killers&#8221;中的第一个注意事项,&nbsp;George&nbsp;Reilly&nbsp;所著，在&nbsp;&#8220;MSDN&nbsp;Online&nbsp;Web&nbsp;Workshop&#8221;上（站点：<img alt="" src="http://club.5ivb.net/pic/url.gif" align=absMiddle border=0><a href="http://msdn.microsoft.com/workshop/server/iis/tencom.asp" target=_blank><font color=#000000><u>http://msdn.microsoft.com/workshop/server/iis/tencom.asp</u></font></a>（英文）。）单一全局锁本质上是用来保护堆数据结构，防止跨多线程的随机存取。若堆操作太频繁，单一全局锁会对性能有不利的影响。</p>
<p>什么是常见的堆性能问题？<br>以下是您使用堆时会遇到的最常见问题：&nbsp;</p>
<p>分配操作造成的速度减慢。光分配就耗费很长时间。最可能导致运行速度减慢原因是空闲列表没有块，所以运行时分配程序代码会耗费周期寻找较大的空闲块，或从后端分配程序分配新块。</p>
<p><br>释放操作造成的速度减慢。释放操作耗费较多周期，主要是启用了收集操作。收集期间，每个释放操作&#8220;查找&#8221;它的相邻块，取出它们并构造成较大块，然后再把此较大块插入空闲列表。在查找期间，内存可能会随机碰到，从而导致高速缓存不能命中，性能降低。</p>
<p><br>堆竞争造成的速度减慢。当两个或多个线程同时访问数据，而且一个线程继续进行之前必须等待另一个线程完成时就发生竞争。竞争总是导致麻烦；这也是目前多处理器系统遇到的最大问题。当大量使用内存块的应用程序或&nbsp;DLL&nbsp;以多线程方式运行（或运行于多处理器系统上）时将导致速度减慢。单一锁定的使用—常用的解决方案—意味着使用堆的所有操作是序列化的。当等待锁定时序列化会引起线程切换上下文。可以想象交叉路口闪烁的红灯处走走停停导致的速度减慢。&nbsp;<br>竞争通常会导致线程和进程的上下文切换。上下文切换的开销是很大的，但开销更大的是数据从处理器高速缓存中丢失，以及后来线程复活时的数据重建。</p>
<p>堆破坏造成的速度减慢。造成堆破坏的原因是应用程序对堆块的不正确使用。通常情形包括释放已释放的堆块或使用已释放的堆块，以及块的越界重写等明显问题。（破坏不在本文讨论范围之内。有关内存重写和泄漏等其他细节，请参见&nbsp;Microsoft&nbsp;Visual&nbsp;C++(R)&nbsp;调试文档&nbsp;。）</p>
<p><br>频繁的分配和重分配造成的速度减慢。这是使用脚本语言时非常普遍的现象。如字符串被反复分配，随重分配增长和释放。不要这样做，如果可能，尽量分配大字符串和使用缓冲区。另一种方法就是尽量少用连接操作。<br>竞争是在分配和释放操作中导致速度减慢的问题。理想情况下，希望使用没有竞争和快速分配/释放的堆。可惜，现在还没有这样的通用堆，也许将来会有。</p>
<p>在所有的服务器系统中（如&nbsp;IIS、MSProxy、DatabaseStacks、网络服务器、&nbsp;Exchange&nbsp;和其他）,&nbsp;堆锁定实在是个大瓶颈。处理器数越多，竞争就越会恶化。</p>
<p>尽量减少堆的使用<br>现在您明白使用堆时存在的问题了，难道您不想拥有能解决这些问题的超级魔棒吗？我可希望有。但没有魔法能使堆运行加快—因此不要期望在产品出货之前的最后一星期能够大为改观。如果提前规划堆策略，情况将会大大好转。调整使用堆的方法，减少对堆的操作是提高性能的良方。</p>
<p>如何减少使用堆操作？通过利用数据结构内的位置可减少堆操作的次数。请考虑下列实例：</p>
<p>struct&nbsp;ObjectA&nbsp;{<br>&nbsp;&nbsp;&nbsp;//&nbsp;objectA&nbsp;的数据&nbsp;<br>}</p>
<p>struct&nbsp;ObjectB&nbsp;{<br>&nbsp;&nbsp;&nbsp;//&nbsp;objectB&nbsp;的数据&nbsp;<br>}</p>
<p>//&nbsp;同时使用&nbsp;objectA&nbsp;和&nbsp;objectB</p>
<p>//<br>//&nbsp;使用指针&nbsp;<br>//<br>struct&nbsp;ObjectB&nbsp;{<br>&nbsp;&nbsp;&nbsp;struct&nbsp;ObjectA&nbsp;*&nbsp;pObjA;<br>&nbsp;&nbsp;&nbsp;//&nbsp;objectB&nbsp;的数据&nbsp;<br>}</p>
<p>//<br>//&nbsp;使用嵌入<br>//<br>struct&nbsp;ObjectB&nbsp;{<br>&nbsp;&nbsp;&nbsp;struct&nbsp;ObjectA&nbsp;pObjA;<br>&nbsp;&nbsp;&nbsp;//&nbsp;objectB&nbsp;的数据&nbsp;<br>}</p>
<p>//<br>//&nbsp;集合&nbsp;&#8211;&nbsp;在另一对象内使用&nbsp;objectA&nbsp;和&nbsp;objectB<br>//</p>
<p>struct&nbsp;ObjectX&nbsp;{<br>&nbsp;&nbsp;&nbsp;struct&nbsp;ObjectA&nbsp;&nbsp;objA;<br>&nbsp;&nbsp;&nbsp;struct&nbsp;ObjectB&nbsp;&nbsp;objB;<br>}</p>
<p>避免使用指针关联两个数据结构。如果使用指针关联两个数据结构，前面实例中的对象&nbsp;A&nbsp;和&nbsp;B&nbsp;将被分别分配和释放。这会增加额外开销—我们要避免这种做法。</p>
<p><br>把带指针的子对象嵌入父对象。当对象中有指针时，则意味着对象中有动态元素（百分之八十）和没有引用的新位置。嵌入增加了位置从而减少了进一步分配/释放的需求。这将提高应用程序的性能。</p>
<p><br>合并小对象形成大对象（聚合）。聚合减少分配和释放的块的数量。如果有几个开发者，各自开发设计的不同部分，则最终会有许多小对象需要合并。集成的挑战就是要找到正确的聚合边界。</p>
<p><br>内联缓冲区能够满足百分之八十的需要（aka&nbsp;80-20&nbsp;规则）。个别情况下，需要内存缓冲区来保存字符串/二进制数据，但事先不知道总字节数。估计并内联一个大小能满足百分之八十需要的缓冲区。对剩余的百分之二十，可以分配一个新的缓冲区和指向这个缓冲区的指针。这样，就减少分配和释放调用并增加数据的位置空间，从根本上提高代码的性能。</p>
<p><br>在块中分配对象（块化）。块化是以组的方式一次分配多个对象的方法。如果对列表的项连续跟踪，例如对一个&nbsp;{名称，值}&nbsp;对的列表，有两种选择：选择一是为每一个&#8220;名称-值&#8221;对分配一个节点；选择二是分配一个能容纳（如五个）&#8220;名称-值&#8221;对的结构。例如，一般情况下，如果存储四对，就可减少节点的数量，如果需要额外的空间数量，则使用附加的链表指针。&nbsp;<br>块化是友好的处理器高速缓存，特别是对于&nbsp;L1-高速缓存，因为它提供了增加的位置&nbsp;—不用说对于块分配，很多数据块会在同一个虚拟页中。</p>
<p>正确使用&nbsp;_amblksiz。C&nbsp;运行时&nbsp;(CRT)&nbsp;有它的自定义前端分配程序，该分配程序从后端（Win32&nbsp;堆）分配大小为&nbsp;_amblksiz&nbsp;的块。将&nbsp;_amblksiz&nbsp;设置为较高的值能潜在地减少对后端的调用次数。这只对广泛使用&nbsp;CRT&nbsp;的程序适用。<br>使用上述技术将获得的好处会因对象类型、大小及工作量而有所不同。但总能在性能和可升缩性方面有所收获。另一方面，代码会有点特殊，但如果经过深思熟虑，代码还是很容易管理的。</p>
<p>其他提高性能的技术<br>下面是一些提高速度的技术：&nbsp;</p>
<p>使用&nbsp;Windows&nbsp;NT5&nbsp;堆&nbsp;<br>由于几个同事的努力和辛勤工作，1998&nbsp;年初&nbsp;Microsoft&nbsp;Windows(R)&nbsp;2000&nbsp;中有了几个重大改进：</p>
<p>改进了堆代码内的锁定。堆代码对每堆一个锁。全局锁保护堆数据结构，防止多线程式的使用。但不幸的是，在高通信量的情况下，堆仍受困于全局锁，导致高竞争和低性能。Windows&nbsp;2000&nbsp;中，锁内代码的临界区将竞争的可能性减到最小,从而提高了可伸缩性。</p>
<p><br>使用&nbsp;&#8220;Lookaside&#8221;列表。堆数据结构对块的所有空闲项使用了大小在&nbsp;8&nbsp;到&nbsp;1,024&nbsp;字节（以&nbsp;8-字节递增）的快速高速缓存。快速高速缓存最初保护在全局锁内。现在，使用&nbsp;lookaside&nbsp;列表来访问这些快速高速缓存空闲列表。这些列表不要求锁定，而是使用&nbsp;64&nbsp;位的互锁操作，因此提高了性能。</p>
<p><br>内部数据结构算法也得到改进。<br>这些改进避免了对分配高速缓存的需求，但不排除其他的优化。使用&nbsp;Windows&nbsp;NT5&nbsp;堆评估您的代码；它对小于&nbsp;1,024&nbsp;字节&nbsp;(1&nbsp;KB)&nbsp;的块（来自前端分配程序的块）是最佳的。GlobalAlloc()&nbsp;和&nbsp;LocalAlloc()&nbsp;建立在同一堆上，是存取每个进程堆的通用机制。如果希望获得高的局部性能，则使用&nbsp;Heap(R)&nbsp;API&nbsp;来存取每个进程堆，或为分配操作创建自己的堆。如果需要对大块操作，也可以直接使用&nbsp;VirtualAlloc()&nbsp;/&nbsp;VirtualFree()&nbsp;操作。</p>
<p>上述改进已在&nbsp;Windows&nbsp;2000&nbsp;beta&nbsp;2&nbsp;和&nbsp;Windows&nbsp;NT&nbsp;4.0&nbsp;SP4&nbsp;中使用。改进后，堆锁的竞争率显著降低。这使所有&nbsp;Win32&nbsp;堆的直接用户受益。CRT&nbsp;堆建立于&nbsp;Win32&nbsp;堆的顶部，但它使用自己的小块堆，因而不能从&nbsp;Windows&nbsp;NT&nbsp;改进中受益。（Visual&nbsp;C++&nbsp;版本&nbsp;6.0&nbsp;也有改进的堆分配程序。）</p>
<p>使用分配高速缓存&nbsp;<br>分配高速缓存允许高速缓存分配的块，以便将来重用。这能够减少对进程堆（或全局堆）的分配/释放调用的次数，也允许最大限度的重用曾经分配的块。另外，分配高速缓存允许收集统计信息,以便较好地理解对象在较高层次上的使用。</p>
<p>典型地，自定义堆分配程序在进程堆的顶部实现。自定义堆分配程序与系统堆的行为很相似。主要的差别是它在进程堆的顶部为分配的对象提供高速缓存。高速缓存设计成一套固定大小（如&nbsp;32&nbsp;字节、64&nbsp;字节、128&nbsp;字节等）。这一个很好的策略，但这种自定义堆分配程序丢失与分配和释放的对象相关的&#8220;语义信息&#8221;。&nbsp;</p>
<p>与自定义堆分配程序相反，&#8220;分配高速缓存&#8221;作为每类分配高速缓存来实现。除能够提供自定义堆分配程序的所有好处之外，它们还能够保留大量语义信息。每个分配高速缓存处理程序与一个目标二进制对象关联。它能够使用一套参数进行初始化，这些参数表示并发级别、对象大小和保持在空闲列表中的元素的数量等。分配高速缓存处理程序对象维持自己的私有空闲实体池（不超过指定的阀值）并使用私有保护锁。合在一起，分配高速缓存和私有锁减少了与主系统堆的通信量，因而提供了增加的并发、最大限度的重用和较高的可伸缩性。</p>
<p>需要使用清理程序来定期检查所有分配高速缓存处理程序的活动情况并回收未用的资源。如果发现没有活动，将释放分配对象的池，从而提高性能。</p>
<p>可以审核每个分配/释放活动。第一级信息包括对象、分配和释放调用的总数。通过查看它们的统计信息可以得出各个对象之间的语义关系。利用以上介绍的许多技术之一，这种关系可以用来减少内存分配。</p>
<p>分配高速缓存也起到了调试助手的作用，帮助您跟踪没有完全清除的对象数量。通过查看动态堆栈返回踪迹和除没有清除的对象之外的签名，甚至能够找到确切的失败的调用者。</p>
<p>MP&nbsp;堆&nbsp;<br>MP&nbsp;堆是对多处理器友好的分布式分配的程序包，在&nbsp;Win32&nbsp;SDK（Windows&nbsp;NT&nbsp;4.0&nbsp;和更新版本）中可以得到。最初由&nbsp;JVert&nbsp;实现，此处堆抽象建立在&nbsp;Win32&nbsp;堆程序包的顶部。MP&nbsp;堆创建多个&nbsp;Win32&nbsp;堆，并试图将分配调用分布到不同堆，以减少在所有单一锁上的竞争。</p>
<p>本程序包是好的步骤&nbsp;—一种改进的&nbsp;MP-友好的自定义堆分配程序。但是，它不提供语义信息和缺乏统计功能。通常将&nbsp;MP&nbsp;堆作为&nbsp;SDK&nbsp;库来使用。如果使用这个&nbsp;SDK&nbsp;创建可重用组件，您将大大受益。但是，如果在每个&nbsp;DLL&nbsp;中建立这个&nbsp;SDK&nbsp;库，将增加工作设置。</p>
<p>重新思考算法和数据结构&nbsp;<br>要在多处理器机器上伸缩，则算法、实现、数据结构和硬件必须动态伸缩。请看最经常分配和释放的数据结构。试问，&#8220;我能用不同的数据结构完成此工作吗？&#8221;例如，如果在应用程序初始化时加载了只读项的列表，这个列表不必是线性链接的列表。如果是动态分配的数组就非常好。动态分配的数组将减少内存中的堆块和碎片，从而增强性能。</p>
<p>减少需要的小对象的数量减少堆分配程序的负载。例如，我们在服务器的关键处理路径上使用五个不同的对象，每个对象单独分配和释放。一起高速缓存这些对象，把堆调用从五个减少到一个，显著减少了堆的负载，特别当每秒钟处理&nbsp;1,000&nbsp;个以上的请求时。</p>
<p>如果大量使用&#8220;Automation&#8221;结构，请考虑从主线代码中删除&#8220;Automation&nbsp;BSTR&#8221;，或至少避免重复的&nbsp;BSTR&nbsp;操作。（BSTR&nbsp;连接导致过多的重分配和分配/释放操作。）</p>
<p>摘要<br>对所有平台往往都存在堆实现，因此有巨大的开销。每个单独代码都有特定的要求，但设计能采用本文讨论的基本理论来减少堆之间的相互作用。&nbsp;</p>
<p>评价您的代码中堆的使用。</p>
<p><br>改进您的代码，以使用较少的堆调用：分析关键路径和固定数据结构。</p>
<p><br>在实现自定义的包装程序之前使用量化堆调用成本的方法。</p>
<p><br>如果对性能不满意，请要求&nbsp;OS&nbsp;组改进堆。更多这类请求意味着对改进堆的更多关注。</p>
<p><br>要求&nbsp;C&nbsp;运行时组针对&nbsp;OS&nbsp;所提供的堆制作小巧的分配包装程序。随着&nbsp;OS&nbsp;堆的改进，C&nbsp;运行时堆调用的成本将减小。</p>
<p><br>操作系统（Windows&nbsp;NT&nbsp;家族）正在不断改进堆。请随时关注和利用这些改进。<br>Murali&nbsp;Krishnan&nbsp;是&nbsp;Internet&nbsp;Information&nbsp;Server&nbsp;(IIS)&nbsp;组的首席软件设计工程师。从&nbsp;1.0&nbsp;版本开始他就设计&nbsp;IIS，并成功发行了&nbsp;1.0&nbsp;版本到&nbsp;4.0&nbsp;版本。Murali&nbsp;组织并领导&nbsp;IIS&nbsp;性能组三年&nbsp;(1995-1998),&nbsp;从一开始就影响&nbsp;IIS&nbsp;性能。他拥有威斯康星州&nbsp;Madison&nbsp;大学的&nbsp;M.S.和印度&nbsp;Anna&nbsp;大学的&nbsp;B.S.。工作之外，他喜欢阅读、打排球和家庭烹饪。<br><br><br><br><img alt="" src="http://club.5ivb.net/pic/url.gif" align=absMiddle border=0><a href="http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=172835" target=_blank><font color=#000000><u>http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=172835</u></font></a><br>我在学习对象的生存方式的时候见到一种是在堆栈(stack)之中，如下&nbsp;&nbsp;<br>CObject&nbsp;&nbsp;object;&nbsp;&nbsp;<br>还有一种是在堆(heap)中&nbsp;&nbsp;如下&nbsp;&nbsp;<br>CObject*&nbsp;&nbsp;pobject=new&nbsp;&nbsp;CObject();&nbsp;&nbsp;<br>&nbsp;<br>请问&nbsp;&nbsp;<br>（1）这两种方式有什么区别？&nbsp;&nbsp;<br>（2）堆栈与堆有什么区别？？&nbsp;&nbsp;<br>&nbsp;<br>&nbsp;<br>---------------------------------------------------------------&nbsp;&nbsp;<br>&nbsp;<br>1)&nbsp;&nbsp;about&nbsp;&nbsp;stack,&nbsp;&nbsp;system&nbsp;&nbsp;will&nbsp;&nbsp;allocate&nbsp;&nbsp;memory&nbsp;&nbsp;to&nbsp;&nbsp;the&nbsp;&nbsp;instance&nbsp;&nbsp;of&nbsp;&nbsp;object&nbsp;&nbsp;automatically,&nbsp;&nbsp;and&nbsp;&nbsp;to&nbsp;&nbsp;the <br>&nbsp;heap,&nbsp;&nbsp;you&nbsp;&nbsp;must&nbsp;&nbsp;allocate&nbsp;&nbsp;memory&nbsp;&nbsp;to&nbsp;&nbsp;the&nbsp;&nbsp;instance&nbsp;&nbsp;of&nbsp;&nbsp;object&nbsp;&nbsp;with&nbsp;&nbsp;new&nbsp;&nbsp;or&nbsp;&nbsp;malloc&nbsp;&nbsp;manually.&nbsp;&nbsp;<br>2)&nbsp;&nbsp;when&nbsp;&nbsp;function&nbsp;&nbsp;ends,&nbsp;&nbsp;system&nbsp;&nbsp;will&nbsp;&nbsp;automatically&nbsp;&nbsp;free&nbsp;&nbsp;the&nbsp;&nbsp;memory&nbsp;&nbsp;area&nbsp;&nbsp;of&nbsp;&nbsp;stack,&nbsp;&nbsp;but&nbsp;&nbsp;to&nbsp;&nbsp;the&nbsp; <br>heap,&nbsp;&nbsp;you&nbsp;&nbsp;must&nbsp;&nbsp;free&nbsp;&nbsp;the&nbsp;&nbsp;memory&nbsp;&nbsp;area&nbsp;&nbsp;manually&nbsp;&nbsp;with&nbsp;&nbsp;free&nbsp;&nbsp;or&nbsp;&nbsp;delete,&nbsp;&nbsp;else&nbsp;&nbsp;it&nbsp;&nbsp;will&nbsp;&nbsp;result&nbsp;&nbsp;in&nbsp;&nbsp;memory <br>leak.&nbsp;&nbsp;<br>3)栈内存分配运算内置于处理器的指令集中，效率很高，但是分配的内存容量有限。&nbsp;&nbsp;<br>4）堆上分配的内存可以有我们自己决定，使用非常灵活。&nbsp;&nbsp;<br></p>
</div>
<img src ="http://www.cppblog.com/deane/aggbug/137757.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2010-12-30 18:38 <a href="http://www.cppblog.com/deane/articles/137757.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GDB用法详解</title><link>http://www.cppblog.com/deane/articles/137755.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Thu, 30 Dec 2010 10:37:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/137755.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/137755.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/137755.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/137755.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/137755.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: GDB用法详解GDB是一个强大的命令行调试工具。虽然X Window提供了GDB的图形版DDD，但是我仍然更钟爱在命令行模式下使用GDB。大家知道命令行的强大就是在于，其可以形成执行序列，形成脚本。UNIX下的软件全是命令行的，这给程序开发提代供了极大的便利，命令行软件的优势在于，它们可以非常容易的集成在一起，使用几个简单的已有工具的命令，就可以做出一个非常强大的功能。&nbsp;&nbsp;...&nbsp;&nbsp;<a href='http://www.cppblog.com/deane/articles/137755.html'>阅读全文</a><img src ="http://www.cppblog.com/deane/aggbug/137755.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2010-12-30 18:37 <a href="http://www.cppblog.com/deane/articles/137755.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>多字节与UTF-8、Unicode之间的转换</title><link>http://www.cppblog.com/deane/articles/120243.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Tue, 13 Jul 2010 10:29:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/120243.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/120243.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/120243.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/120243.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/120243.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: //&nbsp;多字节编码转为UTF8编码&nbsp;&nbsp;bool&nbsp;MBToUTF8(vector&lt;char&gt;&amp;&nbsp;pu8,&nbsp;const&nbsp;char*&nbsp;pmb,&nbsp;int32&nbsp;mLen)&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;//&nbsp;convert&nbsp;an&nbsp;M...&nbsp;&nbsp;<a href='http://www.cppblog.com/deane/articles/120243.html'>阅读全文</a><img src ="http://www.cppblog.com/deane/aggbug/120243.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2010-07-13 18:29 <a href="http://www.cppblog.com/deane/articles/120243.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C风格字符串[C++] </title><link>http://www.cppblog.com/deane/articles/114472.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Wed, 05 May 2010 05:07:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/114472.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/114472.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/114472.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/114472.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/114472.html</trackback:ping><description><![CDATA[<div class=postTitle><strong>1.字符串字面值<br></strong>&nbsp;&nbsp;字符串字面值是一串常量字符，字符串字面值常量用双引号括起来的零个或多个字符表示，为兼容C语言，C++中所有的字符串字面值都由编译器自动在末尾添加一个空字符<br>&nbsp; "Hello World!" <span style="COLOR: #008000">//simple string literal</span><br>&nbsp; ""&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: #008000">//empty string literal</span><br>&nbsp; "\nCC\toptions\tfile.[cC]\n"&nbsp; <span style="COLOR: #008000">//string literal using newlines and tabs</span><br><br>&nbsp; 字符字面值： 'A'&nbsp; <span style="COLOR: #008000">//single quoto:character literal</span><br>&nbsp; 字符串字面值： "A"&nbsp; <span style="COLOR: #008000">//double quote:character string literal.包含字母A和空字符的字符串<br></span><br>&nbsp; 字符串字面值的连接：<br>&nbsp; std::out &lt;&lt; "a multi-line "<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "string literal"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; " using concatenation"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;&lt; std::endl;<br>&nbsp;&nbsp;输出：<span style="COLOR: #000000; BACKGROUND-COLOR: #ffffff">a multi-line string literal using concatenation</span><br><br>&nbsp; 多行字面值：<br>&nbsp;&nbsp;std::out &lt;&lt; "a multi-line&nbsp;&nbsp;\<br>&nbsp;&nbsp; string literal\<br>using a backslash"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;&lt; std::endl;<br>&nbsp; 输出：<span style="COLOR: #000000; BACKGROUND-COLOR: #ffffff">a multi-line&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string literalusing a backslash</span><br><br><br><strong>2.C风格字符串</strong><br>&nbsp;&nbsp; 字符串字面值的类型实质是const char类型的数组。C++从C语言继承下来的一种通用结构是C风格字符串，而字符串字面值就是该类型的实例。C风格字符串是以空字符null结束的字符数组：<br>&nbsp;&nbsp; char ca1[]={'C', '+', '+'};&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: #008000">// no null, not C-style string</span><br>&nbsp;&nbsp; char ca2[]={'C', '+', '+', '\0'};&nbsp;&nbsp;&nbsp;<span style="COLOR: #008000">// explicit null<br></span>&nbsp;&nbsp; char ca3[]="C++";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: #008000">// null terminator added automatically</span><br>&nbsp;&nbsp; const char *cp="C++";&nbsp;&nbsp; <span style="COLOR: #008000">// null terminator added automatically</span><br>&nbsp;&nbsp; char *cp1=ca1;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: #008000">// points to first element of a array, but not C-style string</span><br>&nbsp;&nbsp; char *cp2=ca2;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #008000">// points to first element of a null-terminated char array<br></span>&nbsp;&nbsp; ca1和cp1都不是C风格字符串：ca1是一个不带结束符null的字符数组，而指针cp1指向ca1，因此，它指向的并不是以null结束的数组。<br>&nbsp;&nbsp; 2.1 C风格字符串的使用<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C++语言通过(const) char *类型的指针来操纵C风格字符串。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const char *cp = "some value";&nbsp; <span style="COLOR: #008000">// 一个C风格字符串</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while(*cp) <span style="COLOR: #008000">//判断cp当前指向的字符是true还是false，true表明这是除null外的任意字符</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: #008000">// do something to *cp<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ++cp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp; 2.2 C风格字符串的标准库函数<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;cstring&gt;&nbsp; <span style="COLOR: #008000">// cstring是string.h头文件中的C++版本，而string.h是C语言提供的标准库</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;操纵C风格字符串的标准库函数：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strlen(s)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: #008000">// 返回s的长度，不包括字符串结束符null</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcmp(s1, s2)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcat(s1, s2)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: #008000">//&nbsp;将字符串s2连接到s1后，并返回s1&nbsp;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcpy(s1, s2)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: #008000">// 将s2复制给s1，并返回s1<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strncat(s1, s2, n)&nbsp; <span style="COLOR: #008000">// 将s2的前n个字符连接到s1后面，并返回s1</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strncpy(s1, s2, n)&nbsp; <span style="COLOR: #008000">// 将s2的前n个字符复制给s1，并返回s1</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(cp1 &lt; cp2)&nbsp;&nbsp;&nbsp;<span style="COLOR: #008000">// compares address, not the values pointed to<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const char *cp1 = "A string example";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const char *cp2 = "A different string";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int i=strcmp(cp1, cp2);&nbsp;&nbsp; <span style="COLOR: #008000">// i is positive</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i=strcmp(cp2, cp1);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #008000">// i is negative</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i=strcmp(cp1, cp1);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: #008000">// i is zero<br></span>&nbsp;&nbsp;&nbsp;2.3 永远不要忘记字符串结束符null<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char ca[]={'C', '+', '+'};&nbsp; <span style="COLOR: #008000">// not null-terminated</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; strlen(ca) &lt;&lt; endl;&nbsp;<span style="COLOR: #008000">// disaster: ca isn't null-terminated</span><br>&nbsp;&nbsp; 2.4 调用者必须确保目标字符串具有足够的大小<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: #008000">// Dangerous:What happens if we miscalculate the size of largeStr?<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char largeStr[16+18+2];&nbsp; <span style="COLOR: #008000">// will hold cp1 a&nbsp;space and cp2<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcpy(largeStr, cp1);&nbsp;&nbsp; <span style="COLOR: #008000">// copies cp1 into largeStr</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcat(largeStr, " ");&nbsp;&nbsp; <span style="COLOR: #008000">// adds a&nbsp;space at end of largeStr</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcat(largeStr, cp2);&nbsp;&nbsp; <span style="COLOR: #008000">// concatenates cp2 to largeStr</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: #008000">// prints A string example A different string</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; largeStr &lt;&lt; endl;<br>&nbsp;&nbsp; 2.5 使用strn函数处理C风格字符串<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char largeStr[16+18+2]&nbsp;&nbsp; <span style="COLOR: #008000">// to hold cp1 a space and cp2</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strncpy(largeStr, cp1, 17);&nbsp; <span style="COLOR: #008000">// size to copy includes the null</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strncat(largeStr, " ", 2);&nbsp;&nbsp; <span style="COLOR: #008000">// pedantic, but a good habit</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strncat(largeStr, cp2, 19);&nbsp; <span style="COLOR: #008000">// adds at most 18 characters, plus a null</span><br>&nbsp;&nbsp; 2.6 尽可能使用标准库类型string<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string largeStr = cp1;&nbsp;&nbsp; <span style="COLOR: #008000">// initialize largeStr as a copy of cp1<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; largeStr += " ";&nbsp;&nbsp; <span style="COLOR: #008000">// add space at end of largeStr</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;largeStr += cp2;&nbsp;&nbsp; <span style="COLOR: #008000">// concatenate cp2 onto end of largeStr</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 此时，标准库负责处理所有的内在管理问题。<br>&nbsp;<br><br></div>
<img src ="http://www.cppblog.com/deane/aggbug/114472.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2010-05-05 13:07 <a href="http://www.cppblog.com/deane/articles/114472.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>unsigned, unsinged int，unsigned long，size_t还是std::size_t？</title><link>http://www.cppblog.com/deane/articles/113724.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Tue, 27 Apr 2010 09:29:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/113724.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/113724.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/113724.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/113724.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/113724.html</trackback:ping><description><![CDATA[

<div><div class="tit"><br></div>
<table style="table-layout: fixed; width: 100%; ">
<tbody>
<tr>
<td>
<div class="cnt" id="blog_text">
<p>
<table class="FCK__ShowTableBorders" style="table-layout: fixed; width: 100%; "><tbody><tr><td><div class="cnt">
<div class="postText">
<p>首先四种类型都是无符号类型，是用以表示元素个数或者数组索引的最佳类型。在作为函数参数时，不需像有符号类型那样检测值是否小于零。<br><br><strong>1.</strong> 
::size_t还是std::size_t<br>请使用std::size_t，因为你处于C++的世界。<br>在此，所有C++标准库组件用以表示元素个数的类型（比如size()或者operator[]）都是std::size_t。<br><br>std::size_t 
count = array.size(); // array是typedef vector&lt;int&gt;<br>std::size_t index = 
0;<br>array[ index ] = 0; <br><br>注意：<br>1) 
如果某个CPP没有使用任何C++标准库组件，那么就有可能需要包含&lt;cstddef&gt; 头文件。<br>2) 
std::size_t其实就是::size_t (::size_t被引入到namespace std中(你可以在&lt;cstring&gt;中找到)</p>
<p><br><strong>2.</strong>基本上我们不会考虑unsigned int和unsigned 
long，因为处在C++的世界，使用C++标准库组件就是在所难免了。<br>如果你非要了解其细枝末节的话，那么下面是一份清单：<br><br><font color="#a64d79">unsigned int 和 unsigned 
long比较(不考虑32位以下的平台）</font><br><br>如果不考虑可移植性：<br>在32位平台上更应该使用unsigned 
int，因为它：<br>1)和unsigned long 一样的大小，32位可以表示到42.9亿。<br>2) 比unsigned long更常用<br>3) 
和std::size_t是一样的类型<br><br>如果是64位平台的话：<br>1) unsinged int仍是32位，而unsigned 
long就是64位了。<br>2) 更应该使用unsigned 
long因为处理器对64位具有更快的处理速度。<br><br>就目前而言，64位平台还不够成熟，所以向64位平台的移植基本不做考虑。<br><br>但是如果你坚持要考虑可移植性(注意是硬件32位平台向64位移植，而非软件）：<br>1) 
如果对速度敏感：使用unsigned long，无论在32位还是64位都有最快的处理速度。<br>2) 如果对内存敏感：使用unsigned 
int，使用内存量不会因平台而改变。<br>不过通常对于硬件平台的可移植性的考虑都是多余的（不够敏捷哦）。<br><br><font color="#741b47"><strong>3. 
</strong>关于unsigned类型:</font></p></div></div></td></tr></tbody></table></p>
<p>有时候会碰到一些C语言的函数,它的参数类型是unsigned,而不是unsigned int、unsigned long之类的，例如：Turbo 
C的库函数中有这么几个函数：unsigned far setgraphbufsize(unsigned bufsize);int read(int 
handle, void *buf, unsigned len); int write(int handle, void *buf, unsigned 
len);</p>
<p><font color="#9900ff">[摘引他人]</font>这种情况一般都会在前面进行宏定义的<br>是为了版本兼容或跨平台<br>现在的32位变成64位的机器 
这样的定义有必要<br>如<br>#ifdef WINDOWS<br>&nbsp;&nbsp; define unsigned (unsigned 
int)<br>#endif</p>
<p>ifdef LINUX<br>&nbsp;&nbsp;&nbsp; define unsigned (unsigned 
long)<br>#endif<br>这样的话在跨平台的时候就很有必要了</p>
<p><font color="#9900ff">CHECK:</font>个人经查阅VC6系统头文件，并未发现上面的宏定义；同时结合一些函数（如_beginthreadex）查阅MSDN，判断unsigned在32位系统下就代表unsigned 
int，毕竟这是系统最常用的类型。</p></div></td></tr></tbody></table><br></div><div><br></div><img src ="http://www.cppblog.com/deane/aggbug/113724.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2010-04-27 17:29 <a href="http://www.cppblog.com/deane/articles/113724.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++boost转载</title><link>http://www.cppblog.com/deane/articles/113721.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Tue, 27 Apr 2010 09:18:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/113721.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/113721.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/113721.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/113721.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/113721.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: boost是一个准标准库，相当于STL的延续和扩充，它的设计理念和STL比较接近，都是利用泛型让复用达到最大化。不过对比STL，boost更加实用。  STL集中在算法部分，而boost包含了不少工具类，可以完成比较具体的工作。   boost主要包含一下几个大类：1  字符串及文本处理、容器、迭代子(Iterator)、算法、函数对象和高阶编程、2 泛型编程、模板元编程、预处理元编程、并发编程、...&nbsp;&nbsp;<a href='http://www.cppblog.com/deane/articles/113721.html'>阅读全文</a><img src ="http://www.cppblog.com/deane/aggbug/113721.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2010-04-27 17:18 <a href="http://www.cppblog.com/deane/articles/113721.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++ 堆栈（转）</title><link>http://www.cppblog.com/deane/articles/113603.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Mon, 26 Apr 2010 07:59:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/113603.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/113603.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/113603.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/113603.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/113603.html</trackback:ping><description><![CDATA[<div class=tit>&nbsp;</div>
<table style="TABLE-LAYOUT: fixed; WIDTH: 100%">
    <tbody>
        <tr>
            <td>
            <div class=cnt id=blog_text>
            <p>&#160;</p>
            <p>堆：操作系统有一个记录空闲内存地址的链表，当系统收到程序的申请时，会遍历该链表，寻找第一个空间大于所申请空间的堆结点，然后将该结点从空闲结点链表中删除，并将该结点的空间分配给程序，另外，对于大多数系统，会在这块内存空间中的首地址处记录本次分配的大小，这样代码 中的delete语句才能正确的释放本内存空间。我们常说的内存泄露，最常见的就是堆泄露（还有资源泄露），它是指程序在运行中出现泄露，如果程序被关闭掉的话，操作系统会帮助释放泄露的内存。</p>
            <p>&nbsp;&nbsp;&nbsp; 栈：在函数调用时第一个进栈的主函数中的下一条指令（函数调用语句的下一条可执行语句）的地址然后是函数 的各个参数，在大多数的C编译器中，参数是由右往左入栈，然后是函数中的局部变量。&nbsp;&nbsp;</p>
            <p><strong>一、预备知识—程序的内存分配</strong></p>
            <p>一个由c/C++编译的程序占用的内存分为以下几个部分 <br>1、栈区（stack）— 由编译器自动分配释放 ，存放函数的参数值，局部变量的值等。其操作方式类似于数据结构中的栈。 <br>2、堆区（heap） — 一般由程序员分配释放， 若程序员不释放，程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事，分配方式倒是类似于链表，呵呵。 <br>3、全局区（静态区）（static）—，全局变量和静态变量的存储是放在一块的，初始化的全局变量和静态变量在一块区域， 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 <br>4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放 <br>5、程序代码区—存放函数体的二进制代码。 <br><br>有些说法，把3，4合在一起，也有的把3分成自由存储区（malloc/free）和全局/静态存储区。 <br>这与编译器和操作系统有关。 <br><br>例子程序 <br>这是一个前辈写的，非常详细 <br>//main.cpp <br>int a = 0; 全局初始化区 <br>char *p1; 全局未初始化区 <br>main() <br>{ <br>int b; 栈 <br>char s[] = "abc"; 栈 <br>char *p2; 栈 <br>char *p3 = "123456"; 123456\0在常量区，p3在栈上。 <br>static int c =0； 全局（静态）初始化区 <br>p1 = (char *)malloc(10); <br>p2 = (char *)malloc(20); <br>分配得来得10和20字节的区域就在堆区。 <br>strcpy(p1, "123456"); 123456\0放在常量区，编译器可能会将它与p3所指向的"123456"优化成一个地方。 <br>}</p>
            <p><br><strong>二、堆和栈的理论知识</strong></p>
            <p>2.1申请方式 <br>栈：由系统自动分配。 例如，声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间 <br>堆：需要程序员自己申请，并指明大小，在c中malloc函数 <br>如p1 = (char *)malloc(10); <br>在C++中用new运算符 <br>如p2 = (char *)malloc(10); <br>但是注意p1、p2本身是在栈中的。</p>
            <p>2.2申请后系统的响应 <br>&nbsp;&nbsp;&nbsp; 栈：只要栈的剩余空间大于所申请空间，系统将为程序提供内存，否则将报异常提示栈溢出。 <br>&nbsp;&nbsp;&nbsp; 堆：首先应该知道操作系统有一个记录空闲内存地址的链表，当系统收到程序的申请时，会遍历该链表，寻找第一个空间大于所申请空间的堆结点，然后将该结点从空闲结点链表中删除，并将该结点的空间分配给程序，另外，对于大多数系统，会在这块内存空间中的首地址处记录本次分配的大小，这样，代码中的delete语句才能正确的释放本内存空间。另外，由于找到的堆结点的大小不一定正好等于申请的大小，系统会自动的将多余的那部分重新放入空闲链表中。</p>
            <p>2.3申请大小的限制 <br>&nbsp;&nbsp;&nbsp; 栈：在Windows下,栈是向低地址扩展的数据结构，是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的，在 WINDOWS下，栈的大小是2M（也有的说是1M，总之是一个编译时就确定的常数），如果申请的空间超过栈的剩余空间时，将提示overflow。因此，能从栈获得的空间较小。 <br>&nbsp;&nbsp;&nbsp; 堆：堆是向高地址扩展的数据结构，是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的，自然是不连续的，而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见，堆获得的空间比较灵活，也比较大。</p>
            <p>2.4申请效率的比较： <br>&nbsp;&nbsp;&nbsp; 栈由系统自动分配，速度较快。但程序员是无法控制的。 <br>&nbsp;&nbsp;&nbsp; 堆是由new分配的内存，一般速度比较慢，而且容易产生内存碎片，不过用起来最方便。 <br>&nbsp;&nbsp;&nbsp; 另外，在WINDOWS下，最好的方式是用VirtualAlloc分配内存，他不是在堆，也不是在栈是直接在进程的地址空间中保留一快内存，虽然用起来最不方便。但是速度快，也最灵活。</p>
            <p>2.5堆和栈中的存储内容 <br>&nbsp;&nbsp;&nbsp; 栈： 在函数调用时，第一个进栈的是主函数的下一条指令（函数调用语句的下一条可执行语句）的地址，然后是函数的各个参数，在大多数的C编译器中，参数是由右往左入栈的，然后是函数中的局部变量。注意静态变量是不入栈的。当本次函数调用结束后，局部变量先出栈，然后是参数，最后栈顶指针指向最开始存的地址，也就是主函数中的下一条指令，程序由该点继续运行。 <br>&nbsp;&nbsp;&nbsp; 堆：一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。</p>
            <p>2.6存取效率的比较 <br>char s1[] = "aaaaaaaaaaaaaaa"; <br>char *s2 = "bbbbbbbbbbbbbbbbb"; <br>aaaaaaaaaaa是在运行时刻赋值的； <br>而bbbbbbbbbbb是在编译时就确定的； <br>但是，在以后的存取中，在栈上的数组比指针所指向的字符串(例如堆)快。 <br>比如： <br>＃i nclude <br>void main() <br>{ <br>char a = 1; <br>char c[] = "1234567890"; <br>char *p ="1234567890"; <br>a = c[1]; <br>a = p[1]; <br>return; <br>} <br>对应的汇编代码 <br>10: a = c[1]; <br>00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] <br>0040106A 88 4D FC mov byte ptr [ebp-4],cl <br>11: a = p[1]; <br>0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] <br>00401070 8A 42 01 mov al,byte ptr [edx+1] <br>00401073 88 45 FC mov byte ptr [ebp-4],al <br>第一种在读取时直接就把字符串中的元素读到寄存器cl中，而第二种则要先把指针值读到edx中，在根据edx读取字符，显然慢了。</p>
            <p>2.7小结： <br>堆和栈的区别可以用如下的比喻来看出： <br>&nbsp;&nbsp;&nbsp; 使用栈就象我们去饭馆里吃饭，只管点菜（发出申请）、付钱、和吃（使用），吃饱了就走，不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作，他的好处是快捷，但是自由度小。 <br>&nbsp;&nbsp;&nbsp; 使用堆就象是自己动手做喜欢吃的菜肴，比较麻烦，但是比较符合自己的口味，而且自由度大。 <br><br>堆和栈的区别主要分： <br>&nbsp;&nbsp;&nbsp; 操作系统方面的堆和栈，如上面说的那些，不多说了。 <br>&nbsp;&nbsp;&nbsp; 还有就是数据结构方面的堆和栈，这些都是不同的概念。这里的堆实际上指的就是（满足堆性质的）优先队列的一种数据结构，第1个元素有最高的优先权；栈实际上就是满足先进后出的性质的数学或数据结构。虽然堆栈，堆栈的说法是连起来叫，但是他们还是有很大区别的，连着叫只是由于历史的原因。</p>
            <p>————————————————————————————————————————————<br>2.8 补充知识：&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 堆(heap)和栈(stack)是C/C++编程不可避免会碰到的两个基本概念。首先，这两个概念都可以在讲数据结构的书中找到，他们都是基本的数据结构，虽然栈更为简单一些。 <br>&nbsp;&nbsp;&nbsp; 在具体的C/C++编程框架中，这两个概念并不是并行的。对底层机器代码的研究可以揭示，栈是机器系统提供的数据结构，而堆则是C/C++函数库提供的。 <br>&nbsp;&nbsp;&nbsp; 具体地说，现代计算机(串行执行机制)，都直接在代码底层支持栈的数据结构。这体现在，有专门的寄存器指向栈所在的地址，有专门的机器指令完成数据入栈出栈的操作。这种机制的特点是效率高，支持的数据有限，一般是整数，指针，浮点数等系统直接支持的数据类型，并不直接支持其他的数据结构。因为栈的这种特点， 对栈的使用在程序中是非常频繁的。对子程序的调用就是直接利用栈完成的。机器的call指令里隐含了把返回地址推入栈，然后跳转至子程序地址的操作，而子 程序中的ret指令则隐含从堆栈中弹出返回地址并跳转之的操作。C/C++中的自动变量是直接利用栈的例子，这也就是为什么当函数返回时，该函数的自动变 量自动失效的原因(因为堆栈恢复了调用前的状态)。 <br>&nbsp;&nbsp;&nbsp; 和栈不同，堆的数据结构并不是由系统(无论是机器系统还是操作系统)支持的，而是由函数库提供的。基本的malloc/realloc/free函数维护了一套内部的堆数据结构。当程序使用这些函数去获得新的内存空间时，这套函数首先试图从内部堆中寻找可用的内存空间，如果没有可以使用的内存空间，则试图利用系统调用来动态增加程序数据段的内存大小，新分配得到的空间首先被组织进内部堆中去，然后再以适当的形式返回给调用者。当程序释放分配的内存空间时，这片内存空间被返回内部堆结构中，可能会被适当的处理(比如和其他空闲空间合并成更大 的空闲空间)，以更适合下一次内存分配申请。这套复杂的分配机制实际上相当于一个内存分配的缓冲池(Cache)，使用这套机制有如下若干原因： <br>1. 系统调用可能不支持任意大小的内存分配。有些系统的系统调用只支持固定大小及其倍数的内存请求(按页分配)；这样的话对于大量的小内存分类来说会造成浪费。 <br>2. 系统调用申请内存可能是代价昂贵的。系统调用可能涉及用户态和核心态的转换。<br>3. 没有管理的内存分配在大量复杂内存的分配释放操作下很容易造成内存碎片。</p>
            <p>堆和栈的对比 <br>从以上知识可知，栈是系统提供的功能，特点是快速高效，缺点是有限制，数据不灵活；而堆是函数库提供的功能，特点是灵活方便，数据适应面广泛，但是效率有一 定降低。栈是系统数据结构，对于进程/线程是唯一的；堆是函数库内部数据结构，不一定唯一。不同堆分配的内存逻辑上无法互相操作。栈空间分静态分配和动态 分配两种。静态分配是编译器完成的，比如自动变量(auto)的分配。动态分配由alloca函数完成。栈的动态分配无需释放(是自动的)，也就没有释放 函数。为可移植的程序起见，栈的动态分配操作是不被鼓励的！堆空间的分配总是动态的，虽然程序结束时所有的数据空间都会被释放回系统，但是精确的申请内存 /释放内存匹配是良好程序的基本要素。 <br>堆和栈究竟有什么区别？ <br>&nbsp;&nbsp;&nbsp;&nbsp; 主要的区别由以下几点： <br>&nbsp;&nbsp;&nbsp;&nbsp; 1、管理方式不同； <br>&nbsp;&nbsp;&nbsp;&nbsp; 2、空间大小不同； <br>&nbsp;&nbsp;&nbsp;&nbsp; 3、能否产生碎片不同； <br>&nbsp;&nbsp;&nbsp;&nbsp; 4、生长方向不同； <br>&nbsp;&nbsp;&nbsp;&nbsp; 5、分配方式不同； <br>&nbsp;&nbsp;&nbsp;&nbsp; 6、分配效率不同； <br>&nbsp;&nbsp;&nbsp;&nbsp; 管理方式：对于栈来讲，是由编译器自动管理，无需我们手工控制；对于堆来说，释放工作由程序员控制，容易产生memory leak。 <br>&nbsp;&nbsp;&nbsp;&nbsp; 空间大小：一般来讲在32位系统下，堆内存可以达到4G的空间，从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲，一般都是有一定的空间大小的，例如，在VC6下面，默认的栈空间大小是1M（好像是，记不清楚了）。当然，我们可以修改：&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp; 打开工程，依次操作菜单如下：Project-&gt;Setting-&gt;Link，在Category 中选中Output，然后在Reserve中设定堆栈的最大值和commit。 <br>注意：reserve最小值为4Byte；commit是保留在虚拟内存的页文件里面，它设置的较大会使栈开辟较大的值，可能增加内存的开销和启动时间。 <br>&nbsp;&nbsp;&nbsp;&nbsp; 碎片问题：对于堆来讲，频繁的new/delete势必会造成内存空间的不连续，从而造成大量的碎片，使程序效率降低。对于栈来讲，则不会存在这个问题， 因为栈是先进后出的队列，他们是如此的一一对应，以至于永远都不可能有一个内存块从栈中间弹出，在他弹出之前，在他上面的后进的栈内容已经被弹出，详细的 可以参考数据结构，这里我们就不再一一讨论了。 <br>&nbsp;&nbsp;&nbsp;&nbsp; 生长方向：对于堆来讲，生长方向是向上的，也就是向着内存地址增加的方向；对于栈来讲，它的生长方向是向下的，是向着内存地址减小的方向增长。 <br>&nbsp;&nbsp;&nbsp;&nbsp; 分配方式：堆都是动态分配的，没有静态分配的堆。栈有2种分配方式：静态分配和动态分配。静态分配是编译器完成的，比如局部变量的分配。动态分配由alloca函数进行分配，但是栈的动态分配和堆是不同的，他的动态分配是由编译器进行释放，无需我们手工实现。</p>
            <p>&nbsp;&nbsp;&nbsp; 分配效率：栈是机器系统提供的数据结构，计算机会在底层对栈提供支持：分配专门的寄存器存放栈的地址，压栈出栈都有专门的指令执行，这就决定了栈的效率比 较高。堆则是C/C++函数库提供的，它的机制是很复杂的，例如为了分配一块内存，库函数会按照一定的算法（具体的算法可以参考数据结构/操作系统）在堆 内存中搜索可用的足够大小的空间，如果没有足够大小的空间（可能是由于内存碎片太多），就有可能调用系统功能去增加程序数据段的内存空间，这样就有机会分 到足够大小的内存，然后进行返回。显然，堆的效率比栈要低得多。 <br>&nbsp;&nbsp;&nbsp;&nbsp; 从这里我们可以看到，堆和栈相比，由于大量new/delete的使用，容易造成大量的内存碎片；由于没有专门的系统支持，效率很低；由于可能引发用户态 和核心态的切换，内存的申请，代价变得更加昂贵。所以栈在程序中是应用最广泛的，就算是函数的调用也利用栈去完成，函数调用过程中的参数，返回地址， EBP和局部变量都采用栈的方式存放。所以，我们推荐大家尽量用栈，而不是用堆。 <br>&nbsp;&nbsp;&nbsp;&nbsp; 虽然栈有如此众多的好处，但是由于和堆相比不是那么灵活，有时候分配大量的内存空间，还是用堆好一些。 <br>&nbsp;&nbsp;&nbsp;&nbsp; 无论是堆还是栈，都要防止越界现象的发生（除非你是故意使其越界），因为越界的结果要么是程序崩溃，要么是摧毁程序的堆、栈结构，产生以想不到的结果,就 算是在你的程序运行过程中，没有发生上面的问题，你还是要小心，说不定什么时候就崩掉，那时候debug可是相当困难的：）<br><br><br></p>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/deane/aggbug/113603.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2010-04-26 15:59 <a href="http://www.cppblog.com/deane/articles/113603.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>QueryPerformanceFrequency用法</title><link>http://www.cppblog.com/deane/articles/113151.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Wed, 21 Apr 2010 09:33:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/113151.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/113151.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/113151.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/113151.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/113151.html</trackback:ping><description><![CDATA[&nbsp; <strong><span><br></span></strong><span><a href="javascript:;"><span><span>转载<br></span></span></a></span>
<p align=left><span><br>精确获取时间：</span></p>
<p align=left><span>QueryPerformanceFrequency() -</span><span> </span><span>基本介绍</span></p>
<p align=left><span>类型：</span><span>Win32API</span></p>
<p align=left><span>原型：</span><span>BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);</span></p>
<p align=left><span>作用：返回硬件支持的高精度计数器的频率。</span></p>
<p align=left><span>返回值：非零，硬件支持高精度计数器；零，硬件不支持，读取失败。</span></p>
<p align=left><span>QueryPerformanceFrequency() -</span><span> </span><span>技术特点</span></p>
<p align=left><span>供</span><span>WIN9X</span><span>使用的高精度定时器：</span><span>QueryPerformanceFrequency()</span><span>和</span><span>QueryPerformanceCounter()</span><span>，要求计算机从硬件上支持高精度定时器。需包含</span><span>windows.h</span><span>头文件。</span></p>
<p align=left><span>函数的原形是：</span></p>
<p align=left><span>BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);</span></p>
<p align=left><span>BOOL QueryPerformanceCounter (LARGE_INTEGER *lpCount);</span></p>
<p align=left><span>数据类型</span><span>LARGEINTEGER</span><span>既可以是一个作为</span><span>8</span><span>字节长的整数，也可以是作为两个</span><span>4</span><span>字节长的整数的联合结构，其具体用法根据编译器是否支持</span><span>64</span><span>位而定。该类型的定义如下：</span></p>
<p align=left><span>typeef union _ LARGE_INTEGER</span></p>
<p align=left><span>{</span></p>
<p align=left><span>struct</span></p>
<p align=left><span>{</span></p>
<p align=left><span>DWORD LowPart;</span></p>
<p align=left><span>LONG HighPart;</span></p>
<p align=left><span>};</span></p>
<p align=left><span>LONGLONG QuadPart;</span></p>
<p align=left><span>} LARGE_INTEGER;</span></p>
<p align=left><span>在定时前应该先调用</span><span>QueryPerformanceFrequency()</span><span>函数获得机器内部计时器的时钟频率。接着在需要严格计时的事件发生前和发生之后分别调用</span><span>QueryPerformanceCounter()</span><span>，利用两次获得的计数之差和时钟频率，就可以计算出事件经历的精确时间。</span></p>
<p align=left><span>测试</span><span>Sleep</span><span>的精确时间：</span></p>
<p align=left><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></p>
<p align=left><span>#include</span><span> </span><span>&lt;windows.h&gt;</span></p>
<p align=left><span>void</span><span> </span><span>main</span><span>()</span></p>
<p align=left><span>{</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>&nbsp;</span><span>LARGE_INTEGER</span><span> </span><span>nFreq</span><span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>&nbsp;</span><span>LARGE_INTEGER</span><span> </span><span>nBeginTime</span><span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>&nbsp;</span><span>LARGE_INTEGER</span><span> </span><span>nEndTime</span><span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>&nbsp;</span><span>double</span><span> </span><span>time</span><span>;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>&nbsp;</span><span>QueryPerformanceFrequency</span><span>(&amp;</span><span>nFreq</span><span>);</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>&nbsp;</span><span>QueryPerformanceCounter</span><span>(&amp;</span><span>nBeginTime</span><span>);&nbsp;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>&nbsp;</span><span>Sleep</span><span>(1000);</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>&nbsp;</span><span>QueryPerformanceCounter</span><span>(&amp;</span><span>nEndTime</span><span>);</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>&nbsp;</span><span>time</span><span>=(</span><span>double</span><span>)(</span><span>nEndTime</span><span>.</span><span>QuadPart</span><span>-</span><span>nBeginTime</span><span>.</span><span>QuadPart</span><span>)/(</span><span>double</span><span>)</span><span>nFreq</span><span>.</span><span>QuadPart</span><span>;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>&nbsp;</span><span>printf</span><span>(</span><span>"%f\n"</span><span>,</span><span>time</span><span>);</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>&nbsp;</span><span>Sleep</span><span>(1000);</span></p>
<p align=left><span>system</span><span>(</span><span>"Pause"</span><span>);</span></p>
<p align=left><span>}</span></p>
<p align=left><span>结果为</span></p>
<p align=left><span>0.999982</span></p>
<p align=left><span>1.000088</span></p>
<p align=left><span>1.000200</span></p>
<p align=left><span>等，所以<span>Sleep</span>的精度还是比较低的。<br><br><br></span></p>
<img src ="http://www.cppblog.com/deane/aggbug/113151.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2010-04-21 17:33 <a href="http://www.cppblog.com/deane/articles/113151.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>__cdecl</title><link>http://www.cppblog.com/deane/articles/113090.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Tue, 20 Apr 2010 10:13:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/113090.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/113090.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/113090.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/113090.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/113090.html</trackback:ping><description><![CDATA[<h1>&nbsp;</h1>
<div id=lemmaContent>
<div class=spctrl></div>
　　_cdecl 是C Declaration的缩写，表示C语言默认的函数调用方法：所有参数从右到左依次入栈，这些参数由调用者清除，称为手动清栈。被调用函数不需要求调用者传递多少参数，调用者传递过多或者过少的参数，甚至完全不同的参数都不会产生编译阶段的错误。<br>
<div class=spctrl></div>
　　_stdcall 是Standard Call的缩写，是C++的标准调用方式：所有参数从右到左依次入栈，如果是调用类成员的话，最后一个入栈的是this指针。这些堆栈中的参数由被调用的函数在返回后清除，使用的指令是 retn X，X表示参数占用的字节数，CPU在ret之后自动弹出X个字节的堆栈空间。称为自动清栈。函数在编译的时候就必须确定参数个数，并且调用者必须严格的控制参数的生成，不能多，不能少，否则返回后会出错。<br>
<div class=spctrl></div>
　　PASCAL 是Pascal语言的函数调用方式，也可以在C/C++中使用，参数压栈顺序与前两者相反。返回时的清栈方式忘记了。。。<br>
<div class=spctrl></div>
　　_fastcall 是编译器指定的快速调用方式。由于大多数的函数参数个数很少，使用堆栈传递比较费时。因此_fastcall通常规定将前两个（或若干个）参数由寄存器传递，其余参数还是通过堆栈传递。不同编译器编译的程序规定的寄存器不同。返回方式和_stdcall相当。<br>
<div class=spctrl></div>
　　_thiscall 是为了解决类成员调用中this指针传递而规定的。_thiscall要求把this指针放在特定寄存器中，该寄存器由编译器决定。VC使用ecx，Borland的C++编译器使用eax。返回方式和_stdcall相当。<br>
<div class=spctrl></div>
　　_fastcall 和 _thiscall涉及的寄存器由编译器决定，因此不能用作跨编译器的接口。所以Windows上的COM对象接口都定义为_stdcall调用方式。<br>
<div class=spctrl></div>
　　C中不加说明默认函数为_cdecl方式（C中也只能用这种方式），C++也一样，但是默认的调用方式可以在IDE环境中设置。<br>
<div class=spctrl></div>
　　带有可变参数的函数必须且只能使用_cdecl方式，例如下面的函数:<br>
<div class=spctrl></div>
　　int printf(char * fmtStr, ...);<br>
<div class=spctrl></div>
　　int scanf(char * fmtStr, ...);<br>
<div class=spctrl></div>
　　*/<br>
<div class=spctrl></div>
　　调用约定： <br>
<div class=spctrl></div>
　　__cdecl __fastcall与 <a href="http://baike.baidu.com/view/1276580.htm" target=_blank><u><font color=#0000ff>__stdcall</font></u></a>，三者都是调用约定(Calling convention)，它决定以下内容：1)函数参数的压栈顺序，2)由调用者还是被调用者把参数弹出栈，3)以及产生函数修饰名的方法。 <br>
<div class=spctrl></div>
　　1、__stdcall调用约定：函数的参数自右向左通过栈传递，被调用的函数在返回前清理传送参数的内存栈， <br>
<div class=spctrl></div>
　　2、_cdecl是C和C＋＋程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码，所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。注意：对于可变参数的成员函数，始终使用__cdecl的转换方式。 <br>
<div class=spctrl></div>
　　3、__fastcall调用约定：它是通过寄存器来传送参数的（实际上，它用ECX和EDX传送前两个双字（DWORD）或更小的参数，剩下的参数仍旧自右向左压栈传送，被调用的函数在返回前清理传送参数的内存栈）。 <br>
<div class=spctrl></div>
　　4、thiscall仅仅应用于"C++"成员函数。this指针存放于CX寄存器，参数从右到左压。thiscall不是关键词，因此不能被程序员指定。 <br>
<div class=spctrl></div>
　　5、naked call采用1-4的调用约定时，如果必要的话，进入函数时编译器会产生代码来保存ESI，EDI，EBX，EBP寄存器，退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。naked call不是类型修饰符，故必须和_declspec共同使用。 <br>
<div class=spctrl></div>
　　调用约定可以通过工程设置：Setting...\C/C++ \Code Generation项进行选择，缺省状态为__cdecl。 <br>
<div class=spctrl></div>
　　名字修饰约定： <br>
<div class=spctrl></div>
　　1、修饰名(Decoration name)："C"或者"C++"函数在内部（编译和链接）通过修饰名识别 <br>
<div class=spctrl></div>
　　2、C编译时函数名修饰约定规则： <br>
<div class=spctrl></div>
　　__stdcall调用约定在输出函数名前加上一个下划线前缀，后面加上一个"@"符号和其参数的字节数，格式为_functionname@number,例如 ：function(int a, int b)，其修饰名为：_function@8 <br>
<div class=spctrl></div>
　　__cdecl调用约定仅在输出函数名前加上一个下划线前缀，格式为_functionname。 <br>
<div class=spctrl></div>
　　__fastcall调用约定在输出函数名前加上一个"@"符号，后面也是一个"@"符号和其参数的字节数，格式为@functionname@number。 <br><br><br><br><br></div>
<img src ="http://www.cppblog.com/deane/aggbug/113090.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2010-04-20 18:13 <a href="http://www.cppblog.com/deane/articles/113090.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>线程基本概念</title><link>http://www.cppblog.com/deane/articles/113089.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Tue, 20 Apr 2010 10:12:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/113089.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/113089.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/113089.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/113089.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/113089.html</trackback:ping><description><![CDATA[<p style="MARGIN: 0in 9pt 0pt 0.5in; TEXT-INDENT: -0.5in"><span style="FONT-FAMILY: '微软雅黑','sans-serif'"><strong>线程基本概念</strong></span></p>
<p style="MARGIN: 12pt 9pt 3pt 0in"><strong>1.<span style="FONT-FAMILY: 幼圆">线程的组成</span></strong></p>
<p style="MARGIN: 0in 9pt 0pt 0in">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (1)<span style="FONT-FAMILY: '微软雅黑','sans-serif'">线程内核对象：用于管理线程及存储线程的统计信息</span></p>
<p style="MARGIN: 0in 9pt 0pt 0in">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;(2)<span style="FONT-FAMILY: '微软雅黑','sans-serif'">线程栈：维护线程执行时需要的函数参数和局部变量。　线程栈所需的内存是从进程中分配而得</span><span style="FONT-FAMILY: '微软雅黑','sans-serif'">的，其大小默认是</span>1M.</p>
<p style="MARGIN: 0in 9pt 0pt 0in">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p style="MARGIN: 0in 9pt 0pt 0in">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<span style="FONT-FAMILY: '微软雅黑','sans-serif'">每个线程都有自已独立的线程栈。</span></p>
<p style="MARGIN: 0in 9pt 0pt 0in">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<span style="FONT-FAMILY: '微软雅黑','sans-serif'">进程不执行任何代码，所有的代码都是由线程执行的。进程相当于一个装载线程的容器。</span></p>
<p style="MARGIN: 0in 9pt 0pt 0in">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<span style="FONT-FAMILY: '微软雅黑','sans-serif'">线程共享进程的地址空间和数据，如内核对象句柄</span>(<span style="FONT-FAMILY: '微软雅黑','sans-serif'">内核对象句柄只能依附于某个进程而不是某个线程</span>)</p>
<p style="MARGIN: 0in 9pt 0pt 0in">&nbsp;</p>
<p style="MARGIN: 12pt 9pt 3pt 0in"><strong>2.<span style="FONT-FAMILY: 幼圆">线程函数原型</span></strong></p>
<p style="MARGIN: 0in 9pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD WINAPI ThreadFunc(PVOID pvParam)</p>
<p style="MARGIN: 0in 9pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p style="MARGIN: 0in 9pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; DWORD dwResult = 0;</p>
<p style="MARGIN: 0in 9pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; ...</p>
<p style="MARGIN: 0in 9pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; return(dwResult);</p>
<p style="MARGIN: 0in 9pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p style="MARGIN: 0in 9pt 0pt">&nbsp;</p>
<p style="MARGIN: 0in 9pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The system allocates memory out of the process' address space for use by the thread's stack.</p>
<p style="MARGIN: 0in 9pt 0pt">&nbsp;</p>
<p style="MARGIN: 12pt 9pt 3pt 0in"><strong>3.<span style="FONT-FAMILY: 幼圆">终止线程</span></strong></p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.<span style="FONT-FAMILY: '微软雅黑','sans-serif'">线程正常退出。系统会对线程函数内创建的所有对象调用析构函数。</span></p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.ExitThread(). <span style="FONT-FAMILY: '微软雅黑','sans-serif'">线程退出，　系统会清理线程栈。</span> <span style="FONT-FAMILY: '微软雅黑','sans-serif'">但是系统不会对线程函数内创建的所有对象调用析构函数。</span></p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.TerminateThread().<span style="FONT-FAMILY: '微软雅黑','sans-serif'">线程异步退出，系统不清理线程栈。只到拥有该线程的进程退出时才清理线程栈。</span></p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="FONT-FAMILY: '微软雅黑','sans-serif'">　该函数是个异步函数，它只会告诉系统去杀掉某个线程，但是系统不会保证当该函数返回时线程</span><span style="FONT-FAMILY: '微软雅黑','sans-serif'">立刻终止。</span></p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="FONT-FAMILY: '微软雅黑','sans-serif'">　因此我们如果我们要确认线程已经终止了，则需要用</span>WaitForSingleObject()<span style="FONT-FAMILY: '微软雅黑','sans-serif'">来等待线程结束。</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.<span style="FONT-FAMILY: '微软雅黑','sans-serif'">内核对象由进程所拥有，用户对象由线程拥有。线程可拥有两种用户对象：</span>Windows<span style="FONT-FAMILY: '微软雅黑','sans-serif'">和</span>Hook.</p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.<span style="FONT-FAMILY: '微软雅黑','sans-serif'">线程终止后，线程所拥有的用户对象会被系统释放。</span> </p>
<p style="MARGIN: 0in 9pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;6.GetExitCodeThread() //<span style="FONT-FAMILY: '微软雅黑','sans-serif'">检查线程是否已终止</span></p>
<p style="MARGIN: 0in 9pt 0pt">&nbsp;</p>
<p style="MARGIN: 12pt 9pt 3pt 0in"><strong>4.<span style="FONT-FAMILY: 幼圆">线程内部细节</span></strong></p>
<p style="MARGIN: 0in 5pt 0pt">1.CreateThread <span style="FONT-FAMILY: '微软雅黑','sans-serif'">和</span> _beginthreadex <span style="FONT-FAMILY: '微软雅黑','sans-serif'">区别：</span></p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;CreateThread<span style="FONT-FAMILY: '微软雅黑','sans-serif'">是系统</span>API,_beginthreadex<span style="FONT-FAMILY: '微软雅黑','sans-serif'">是</span>CRT(C Run Time Library <span style="FONT-FAMILY: '微软雅黑','sans-serif'">运行时库</span>)<span style="FONT-FAMILY: '微软雅黑','sans-serif'">函数</span>.&nbsp;&nbsp;&nbsp;&nbsp; _beginthreadex<span style="FONT-FAMILY: '微软雅黑','sans-serif'">内</span><span style="FONT-FAMILY: '微软雅黑','sans-serif'">部会调用</span>CreateThread<span style="FONT-FAMILY: '微软雅黑','sans-serif'">函数。</span>&nbsp;</p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp; _endthreadex<span style="FONT-FAMILY: '微软雅黑','sans-serif'">会释放</span>_beginthreadex<span style="FONT-FAMILY: '微软雅黑','sans-serif'">为</span>tiddata<span style="FONT-FAMILY: '微软雅黑','sans-serif'">结构分配的内存。</span></p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;</p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp; <span style="FONT-FAMILY: '微软雅黑','sans-serif'">如果线程函数中调用了</span>CRT<span style="FONT-FAMILY: '微软雅黑','sans-serif'">函数</span>(<span style="FONT-FAMILY: '微软雅黑','sans-serif'">注：不是全部</span>CRT<span style="FONT-FAMILY: '微软雅黑','sans-serif'">函数　只是其中一部分函数</span>),<span style="FONT-FAMILY: '微软雅黑','sans-serif'">则该线程函数必须</span><span style="FONT-FAMILY: '微软雅黑','sans-serif'">由</span>_beginthreadex<span style="FONT-FAMILY: '微软雅黑','sans-serif'">而不是</span>CreateThread<span style="FONT-FAMILY: '微软雅黑','sans-serif'">函数创建。否则会产生内存泄露。</span></p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp; &nbsp; <span style="FONT-FAMILY: '微软雅黑','sans-serif'">如果在除主线程之外的任何线程中进行一下操作，你就应该使用多线程版本的</span>C runtime library,<span style="FONT-FAMILY: '微软雅黑','sans-serif'">并使</span><span style="FONT-FAMILY: '微软雅黑','sans-serif'">用</span>_beginthreadex<span style="FONT-FAMILY: '微软雅黑','sans-serif'">和</span>_endthreadex<span style="FONT-FAMILY: '微软雅黑','sans-serif'">：</span></p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (1) <span style="FONT-FAMILY: '微软雅黑','sans-serif'">使用</span>malloc()<span style="FONT-FAMILY: '微软雅黑','sans-serif'">和</span>free()<span style="FONT-FAMILY: '微软雅黑','sans-serif'">，或是</span>new<span style="FONT-FAMILY: '微软雅黑','sans-serif'">和</span>delete</p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (2) <span style="FONT-FAMILY: '微软雅黑','sans-serif'">使用</span>stdio.h<span style="FONT-FAMILY: '微软雅黑','sans-serif'">或</span>io.h<span style="FONT-FAMILY: '微软雅黑','sans-serif'">里面声明的任何函数</span></p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (3) <span style="FONT-FAMILY: '微软雅黑','sans-serif'">使用浮点变量或浮点运算函数</span></p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (4) <span style="FONT-FAMILY: '微软雅黑','sans-serif'">调用任何一个使用了静态缓冲区的</span>runtime<span style="FONT-FAMILY: '微软雅黑','sans-serif'">函数，比如</span>:asctime(),strtok()<span style="FONT-FAMILY: '微软雅黑','sans-serif'">或</span>rand()</p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;</p>
<p style="MARGIN: 0in 5pt 0pt">2._beginthreadex<span style="FONT-FAMILY: '微软雅黑','sans-serif'">和</span>_beginthread<span style="FONT-FAMILY: '微软雅黑','sans-serif'">区别</span></p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;</p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;_beginthreadex<span style="FONT-FAMILY: '微软雅黑','sans-serif'">内部会自动调用</span> _endthreadex.</p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;_beginthread<span style="FONT-FAMILY: '微软雅黑','sans-serif'">内部会自动调用</span>_endthread.</p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p style="MARGIN: 0in 5pt 0pt">&nbsp;&nbsp;&nbsp;&nbsp;_endthread<span style="FONT-FAMILY: '微软雅黑','sans-serif'">内部会自动调用</span>CloseHandle<span style="FONT-FAMILY: '微软雅黑','sans-serif'">关闭当前</span>Thread<span style="FONT-FAMILY: '微软雅黑','sans-serif'">内核对象的句柄</span>,<span style="FONT-FAMILY: '微软雅黑','sans-serif'">所以在用</span>_beginthread <span style="FONT-FAMILY: '微软雅黑','sans-serif'">时</span><span style="FONT-FAMILY: '微软雅黑','sans-serif'">我们不需要在主线程中调用</span>CloseHandle<span style="FONT-FAMILY: '微软雅黑','sans-serif'">来关闭子线程的句柄。</span>&nbsp;</p>
<p style="MARGIN: 0in 9pt 0pt">&nbsp;&nbsp; _endthreadex<span style="FONT-FAMILY: '微软雅黑','sans-serif'">相比</span>_endthread<span style="FONT-FAMILY: '微软雅黑','sans-serif'">而言更安全。它不会自动关闭当前</span>Thread<span style="FONT-FAMILY: '微软雅黑','sans-serif'">内核对象的句柄。所以在用</span>_beginthreadex<span style="FONT-FAMILY: '微软雅黑','sans-serif'">时我们需要用</span>CloseHandle<span style="FONT-FAMILY: '微软雅黑','sans-serif'">来关闭子线程的句柄。</span></p>
<p style="MARGIN: 0in 9pt 0pt 0in">&nbsp;</p>
<p style="MARGIN: 12pt 9pt 3pt 0in"><strong>5.<span style="FONT-FAMILY: 幼圆">伪句柄和真实句柄</span></strong></p>
<p style="MARGIN: 0in 9pt 0pt 0.25in; TEXT-INDENT: 0in">&nbsp;</p>
<p style="MARGIN: 0in 9pt 0pt 5pt; TEXT-INDENT: 13pt">1.<span style="FONT-FAMILY: '微软雅黑','sans-serif'">伪句柄</span>(Pseudohandle):</p>
<p style="MARGIN: 0in 5pt 0pt 0.25in; TEXT-INDENT: 0.25in">&nbsp;</p>
<p style="MARGIN: 0in 5pt 0pt 0.25in; TEXT-INDENT: 0.25in">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HANDLE GetCurrentProcess();</p>
<p style="MARGIN: 0in 5pt 0pt 0.25in; TEXT-INDENT: 0.25in">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HANDLE GetCurrentThread();&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; </p>
<p style="MARGIN: 0in 5pt 0pt 0.25in; TEXT-INDENT: 0.25in">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p style="MARGIN: 0in 5pt 0pt 0.25in; TEXT-INDENT: 0.25in"><span style="FONT-FAMILY: '微软雅黑','sans-serif'">以上两个函数会返回指向线程或进程内核对象的伪句柄（其实以上两个函数返回的是一个常数</span><span style="FONT-FAMILY: '微软雅黑','sans-serif'">如</span>-1<span style="FONT-FAMILY: '微软雅黑','sans-serif'">）。所以伪句柄的值永远是指向当前线程或进程的。</span>&nbsp;</p>
<p style="MARGIN: 0in 5pt 0pt 0.25in; TEXT-INDENT: 0.25in"><span style="FONT-FAMILY: '微软雅黑','sans-serif'">如果把该值传给子进程，该值则代表当前子进程的伪句柄。所以把句柄传给子线程时一定要传</span><span style="FONT-FAMILY: '微软雅黑','sans-serif'">真时的句柄不能传伪句柄。</span>&nbsp;</p>
<p style="MARGIN: 0in 5pt 0pt 0.25in; TEXT-INDENT: 0.25in"><span style="FONT-FAMILY: '微软雅黑','sans-serif'">该句柄不会增加内核对象的引用计数，所以不需要调用</span>CloseHandle()<span style="FONT-FAMILY: '微软雅黑','sans-serif'">函数。</span></p>
<p style="MARGIN: 0in 5pt 0pt 0.25in; TEXT-INDENT: 0.25in">&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; </p>
<p style="MARGIN: 0in 9pt 0pt 5pt"><span style="FONT-FAMILY: '微软雅黑','sans-serif'">　　</span>2.<span style="FONT-FAMILY: '微软雅黑','sans-serif'">把伪句柄转换成真实句柄</span></p>
<p style="MARGIN: 0in 9pt 0pt 5pt">&nbsp;</p>
<p style="MARGIN: 0in 5pt 0pt 0.25in; TEXT-INDENT: 0.25in">DuplicateHandle<span style="FONT-FAMILY: '微软雅黑','sans-serif'">会增加内核对象的引用计数，所以要用</span>CloseHandle()<span style="FONT-FAMILY: '微软雅黑','sans-serif'">来关闭复制所得的对象句柄。</span></p>
<p style="MARGIN: 0in 5pt 0pt 0.25in; TEXT-INDENT: 0.25in">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p style="MARGIN: 12pt 9pt 3pt 0in"><strong>6.Common API</strong></p>
<p style="MARGIN: 0in 9pt 0pt 0.25in; TEXT-INDENT: 0in">&nbsp;</p>
<p style="MARGIN: 0in 5pt 0pt 0.25in; TEXT-INDENT: 0.25in">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD GetCurrentProcessId();</p>
<p style="MARGIN: 0in 5pt 0pt 0.25in; TEXT-INDENT: 0.25in">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD GetCurrentThreadId();</p>
<p style="MARGIN: 0in 5pt 0pt 0.25in; TEXT-INDENT: 0.25in">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HANDLE GetCurrentProcess();</p>
<p style="MARGIN: 0in 5pt 0pt 0.25in; TEXT-INDENT: 0.25in">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HANDLE GetCurrentThread();</p>
<p style="MARGIN: 0in 9pt 0pt 0.25in; TEXT-INDENT: 0in">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DuplicateHandle()</p>
<img src ="http://www.cppblog.com/deane/aggbug/113089.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2010-04-20 18:12 <a href="http://www.cppblog.com/deane/articles/113089.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>函数的调用规则(__cdecl,__stdcall,__fastcall,__pascal) </title><link>http://www.cppblog.com/deane/articles/113088.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Tue, 20 Apr 2010 10:11:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/113088.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/113088.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/113088.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/113088.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/113088.html</trackback:ping><description><![CDATA[<p><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 关于函数的调用规则（调用约定），大多数时候是不需要了解的，但是如果需要跨语言的编程，比如VC写的dll要delphi调用，则需要了解。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; microsoft的vc默认的是__cdecl方式，而windows API则是__stdcall，如果用vc开发dll给其他语言用，则应该指定__stdcall方式。堆栈由谁清除这个很重要，如果是要写汇编函数给C调用，一定要小心堆栈的清除工作，如果是__cdecl方式的函数，则函数本身（如果不用汇编写）则不需要关心保存参数的堆栈的清除，但是如果是__stdcall的规则，一定要在函数退出(ret)前恢复堆栈。<br>1.__cdecl<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所谓的C调用规则。按从右至左的顺序压参数入栈，由调用者把参数弹出栈。切记：对于传送参数的内存栈是由调用者来维护的。返回值在EAX中因此，对于象printf这样变参数的函数必须用这种规则。编译器在编译的时候对这种调用规则的函数生成修饰名的饿时候，仅在输出函数名前加上一个下划线前缀，格式为_functionname。 <br>2.__stdcall <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 按从右至左的顺序压参数入栈，由被调用者把参数弹出栈。_stdcall是Pascal程序的缺省调用方式，通常用于Win32 Api中，切记：函数自己在退出时清空堆栈，返回值在EAX中。　　__stdcall调用约定在输出函数名前加上一个下划线前缀，后面加上一个&#8220;@&#8221;符号和其参数的字节数，格式为<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#95;&#102;&#117;&#110;&#99;&#116;&#105;&#111;&#110;&#110;&#97;&#109;&#101;&#64;&#110;&#117;&#109;&#98;&#101;&#114;">_functionname@number</a>。如函数int func(int a, double b)的修饰名是<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#95;&#102;&#117;&#110;&#99;&#64;&#49;&#50;">_func@12</a>。<br>3.__fastcall<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __fastcall调用的主要特点就是快，因为它是通过寄存器来传送参数的（实际上，它用ECX和EDX传送前两个双字（DWORD）或更小的参数，剩下的参数仍旧自右向左压栈传送，被调用的函数在返回前清理传送参数的内存栈）。__fastcall调用约定在输出函数名前加上一个&#8220;@&#8221;符号，后面也是一个&#8220;@&#8221;符号和其参数的字节数，格式为@functionname@number。这个和__stdcall很象，唯一差别就是头两个参数通过寄存器传送。注意通过寄存器传送的两个参数是从左向右的，即第一个参数进ECX，第2个进EDX，其他参数是从右向左的入stack。返回仍然通过EAX.<br>4.__pascal<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这种规则从左向右传递参数，通过EAX返回，堆栈由被调用者清除</p>
<p>&nbsp;</p>
<p>5.__thiscall</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 仅仅应用于"C++"成员函数。this指针存放于CX寄存器，参数从右到左压。thiscall不是关键词，因此不能被程序员指定</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 调用约定可以通过工程设置：Setting...\C/C++ \Code Generation项进行选择，缺省状态为__cdecl。</p>
<p>&nbsp;</p>
<p>名字修饰约定：</p>
<p>1、修饰名(Decoration name)："C"或者"C++"函数在内部（编译和链接）通过修饰名识别<br>2、C编译时函数名修饰约定规则：<br>__stdcall调用约定在输出函数名前加上一个下划线前缀，后面加上一个"@"符号和其参数的字节数，格式为<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#95;&#102;&#117;&#110;&#99;&#116;&#105;&#111;&#110;&#110;&#97;&#109;&#101;&#64;&#110;&#117;&#109;&#98;&#101;&#114;">_functionname@number</a>,例如 ：function(int a, int b)，其修饰名为：<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#95;&#102;&#117;&#110;&#99;&#116;&#105;&#111;&#110;&#64;&#56;">_function@8</a><br>__cdecl调用约定仅在输出函数名前加上一个下划线前缀，格式为_functionname。<br>__fastcall调用约定在输出函数名前加上一个"@"符号，后面也是一个"@"符号和其参数的字节数，格式为@functionname@number。</p>
<p>3、C++编译时函数名修饰约定规则：<br>__stdcall调用约定：<br>1)、以"?"标识函数名的开始，后跟函数名；<br>2)、函数名后面以"@@YG"标识参数表的开始，后跟参数表；<br>3)、参数表以代号表示：<br>X--void ，<br>D--char，<br>E--unsigned char，<br>F--short，<br>H--int，<br>I--unsigned int，<br>J--long，<br>K--unsigned long，<br>M--float，<br>N--double，<br>_N--bool，<br>PA--表示指针，后面的代号表明指针类型，如果相同类型的指针连续出现，以"0"代替，一个"0"代表一次重复；<br>4)、参数表的第一项为该函数的返回值类型，其后依次为参数的数据类型,指针标识在其所指数据类型前； <br>5)、参数表后以"@Z"标识整个名字的结束，如果该函数无参数，则以"Z"标识结束。<br>其格式为"<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#63;&#102;&#117;&#110;&#99;&#116;&#105;&#111;&#110;&#110;&#97;&#109;&#101;&#64;&#64;&#89;&#71;&#42;&#42;&#42;&#42;&#42;&#64;&#90;">?functionname@@YG*****@Z</a>"或"<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#63;&#102;&#117;&#110;&#99;&#116;&#105;&#111;&#110;&#110;&#97;&#109;&#101;&#64;&#64;&#89;&#71;&#42;&#88;&#90;">?functionname@@YG*XZ</a>"，例如<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int Test1(char *var1,unsigned long)-----&#8220;?Test1@@YGHPADK@Z&#8221;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void Test2()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#45;&#45;&#45;&#45;&#45;&#8220;&#63;&#84;&#101;&#115;&#116;&#50;&#64;&#64;&#89;&#71;&#88;&#88;&#90;">-----&#8220;?Test2@@YGXXZ</a>&#8221;</p>
<p>&nbsp;</p>
<p>__cdecl调用约定：<br>规则同上面的_stdcall调用约定，只是参数表的开始标识由上面的"@@YG"变为"@@YA"。<br>__fastcall调用约定：<br>规则同上面的_stdcall调用约定，只是参数表的开始标识由上面的"@@YG"变为"@@YI"。<br>VC++对函数的省缺声明是"__cedcl",将只能被C/C++调用. </p>
<p><br>注意：<br>1、_beginthread需要__cdecl的线程函数地址，_beginthreadex和CreateThread需要__stdcall的线程函数地址。</p>
<p>2、一般WIN32的函数都是__stdcall。而且在Windef.h中有如下的定义：<br>#define CALLBACK __stdcall<br>#define WINAPI　 __stdcall</p>
<p>3、extern "C" _declspec(dllexport) int __cdecl Add(int a, int b);<br>&nbsp;&nbsp; typedef int (__cdecl*FunPointer)(int a, int b);<br>&nbsp;&nbsp; 修饰符的书写顺序如上。</p>
<p>4、extern "C"的作用：如果Add(int a, int b)是在c语言编译器编译，而在c++文件使用，则需要在c++文件中声明：extern "C" Add(int a, int b)，因为c编译器和c++编译器对函数名的解释不一样（c++编译器解释函数名的时候要考虑函数参数，这样是了方便函数重载，而在c语言中不存在函数重载的问题），使用extern "C"，实质就是告诉c++编译器，该函数是c库里面的函数。如果不使用extern "C"则会出现链接错误。<br>一般象如下使用：<br>#ifdef _cplusplus <br>#define ETERN_C extern "C"<br>#else<br>#define EXTERN_C extern<br>#endif</p>
<p>#ifdef _cplusplus <br>extern "C"{<br>#endif <br>EXTERN_C int func(int a, int b); <br>#ifdef _cplusplus <br>} <br>#endif</p>
<p>5、MFC提供了一些宏，可以使用AFX_EXT_CLASS来代替__declspec(DLLexport)，并修饰类名，从而导出类，AFX_API_EXPORT来修饰函数，AFX_DATA_EXPORT来修饰变量<br>AFX_CLASS_IMPORT：__declspec(DLLexport)<br>AFX_API_IMPORT：__declspec(DLLexport)<br>AFX_DATA_IMPORT：__declspec(DLLexport)<br>AFX_CLASS_EXPORT：__declspec(DLLexport)<br>AFX_API_EXPORT：__declspec(DLLexport)<br>AFX_DATA_EXPORT：__declspec(DLLexport)<br>AFX_EXT_CLASS：#ifdef _AFXEXT <br>&nbsp;&nbsp;&nbsp; AFX_CLASS_EXPORT<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #else<br>&nbsp;&nbsp;&nbsp; AFX_CLASS_IMPORT</p>
<p>6、DLLMain负责初始化(Initialization)和结束(Termination)工作，每当一个新的进程或者该进程的新的线程访问DLL时，或者访问DLL的每一个进程或者线程不再使用DLL或者结束时，都会调用DLLMain。但是，使用TerminateProcess或TerminateThread结束进程或者线程，不会调用DLLMain。</p>
<p>7、一个DLL在内存中只有一个实例<br>DLL程序和调用其输出函数的程序的关系：<br>1)、DLL与进程、线程之间的关系<br>DLL模块被映射到调用它的进程的虚拟地址空间。<br>DLL使用的内存从调用进程的虚拟地址空间分配，只能被该进程的线程所访问。<br>DLL的句柄可以被调用进程使用；调用进程的句柄可以被DLL使用。<br>DLLDLL可以有自己的数据段，但没有自己的堆栈，使用调用进程的栈，与调用它的应用程序相同的堆栈模式。</p>
<p>2)、关于共享数据段<br>DLL定义的全局变量可以被调用进程访问；DLL可以访问调用进程的全局数据。使用同一DLL的每一个进程都有自己的DLL全局变量实例。如果多个线程并发访问同一变量，则需要使用同步机制；对一个DLL的变量，如果希望每个使用DLL的线程都有自己的值，则应该使用线程局部存储(TLS，Thread Local Strorage)。</p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/jia_xiaoxin/archive/2008/09/02/2868216.aspx">http://blog.csdn.net/jia_xiaoxin/archive/2008/09/02/2868216.aspx</a><br><br></p>
<img src ="http://www.cppblog.com/deane/aggbug/113088.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2010-04-20 18:11 <a href="http://www.cppblog.com/deane/articles/113088.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++内存分配优先使用内存池，而不是new，delete</title><link>http://www.cppblog.com/deane/articles/94437.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Wed, 26 Aug 2009 02:46:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/94437.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/94437.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/94437.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/94437.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/94437.html</trackback:ping><description><![CDATA[<div>
<p align=center><strong><span><font face="Times New Roman">c++</font></span></strong><strong><span>内存分配优先使用内存池，而不是</span></strong><strong><span><font face="Times New Roman">new</font></span></strong><strong><span>，</span></strong><strong><span><font face="Times New Roman">delete</font></span></strong></p>
<p align=right><span><font face="Times New Roman"><st1:chsdate w:st="on" IsROCDate="False" IsLunarDate="False" Day="1" Month="2" Year="2008">转载</st1:chsdate></font></span></p>
<p align=right><span><font face="Times New Roman"><st1:chsdate w:st="on" IsROCDate="False" IsLunarDate="False" Day="1" Month="2" Year="2008">原文出处：<a href="http://www.devdiv.net/home/space.php?uid=125&amp;do=blog&amp;id=364">http://www.devdiv.net/home/space.php?uid=125&amp;do=blog&amp;id=364</a></st1:chsdate></font></span></p>
<p><span><font face="Times New Roman">&nbsp;</font></span></p>
<h1><span>认识一下</span><span><font face="Times New Roman">new</font></span><span>和</span><span><font face="Times New Roman">delete</font></span><span>的开销：</span></h1>
<p><span><font face="Times New Roman">new</font></span><span>和</span><span><font face="Times New Roman">delete</font></span><span>首先会转调用到</span><span><font face="Times New Roman">malloc</font></span><span>和</span><span><font face="Times New Roman">free</font></span><span>，这个大家应该很熟识了。很多人认为</span><span><font face="Times New Roman">malloc</font></span><span>是一个很简单的操作，其实巨复杂，它会执行一个系统调用，从用户态转到内核态，该系统调用会锁住内存硬件，然后通过链表的方式查找空闲内存，如果找到大小合适的，就把用户的进程地址映射到内存硬件地址中，然后释放锁，返回用户态。</span><span><font face="Times New Roman">delete</font></span><span>是一个反过程。</span></p>
<p><span>相对的，如果不是使用堆分配，而是直接在栈上分配，比如类型</span><span><font face="Times New Roman">int</font></span><span>，那么开销就是把</span><span><font face="Times New Roman">sp</font></span><span>这个寄存器加上</span><span><font face="Times New Roman">sizeof(int)</font></span><span>。</span></p>
<h1><span>内存池模式：</span></h1>
<p><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span>内存池就是预先分配好，放到进程空间的内存块，用户申请与释放内存其实都是在进程内进行，</span><span><font face="Times New Roman">SGI-STL</font></span><span>的</span><span><font face="Times New Roman">alloc</font></span><span>遇到小对象时就是基于内存池的。只有当内存池空间不够时，才会再从系统找一块很大的内存。</span></p>
<p><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span>内存池模式是如此之重要，以至于让我想不明白为什么四人帮那本《设计模式》没有把内存池列为基本模式，目前其它的教材，包括学院教材，实践教材都没有列出这个模式</span><span><font face="Times New Roman">(</font></span><span>讲线程池模式的教材倒非常多</span><span><font face="Times New Roman">)</font></span><span>。可能他们认为这不属于设计，而属于具体实现吧。但我觉得这样的后果是间接把很多</span><span><font face="Times New Roman">c++ fans</font></span><span>带向低效的编码方式。</span></p>
<p><span><font face="Times New Roman">sun</font></span><span>公司就挺喜欢搞一些算法，用</span><span><font face="Times New Roman">c++</font></span><span>实现与</span><span><font face="Times New Roman">java</font></span><span>实现一遍，结果显示</span><span><font face="Times New Roman">c++</font></span><span>的效率有时甚至比</span><span><font face="Times New Roman">java</font></span><span>低，很多</span><span><font face="Times New Roman">c++</font></span><span>高手看了之后都会觉得很难解，其实有玄机：</span><span><font face="Times New Roman">java</font></span><span>的</span><span><font face="Times New Roman">new</font></span><span>其实是基于内存池的，而</span><span><font face="Times New Roman">c++</font></span><span>的</span><span><font face="Times New Roman">new</font></span><span>是直接系统调用。</span></p>
<h1><span><font face="Times New Roman">c++</font></span><span>内存池模式的发展：</span></h1>
<p><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>c++98</font></span><span>标准之前，基本上大多数程序员没用使用内存池，</span><span><font face="Times New Roman">c++98 </font></span><span>标准之后，内存池的使用也只是停留在</span><span><font face="Times New Roman">STL</font></span><span>内部的使用上，并没有得到推广。</span></p>
<p><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span>其实我认为，</span><span><font face="Times New Roman">STL</font></span><span>的内存分配模式是一场变革，它不但包含内存分配的革命，也包含了内存管理</span><span><font face="Times New Roman">(</font></span><span>这个话题先放一边</span><span><font face="Times New Roman">)</font></span><span>的革命，只是这场变革被很多人忽略了。也有一些人认为</span><span><font face="Times New Roman">STL</font></span><span>的内存分配方案有潜在问题，就是只管从系统分配，但却永远不会调用系统级的释放，如果使用不当，程序拿住的内存会越来越多。我自己工作过的项目没遇上过这样的问题，但之前营帐报表组的一个容灾项目倒是遇上了。不过</span><span><font face="Times New Roman">STL</font></span><span>的内存模式没有推广最大的原因还是因为</span><span><font face="Times New Roman">alloc</font></span><span>不是标准组件，以至于被人忽略了。</span></p>
<p><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>STL</font></span><span>之后，一些</span><span><font face="Times New Roman">c++ fans</font></span><span>们开始搞出了几套内部使用的内存池。为了项目需要，我自己也曾经做过一个。但这些都没有很正式的公开，而且也不完美。</span></p>
<p><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span>大概在</span><span><font face="Times New Roman">200x</font></span><span>年</span><span><font face="Times New Roman">(-_-!)</font></span><span>，主导</span><span><font face="Times New Roman">c++</font></span><span>标准的一群牛人发起了一个叫</span><span><font face="Times New Roman">boost</font></span><span>的项目，才正式的把内存池带到实用与标准化阶段。</span></p>
<p><span>插入一点题外话：关于</span><span><font face="Times New Roman">boost</font></span><span>，很多人</span><span><font face="Times New Roman">(</font></span><span>包括我自己也曾经</span><span><font face="Times New Roman">)</font></span><span>产生误解，认为它是准标准库，是下一代标准库。其实</span><span><font face="Times New Roman">boost</font></span><span>是套基础建设，用来证明哪些方案是可行，哪些是不可行的，它里面的一些组件有可能会出局，也有可能不是以库的方式存在，而是以语言核心的方式存在，下一代标准库名字叫</span><span><font face="Times New Roman">TR1</font></span><span>，再一下代叫</span><span><font face="Times New Roman">TR2(</font></span><span>我对使用</span><span><font face="Times New Roman">TR</font></span><span>这个名字很费解，为什么不统一叫</span><span><font face="Times New Roman">STL</font></span><span>呢</span><span><font face="Times New Roman">)</font></span><span>。</span></p>
<h1><span><font face="Times New Roman">new,delete</font></span><span>调用与内存池调用的效率对比：</span></h1>
<p><span>讲了这么多费话，要到关键时候了，用事例来证明为什么要优先使用内存池。下面这段代码是我很久以前的一段测试案例，细节上可能有点懂难，但流程还是清晰的：</span></p>
<p align=left><span>#include</span><span> <span>&lt;time.h&gt;</span></span></p>
<p align=left><span>#include</span><span> <span>&lt;boost/pool/object_pool.hpp&gt;</span></span></p>
<p align=left>&nbsp;</p>
<p align=left><span>struct</span><span> <span>CCC</span></span></p>
<p align=left><span>{</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>CCC</span>() {}</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>char</span> <span>data</span>[<span>10</span>];</span></p>
<p align=left><span>};</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>struct</span><span> <span>SSS</span></span></p>
<p align=left><span>{</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>SSS</span>() {}</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>short</span> <span>data</span>[<span>10</span>];</span></p>
<p align=left><span>};</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>struct</span><span> <span>DDD</span></span></p>
<p align=left><span>{</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>DDD</span>() {}</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>double</span> <span>data</span>[<span>10</span>];</span></p>
<p align=left><span>};</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>// </span><span>把<span>new,delete</span>封装为一个与<span>boost::object_pool</span>一样的接口，以便于测试</span></p>
<p align=left><span>template</span><span> &lt;<span>typename</span> <span>element_type</span>, <span>typename</span> <span>user_allocator</span> = <span>boost</span>::<span>default_user_allocator_malloc_free</span>&gt;</span></p>
<p align=left><span>class</span><span> <span>new_delete_alloc</span></span></p>
<p align=left><span>{</span></p>
<p align=left><span>public</span><span>:</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>element_type</span>* <span>construct</span>() { <span>return</span> <span>new</span> <span>element_type</span>; }</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>void</span> <span>destroy</span>(<span>element_type</span>* <span>const</span> <span>chunk</span>) { <span>delete</span> <span>chunk</span>; }</span></p>
<p align=left><span>};</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>template</span></p>
<p align=left><span>&lt;&nbsp;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>template</span>&lt;<span>typename</span>, <span>typename</span>&gt; </span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>class</span> <span>allocator</span></span></p>
<p align=left><span>&gt;&nbsp;</span></p>
<p align=left><span>double</span><span> <span>test_allocator</span>()</span></p>
<p align=left><span>{</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>// </span></span><span>使用了一些不规则的分配与释放，增加内存管理的负担</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>// </span></span><span>但总体流程还是很规则的，基本上不产生内存碎片，要不然反差效果会更大。</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>allocator</span>&lt;<span>CCC</span>&gt; <span>c_allc</span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>allocator</span>&lt;<span>SSS</span>&gt; <span>s_allc</span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>allocator</span>&lt;<span>DDD</span>&gt; <span>d_allc</span>;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>double</span> <span>re</span> = <span>0</span>; <span>// </span></span><span>随便作一些运算，仿止编译器优化掉内存分配的代码</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>for</span> (<span>unsigned</span> <span>int</span> <span>i</span> = <span>0</span>; <span>i</span> &lt; <span>10000</span>; ++<span>i</span>)</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>for</span> (<span>unsigned</span> <span>int</span> <span>j</span> = <span>0</span>; <span>j</span> &lt; <span>10000</span>; ++<span>j</span>)</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>CCC</span>* <span>pc</span> = <span>c_allc</span>.<span>construct</span>();</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>SSS</span>* <span>ps</span> = <span>s_allc</span>.<span>construct</span>();</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>re</span> += <span>pc</span>-&gt;<span>data</span>[<span>2</span>];</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>c_allc</span>.<span>destroy</span>(<span>pc</span>);</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>DDD</span>* <span>pd</span> = <span>d_allc</span>.<span>construct</span>();</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>re</span> += <span>ps</span>-&gt;<span>data</span>[<span>2</span>];</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>re</span> += <span>pd</span>-&gt;<span>data</span>[<span>2</span>];</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>s_allc</span>.<span>destroy</span>(<span>ps</span>);</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>d_allc</span>.<span>destroy</span>(<span>pd</span>);</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>return</span> <span>re</span>;</span></p>
<p align=left><span>}</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>int</span><span> <span>main</span>(<span>int</span> <span>argc</span>, <span>char</span>* <span>argv</span>[])</span></p>
<p align=left><span>{</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>double</span> <span>re1</span> = <span>0</span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>double</span> <span>re2</span> = <span>0</span>;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>// </span></span><span>运行内存池测试时，基本上对我机器其它进程没什么影响</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>time_t</span> <span>begin</span> = <span>time</span>(<span>0</span>);</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>re1</span> = <span>test_allocator</span>&lt;<span>boost</span>::<span>object_pool</span>&gt;(); <span>// </span></span><span>使用内存池<span>boost::object_pool</span></span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>time_t</span> <span>seporator</span> = <span>time</span>(<span>0</span>);</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span></span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>// </span></span><span>运行到系统调用测试时，感觉机器明显变慢，</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>// </span></span><span>如果再加上内存碎片的考虑，对其它进程的影响会更大。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>std</span>::<span>cout</span> &lt;&lt; <span>long</span>(<span>seporator</span> - <span>begin</span>) &lt;&lt; <span>std</span>::<span>endl</span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>re2</span> = <span>test_allocator</span>&lt;<span>new_delete_alloc</span>&gt;();<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>// </span></span><span>直接系统调用</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>std</span>::<span>cout</span> &lt;&lt; <span>long</span>(<span>time</span>(<span>0</span>) - <span>seporator</span>) &lt;&lt; <span>std</span>::<span>endl</span>;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>std</span>::<span>cout</span> &lt;&lt; <span>re1</span> &lt;&lt; <span>re2</span> &lt;&lt; <span>std</span>::<span>endl</span>;</span></p>
<p><span>}</span></p>
<h1><span>总结：</span></h1>
<p><span>在一个<span>100000000</span>次的循环中，使用内存池是<span>3</span>秒，使用系统调用是<span>93</span>秒。</span></p>
<p><span>可能会有人觉得<span>100000000</span>这个数很大，<span>93</span>秒没什么，但想一下，一个表有几千万行是很正常的，如果每行有十多列，每列有数据类型，数据长度，数据内容。如果在这样的一个循环错误的使用了<span>new</span>和<span>delete</span>。</span></p>
<p><span>而且以上测试还没有考虑到碎片的影响，以及运行该程序时对其它程序的影响。而且还有一点，就是机器的内存硬件容量越大，内存分配时，需要搜索的时间就可能越长，如果内存是多条共同工作的，影响就再进一步。</span></p>
<p><span>什么算是错误的使用呢，比如返回一个<span>std::string</span>给用户，有人觉得<span>new</span>出来返回指针给用户会更好，你可能会想到如果<span>new</span>的话，只产生一次<span>string</span>的构造，如果直接返回对象可能需要多次构造，所以<span>new</span>效率更高。但事实不是这样，虽然在构造里会有字符串的分配，但其实这个分配是在内存池中进行的，而你直接的那个<span>new</span>就肯定是系统调用。</span></p>
<p><span>当然，有些情况是不可说用什么就用什么的，但如果可选的话，优先使用栈上的对象，其次考虑内存池，然后再考虑系统调用。</span></p>
<br><br></div>
<img src ="http://www.cppblog.com/deane/aggbug/94437.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-08-26 10:46 <a href="http://www.cppblog.com/deane/articles/94437.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ 对象的内存布局</title><link>http://www.cppblog.com/deane/articles/90834.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Wed, 22 Jul 2009 07:27:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/90834.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/90834.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/90834.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/90834.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/90834.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: C++ 对象的内存布局                                        &nbsp;                                        2008-11-14 作者：陈皓 来源：csdn                                        &nbsp;       ...&nbsp;&nbsp;<a href='http://www.cppblog.com/deane/articles/90834.html'>阅读全文</a><img src ="http://www.cppblog.com/deane/aggbug/90834.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-07-22 15:27 <a href="http://www.cppblog.com/deane/articles/90834.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ 虚函数表解析</title><link>http://www.cppblog.com/deane/articles/90833.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Wed, 22 Jul 2009 07:17:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/90833.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/90833.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/90833.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/90833.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/90833.html</trackback:ping><description><![CDATA[<br>
<table cellSpacing=0 cellPadding=0 width=895 align=center border=0>
    <tbody>
        <tr>
            <td bgColor=#ffffff>
            <div class=title align=center>C++ 虚函数表解析</div>
            </td>
        </tr>
        <tr>
            <td bgColor=#ffffff height=16>&nbsp;</td>
        </tr>
        <tr>
            <td bgColor=#ffffff>
            <div class=formtitle align=center>2008-11-14 作者：陈皓 来源：csdn</div>
            </td>
        </tr>
        <tr>
            <td bgColor=#ffffff height=16>&nbsp;</td>
        </tr>
        <tr>
            <td vAlign=top bgColor=#ffffff>
            <table cellSpacing=1 cellPadding=3 width="95%" align=center border=0>
                <tbody>
                    <tr>
                        <td class=content vAlign=top bgColor=#ffffff>
                        <p><strong>前言</strong></p>
                        <p>C++中的虚函数的作用主要是实现了多态的机制。关于多态，简而言之就是用父类型别的指针指向其子类的实例，然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有&#8220;多种形态&#8221;，这是一种泛型技术。所谓泛型技术，说白了就是试图使用不变的代码来实现可变的算法。比如：模板技术，RTTI技术，虚函数技术，要么是试图做到在编译时决议，要么试图做到运行时决议。</p>
                        <p>关于虚函数的使用方法，我在这里不做过多的阐述。大家可以看看相关的C++的书籍。在这篇文章中，我只想从虚函数的实现机制上面为大家 一个清晰的剖析。</p>
                        <p>当然，相同的文章在网上也出现过一些了，但我总感觉这些文章不是很容易阅读，大段大段的代码，没有图片，没有详细的说明，没有比较，没有举一反三。不利于学习和阅读，所以这是我想写下这篇文章的原因。也希望大家多给我提意见。</p>
                        <p>言归正传，让我们一起进入虚函数的世界。</p>
                        <p><strong>虚函数表</strong></p>
                        <p>对C++ 了解的人都应该知道虚函数（Virtual Function）是通过一张虚函数表（Virtual Table）来实现的。简称为V-Table。在这个表中，主是要一个类的虚函数的地址表，这张表解决了继承、覆盖的问题，保证其容真实反应实际的函数。这样，在有虚函数的类的实例中这个表被分配在了这个实例的内存中，所以，当我们用父类的指针来操作一个子类的时候，这张虚函数表就显得由为重要了，它就像一个地图一样，指明了实际所应该调用的函数。</p>
                        <p>这里我们着重看一下这张虚函数表。C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置（这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下）。 这意味着我们通过对象实例的地址得到这张虚函数表，然后就可以遍历其中函数指针，并调用相应的函数。</p>
                        <p>听我扯了那么多，我可以感觉出来你现在可能比以前更加晕头转向了。 没关系，下面就是实际的例子，相信聪明的你一看就明白了。</p>
                        <p>假设我们有这样的一个类：</p>
                        <table class=content id=table1 style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellSpacing=0 cellPadding=6 width="95%" align=center border=0>
                            <tbody>
                                <tr>
                                    <td style="WORD-WRAP: break-word" bgColor=#f3f3f3><span class=content lang=ZH-CN style="FONT-FAMILY: 宋体"><font style="FONT-WEIGHT: bold; COLOR: #990000">以下是引用片段：</font><br>class&nbsp;Base&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;f()&nbsp;{&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Base::f"&nbsp;&lt;&lt;&nbsp;endl;&nbsp;} <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;g()&nbsp;{&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Base::g"&nbsp;&lt;&lt;&nbsp;endl;&nbsp;} <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;h()&nbsp;{&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Base::h"&nbsp;&lt;&lt;&nbsp;endl;&nbsp;} <br>&nbsp; <br>}; <br>&nbsp;</span></td>
                                </tr>
                            </tbody>
                        </table>
                        <p>按照上面的说法，我们可以通过Base的实例来得到虚函数表。 下面是实际例程：</p>
                        <table id=table2 style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellSpacing=0 cellPadding=6 width="95%" align=center border=0>
                            <tbody>
                                <tr>
                                    <td style="WORD-WRAP: break-word" bgColor=#f3f3f3><span class=content style="FONT-FAMILY: Tahoma"><font style="FONT-WEIGHT: bold; COLOR: #990000">以下是引用片段：</font><br>&nbsp;&nbsp;&nbsp;typedef&nbsp;void(*Fun)(void); <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Base&nbsp;b; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Fun&nbsp;pFun&nbsp;=&nbsp;NULL; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"虚函数表地址："&nbsp;&lt;&lt;&nbsp;(int*)(&amp;b)&nbsp;&lt;&lt;&nbsp;endl; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"虚函数表&nbsp;—&nbsp;第一个函数地址："&nbsp;&lt;&lt;&nbsp;(int*)*(int*)(&amp;b)&nbsp;&lt;&lt;&nbsp;endl; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Invoke&nbsp;the&nbsp;first&nbsp;virtual&nbsp;function&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun&nbsp;=&nbsp;(Fun)*((int*)*(int*)(&amp;b)); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun();</span></td>
                                </tr>
                            </tbody>
                        </table>
                        <p>实际运行经果如下：(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3)</p>
                        <table id=table3 style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellSpacing=0 cellPadding=6 width="95%" align=center border=0>
                            <tbody>
                                <tr>
                                    <td style="WORD-WRAP: break-word" bgColor=#f3f3f3><span style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Tahoma"><font style="FONT-WEIGHT: bold; COLOR: #990000">以下是引用片段：</font><br>虚函数表地址：0012FED4 <br>虚函数表&nbsp;—&nbsp;第一个函数地址：0044F148 <br>Base::f</span></td>
                                </tr>
                            </tbody>
                        </table>
                        <p>通过这个示例，我们可以看到，我们可以通过强行把&amp;b转成int *，取得虚函数表的地址，然后，再次取址就可以得到第一个虚函数的地址了，也就是Base::f()，这在上面的程序中得到了验证（把int* 强制转成了函数指针）。通过这个示例，我们就可以知道如果要调用Base::g()和Base::h()，其代码如下：</p>
                        <table id=table4 style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellSpacing=0 cellPadding=6 width="95%" align=center border=0>
                            <tbody>
                                <tr>
                                    <td style="WORD-WRAP: break-word" bgColor=#f3f3f3><span class=content><font style="FONT-WEIGHT: bold; COLOR: #990000" face="Times New Roman">以下是引用片段：</font><font face="Times New Roman"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (Fun)*((int*)*(int*)(&amp;b)+0);&nbsp;&nbsp;//&nbsp;Base::f() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(Fun)*((int*)*(int*)(&amp;b)+1);&nbsp;&nbsp;//&nbsp;Base::g() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(Fun)*((int*)*(int*)(&amp;b)+2);&nbsp;&nbsp;//&nbsp;Base::h()</font></span></td>
                                </tr>
                            </tbody>
                        </table>
                        <p>这个时候你应该懂了吧。什么？还是有点晕。也是，这样的代码看着太乱了。没问题，让我画个图解释一下。如下所示：</p>
                        <p><span lang=ZH-CN style="FONT-FAMILY: 宋体"><font size=3><img alt="" src="http://www.uml.org.cn/c++/images/9odbu7371622.jpg"></font></span></p>
                        <p>注意：在上面这个图中，我在虚函数表的最后多加了一个结点，这是虚函数表的结束结点，就像字符串的结束符&#8220;\0&#8221;一样，其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。在WinXP+VS2003下，这个值是NULL。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下，这个值是如果1，表示还有下一个虚函数表，如果值是0，表示是最后一个虚函数表。</p>
                        <p>下面，我将分别说明&#8220;无覆盖&#8221;和&#8220;有覆盖&#8221;时的虚函数表的样子。没有覆盖父类的虚函数是毫无意义的。我之所以要讲述没有覆盖的情况，主要目的是为了给一个对比。在比较之下，我们可以更加清楚地知道其内部的具体实现。</p>
                        <p><strong>一般继承（无虚函数覆盖）</strong></p>
                        <p>下面，再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系：</p>
                        <p><img alt="" src="http://www.uml.org.cn/c++/images/ol8i4mw575e6.jpg"></p>
                        <p>请注意，在这个继承关系中，子类没有重载任何父类的函数。那么，在派生类的实例中，其虚函数表如下所示：</p>
                        <p>对于实例：Derive d; 的虚函数表如下：</p>
                        <p><font size=3><span lang=ZH-CN style="FONT-FAMILY: 宋体"><a href="http://dev.yesky.com/syscore/168/565168d_2.shtml" target=_blank><img alt="" src="http://www.uml.org.cn/c++/images/260wosl5tj76s.jpg" border=0></a></span></font></p>
                        <p>我们可以看到下面几点：</p>
                        <p>1）虚函数按照其声明顺序放于表中。</p>
                        <p>2）父类的虚函数在子类的虚函数前面。</p>
                        <p>我相信聪明的你一定可以参考前面的那个程序，来编写一段程序来验证。</p>
                        <p><strong>一般继承（有虚函数覆盖）</strong></p>
                        <p>覆盖父类的虚函数是很显然的事情，不然，虚函数就变得毫无意义。下面，我们来看一下，如果子类中有虚函数重载了父类的虚函数，会是一个什么样子？假设，我们有下面这样的一个继承关系。</p>
                        <p><font face="Times New Roman" size=3><img alt="" src="http://www.uml.org.cn/c++/images/ma6262p9mhw8.jpg"></font></p>
                        <p>为了让大家看到被继承过后的效果，在这个类的设计中，我只覆盖了父类的一个函数：f()。那么，对于派生类的实例，其虚函数表会是下面的一个样子：</p>
                        <p><font face="Times New Roman" size=3><img alt="" src="http://www.uml.org.cn/c++/images/l4j7o68j6d5y.jpg"></font></p>
                        <p>我们从表中可以看到下面几点，</p>
                        <p>1）覆盖的f()函数被放到了虚表中原来父类虚函数的位置。</p>
                        <p>2）没有被覆盖的函数依旧。</p>
                        <p>这样，我们就可以看到对于下面这样的程序，</p>
                        <table id=table1 style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellSpacing=0 cellPadding=6 width="95%" align=center border=0>
                            <tbody>
                                <tr>
                                    <td style="WORD-WRAP: break-word" bgColor=#f3f3f3><span class=content style="FONT-SIZE: 9pt; FONT-FAMILY: Tahoma"><font style="FONT-WEIGHT: bold; COLOR: #990000">以下是引用片段：</font><br>&nbsp;Base&nbsp;*b&nbsp;=&nbsp;new&nbsp;Derive(); <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b-&gt;f();</span></td>
                                </tr>
                            </tbody>
                        </table>
                        <p>由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代，于是在实际调用发生时，是Derive::f()被调用了。这就实现了多态。</p>
                        <p><strong>多重继承（无虚函数覆盖）</strong></p>
                        <p>下面，再让我们来看看多重继承中的情况，假设有下面这样一个类的继承关系。注意：子类并没有覆盖父类的函数。</p>
                        <p><font face="Times New Roman" size=3><img alt="" src="http://www.uml.org.cn/c++/images/672lazylxvxs.jpg"></font></p>
                        <p>对于子类实例中的虚函数表，是下面这个样子：</p>
                        <p><span lang=ZH-CN style="FONT-FAMILY: 宋体"><img alt="" src="http://www.uml.org.cn/c++/images/exfu77u3q2ut.jpg"></span></p>
                        <p>我们可以看到：</p>
                        <p>1） 每个父类都有自己的虚表。</p>
                        <p>2） 子类的成员函数被放到了第一个父类的表中。（所谓的第一个父类是按照声明顺序来判断的）</p>
                        <p>这样做就是为了解决不同的父类类型的指针指向同一个子类实例，而能够调用到实际的函数。</p>
                        <p><strong>多重继承（有虚函数覆盖）</strong></p>
                        <p>下面我们再来看看，如果发生虚函数覆盖的情况。</p>
                        <p>下图中，我们在子类中覆盖了父类的f()函数。</p>
                        <p><font face="Times New Roman" size=3><img alt="" src="http://www.uml.org.cn/c++/images/s33b2a01446o.jpg"></font></p>
                        <p>下面是对于子类实例中的虚函数表的图：</p>
                        <p><font face="Times New Roman" size=3><img alt="" src="http://www.uml.org.cn/c++/images/5ad9us16b33j.jpg"></font></p>
                        <p>我们可以看见，三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样，我们就可以任一静态类型的父类来指向子类，并调用子类的f()了。如：</p>
                        <table class=content id=table1 style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellSpacing=0 cellPadding=6 width="95%" align=center border=0>
                            <tbody>
                                <tr>
                                    <td style="WORD-WRAP: break-word" bgColor=#f3f3f3><span style="FONT-SIZE: 9pt; FONT-FAMILY: Tahoma"><font style="FONT-WEIGHT: bold; COLOR: #990000">以下是引用片段：</font><br>&nbsp;&nbsp;Derive&nbsp;d; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Base1&nbsp;*b1&nbsp;=&nbsp;&amp;d; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Base2&nbsp;*b2&nbsp;=&nbsp;&amp;d; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Base3&nbsp;*b3&nbsp;=&nbsp;&amp;d; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b1-&gt;f();&nbsp;//Derive::f() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b2-&gt;f();&nbsp;//Derive::f() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b3-&gt;f();&nbsp;//Derive::f() <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b1-&gt;g();&nbsp;//Base1::g() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b2-&gt;g();&nbsp;//Base2::g() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b3-&gt;g();&nbsp;//Base3::g()</span></td>
                                </tr>
                            </tbody>
                        </table>
                        <p><strong>安全性</strong></p>
                        <p>每次写C++的文章，总免不了要批判一下C++。这篇文章也不例外。通过上面的讲述，相信我们对虚函数表有一个比较细致的了解了。水可载舟，亦可覆舟。下面，让我们来看看我们可以用虚函数表来干点什么坏事吧。</p>
                        <p>一、通过父类型的指针访问子类自己的虚函数</p>
                        <p>我们知道，子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数，但我们根本不可能使用下面的语句来调用子类的自有虚函数：</p>
                        <table id=table1 style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellSpacing=0 cellPadding=6 width="95%" align=center border=0>
                            <tbody>
                                <tr>
                                    <td style="WORD-WRAP: break-word" bgColor=#f3f3f3><span class=content style="FONT-FAMILY: Tahoma"><font style="FONT-WEIGHT: bold; COLOR: #990000">以下是引用片段：</font><br>&nbsp;&nbsp;Base1&nbsp;*b1&nbsp;=&nbsp;new&nbsp;Derive(); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b1-&gt;f1();&nbsp;&nbsp;//编译出错</span></td>
                                </tr>
                            </tbody>
                        </table>
                        <p>任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法，所以，这样的程序根本无法编译通过。但在运行时，我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。（关于这方面的尝试，通过阅读后面附录的代码，相信你可以做到这一点）</p>
                        <p><strong>二、访问non-public的虚函数</strong></p>
                        <p>另外，如果父类的虚函数是private或是protected的，但这些非public的虚函数同样会存在于虚函数表中，所以，我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数，这是很容易做到的。</p>
                        <p>如：</p>
                        <table id=table2 style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellSpacing=0 cellPadding=6 width="95%" align=center border=0>
                            <tbody>
                                <tr>
                                    <td style="WORD-WRAP: break-word" bgColor=#f3f3f3><span class=content style="FONT-SIZE: 9pt; FONT-FAMILY: Tahoma"><font style="FONT-WEIGHT: bold; COLOR: #990000">以下是引用片段：</font><br>class&nbsp;Base&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;private: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;f()&nbsp;{&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Base::f"&nbsp;&lt;&lt;&nbsp;endl;&nbsp;} <br>&nbsp; <br>}; <br>&nbsp; <br>class&nbsp;Derive&nbsp;:&nbsp;public&nbsp;Base{ <br>&nbsp; <br>}; <br>&nbsp; <br>typedef&nbsp;void(*Fun)(void); <br>&nbsp; <br>void&nbsp;main()&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;Derive&nbsp;d; <br>&nbsp;&nbsp;&nbsp;&nbsp;Fun&nbsp;&nbsp;pFun&nbsp;=&nbsp;(Fun)*((int*)*(int*)(&amp;d)+0); <br>&nbsp;&nbsp;&nbsp;&nbsp;pFun(); <br>} <br>&nbsp;</span></td>
                                </tr>
                            </tbody>
                        </table>
                        <p><strong>结束语</strong></p>
                        <p>C++这门语言是一门Magic的语言，对于程序员来说，我们似乎永远摸不清楚这门语言背着我们在干了什么。需要熟悉这门语言，我们就必需要了解C++里面的那些东西，需要去了解C++中那些危险的东西。不然，这是一种搬起石头砸自己脚的编程语言。</p>
                        <p>在文章束之前还是介绍一下自己吧。我从事软件研发有十个年头了，目前是软件开发技术主管，技术方面，主攻Unix/C/C++，比较喜欢网络上的技术，比如分布式计算，网格计算，P2P，Ajax等一切和互联网相关的东西。管理方面比较擅长于团队建设，技术趋势分析，项目管理。欢迎大家和我交流，我的MSN和Email是：haoel@hotmail.com </p>
                        <p><strong>附录一：VC中查看虚函数表</strong> </p>
                        <p>我们可以在VC的IDE环境中的Debug状态下展开类的实例就可以看到虚函数表了（并不是很完整的）</p>
                        <p><font size=3><span lang=ZH-CN style="FONT-FAMILY: 宋体"><a href="http://dev.yesky.com/syscore/168/565168d_9.shtml" target=_blank><img alt="" src="http://www.uml.org.cn/c++/images/9hd375i442e1s.jpg" border=0></a></span></font></p>
                        <p><strong>附录 二：例程 </strong></p>
                        <p>下面是一个关于多重继承的虚函数表访问的例程：</p>
                        <table id=table1 style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellSpacing=0 cellPadding=6 width="95%" align=center border=0>
                            <tbody>
                                <tr>
                                    <td style="WORD-WRAP: break-word" bgColor=#f3f3f3><span class=content style="FONT-SIZE: 9pt"><font style="FONT-WEIGHT: bold; COLOR: #990000">以下是引用片段：</font><br>#include&nbsp;&lt;iostream&gt; <br>using&nbsp;namespace&nbsp;std; <br>&nbsp; <br>class&nbsp;Base1&nbsp;{ <br>public: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;f()&nbsp;{&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Base1::f"&nbsp;&lt;&lt;&nbsp;endl;&nbsp;} <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;g()&nbsp;{&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Base1::g"&nbsp;&lt;&lt;&nbsp;endl;&nbsp;} <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;h()&nbsp;{&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Base1::h"&nbsp;&lt;&lt;&nbsp;endl;&nbsp;} <br>&nbsp; <br>}; <br>&nbsp; <br>class&nbsp;Base2&nbsp;{ <br>public: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;f()&nbsp;{&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Base2::f"&nbsp;&lt;&lt;&nbsp;endl;&nbsp;} <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;g()&nbsp;{&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Base2::g"&nbsp;&lt;&lt;&nbsp;endl;&nbsp;} <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;h()&nbsp;{&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Base2::h"&nbsp;&lt;&lt;&nbsp;endl;&nbsp;} <br>}; <br>&nbsp; <br>class&nbsp;Base3&nbsp;{ <br>public: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;f()&nbsp;{&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Base3::f"&nbsp;&lt;&lt;&nbsp;endl;&nbsp;} <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;g()&nbsp;{&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Base3::g"&nbsp;&lt;&lt;&nbsp;endl;&nbsp;} <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;h()&nbsp;{&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Base3::h"&nbsp;&lt;&lt;&nbsp;endl;&nbsp;} <br>}; <br>&nbsp; <br>&nbsp; <br>class&nbsp;Derive&nbsp;:&nbsp;public&nbsp;Base1,&nbsp;public&nbsp;Base2,&nbsp;public&nbsp;Base3&nbsp;{ <br>public: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;f()&nbsp;{&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Derive::f"&nbsp;&lt;&lt;&nbsp;endl;&nbsp;} <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;g1()&nbsp;{&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Derive::g1"&nbsp;&lt;&lt;&nbsp;endl;&nbsp;} <br>}; <br>&nbsp; <br>&nbsp; <br>typedef&nbsp;void(*Fun)(void); <br>&nbsp; <br>int&nbsp;main()&nbsp; <br>{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Fun&nbsp;pFun&nbsp;=&nbsp;NULL; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Derive&nbsp;d; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int**&nbsp;pVtab&nbsp;=&nbsp;(int**)&amp;d; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Base1's&nbsp;vtable <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//pFun&nbsp;=&nbsp;(Fun)*((int*)*(int*)((int*)&amp;d+0)+0); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun&nbsp;=&nbsp;(Fun)pVtab[0][0]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun(); <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//pFun&nbsp;=&nbsp;(Fun)*((int*)*(int*)((int*)&amp;d+0)+1); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun&nbsp;=&nbsp;(Fun)pVtab[0][1]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun(); <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//pFun&nbsp;=&nbsp;(Fun)*((int*)*(int*)((int*)&amp;d+0)+2); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun&nbsp;=&nbsp;(Fun)pVtab[0][2]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun(); <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Derive's&nbsp;vtable <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//pFun&nbsp;=&nbsp;(Fun)*((int*)*(int*)((int*)&amp;d+0)+3); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun&nbsp;=&nbsp;(Fun)pVtab[0][3]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun(); <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//The&nbsp;tail&nbsp;of&nbsp;the&nbsp;vtable <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun&nbsp;=&nbsp;(Fun)pVtab[0][4]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;pFun&lt;&lt;endl; <br>&nbsp; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Base2's&nbsp;vtable <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//pFun&nbsp;=&nbsp;(Fun)*((int*)*(int*)((int*)&amp;d+1)+0); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun&nbsp;=&nbsp;(Fun)pVtab[1][0]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun(); <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//pFun&nbsp;=&nbsp;(Fun)*((int*)*(int*)((int*)&amp;d+1)+1); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun&nbsp;=&nbsp;(Fun)pVtab[1][1]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun(); <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun&nbsp;=&nbsp;(Fun)pVtab[1][2]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun();&nbsp; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//The&nbsp;tail&nbsp;of&nbsp;the&nbsp;vtable <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun&nbsp;=&nbsp;(Fun)pVtab[1][3]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;pFun&lt;&lt;endl; <br>&nbsp; <br>&nbsp; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Base3's&nbsp;vtable <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//pFun&nbsp;=&nbsp;(Fun)*((int*)*(int*)((int*)&amp;d+1)+0); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun&nbsp;=&nbsp;(Fun)pVtab[2][0]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun(); <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//pFun&nbsp;=&nbsp;(Fun)*((int*)*(int*)((int*)&amp;d+1)+1); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun&nbsp;=&nbsp;(Fun)pVtab[2][1]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun(); <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun&nbsp;=&nbsp;(Fun)pVtab[2][2]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun();&nbsp; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//The&nbsp;tail&nbsp;of&nbsp;the&nbsp;vtable <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFun&nbsp;=&nbsp;(Fun)pVtab[2][3]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;pFun&lt;&lt;endl; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0; <br>}</span></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/deane/aggbug/90833.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-07-22 15:17 <a href="http://www.cppblog.com/deane/articles/90833.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>seekg 无效问题</title><link>http://www.cppblog.com/deane/articles/87788.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Tue, 16 Jun 2009 05:20:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/87788.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/87788.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/87788.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/87788.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/87788.html</trackback:ping><description><![CDATA[<p><br><br>ifstream 打开一个文件<br><br>read直到eof<br>然后我希望从头处理这个文件，<br>seekg(0,ios:beg)等诸多形式均无效，<br>查C++标准文档未果，<br>仔细google后发现这里需要 clear() 一下，清除错误状态。<br></p>
<img src ="http://www.cppblog.com/deane/aggbug/87788.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-06-16 13:20 <a href="http://www.cppblog.com/deane/articles/87788.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>boost的网络库asio</title><link>http://www.cppblog.com/deane/articles/82033.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Wed, 06 May 2009 06:15:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/82033.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/82033.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/82033.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/82033.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/82033.html</trackback:ping><description><![CDATA[<br>boost在1.35版本之后终于加入了网络库asio。<br>地址：<a href="http://think-async.com/Asio/">http://think-async.com/Asio/</a><br><br>asio的名字突出了异步I/O的能力，从asio的文档中看到它使用了和ACE Proactor框架中相似的Proactor模式。CSDN上也有很多网友也写了很多关于异步I/O的好文章，但是我还是决定从同步I/O开始。尽管阻塞I/O不是那么酷那么绚丽但是在网络编程中它和异步I/O一样重要。 <br><br>下面是一个简单的同步I/O的例子，使用的是锁步(lock-step)方式的通讯。 <br><br>view plaincopy to clipboardprint? <br>#include &lt;string&gt; <br>#include "boost/asio.hpp" <br>#include "boost/lexical_cast.hpp" <br><br>using namespace std; <br>using namespace boost; <br>using boost::asio::ip::tcp; <br>using boost::asio::io_service; <br><br>class Client <br>{ <br>public: <br>Client (const string &amp; hostname, unsigned short port); <br>virtual ~Client (); <br><br>// methods <br>virtual void send (const string &amp; message); <br>virtual string recv (); <br>virtual void close (); <br><br>private: <br>io_service * io_service_; <br>tcp::socket * socket_; <br>}; <br><br>Client::Client (const string &amp; hostname, unsigned short port) <br>{ <br>io_service_ = new io_service(); <br>socket_ = new tcp::socket(*io_service_); <br><br>tcp::resolver resolver(*io_service_); <br>tcp::resolver::query query(hostname, boost::lexical_cast&lt;string, unsigned short&gt;(port)); <br><br>boost::system::error_code ec; <br>tcp::resolver::iterator iter = resolver.resolve(query, ec); <br>tcp::resolver::iterator end; <br><br>// pick the first endpoint <br>if (iter != end &amp;&amp; ec == 0) <br>{ <br>tcp::endpoint endpoint = *iter; <br>std::cout &lt;&lt; "Connecting to: " &lt;&lt; endpoint &lt;&lt; std::endl; <br><br>socket_-&gt;connect(endpoint, ec); <br>if (ec) <br>{ <br>std::cerr &lt;&lt; "Error: " &lt;&lt; ec &lt;&lt; std::endl; <br>throw ec; <br>} <br>} <br>} <br><br>Client::~Client () <br>{ <br>delete socket_; <br>delete io_service_; <br>} <br><br>void Client::send (const string &amp; message) <br>{ <br>boost::asio::const_buffers_1 request(message.data(), message.size()); <br>socket_-&gt;send(request); <br>} <br><br>string Client::recv () <br>{ <br>char response[128]; <br>size_t num = socket_-&gt;receive(boost::asio::buffer(response)); <br>if (num &gt; 0) <br>{ <br>return string (response, num); <br>} <br><br>return ""; <br>} <br><br>void Client::close () <br>{ <br>socket_-&gt;close(); <br><br>} <br><br>int _tmain(int argc, _TCHAR* argv[]) <br>{ <br>Client client ("localhost", 2009); <br>std::cout &lt;&lt; client.recv() &lt;&lt; endl; <br><br>string request; <br><br>do <br>{ <br>std::cout &lt;&lt; "Request: "; <br><br>std::cin &gt;&gt; request; <br><br>if (request == "q") <br>break; <br><br>client.send (request); <br><br>std::cout &lt;&lt; "Response: " &lt;&lt; client.recv() &lt;&lt; endl; <br>} <br>while (true); <br><br>client.close(); <br>return 0; <br>} <br>
<img src ="http://www.cppblog.com/deane/aggbug/82033.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-05-06 14:15 <a href="http://www.cppblog.com/deane/articles/82033.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>共享数据段的设置</title><link>http://www.cppblog.com/deane/articles/78943.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Sat, 04 Apr 2009 05:58:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/78943.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/78943.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/78943.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/78943.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/78943.html</trackback:ping><description><![CDATA[<div class=postTitle>&nbsp;</div>
<p><br><br>一般来说，动态链接库在内存中只会加载一次</p>
<p>&nbsp;</p>
<p>每个进程需要调用到次动态链接库的时候，都会从这一个内存地址中加载</p>
<p>&nbsp;</p>
<p>但，如果进程需要改动动态链接库中的信息，哪怕是全局变量，系统也会从内存中重新分配出一小块区域，来进行这些变量的存储</p>
<p>那么与之对应的，每一个进程，所修改的动态链接库，只能是自己的一份，不能在所有进程中共享的</p>
<p>&nbsp;</p>
<p>如果想在所有进程中共享一份动态链接库数据，则需要在DLL中设置一个共享的 &#8220;段&#8221;。<br>&nbsp;</p>
<p>以下代码是定义一个节并给节命名为MySec，HWND g_hWnd=NULL;为将放在节中的数据</p>
<p>#pragma data_seg("MySec")<br>HWND g_hWnd=NULL;<br>#pragma data_seg()
<p>&nbsp;注意：但是放在 MySec 段中的变量必须要被初始化。飞走而编译器就会将该变量放到 MySec 段以外<br>的其他段中。</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp; </p>
<p align=left><span>仅定义一个数据段还不能达到共享数据的目的，还要告诉编译器该段的属性，有两种方法可以实现该目的（其效果是相同的），一种方法是在</span><span>.DEF</span><span>文件中加入如下语句：</span><span> </span></p>
<p>&nbsp; </p>
<p align=left><span>SETCTIONS </span></p>
<p align=left><span>　　</span><span>MySec READ WRITE SHARED </span></p>
<p><br><br>&nbsp; </p>
<p align=left><span>另一种方法是在项目设置链接选项中加入如下语句：</span><span> </span></p>
<p align=left><span>　　</span><span>/SECTION:shareddata,rws </span></p>
<p><br>RWS：r为读，w为写，s为共享<br><br><br>或者 使用一种更方便的方法，直接将连接器开关嵌入到 dll 的源代码中。<br><br>#pragma comment(linker,"/section:MySec,RWS")</p>
<br>
<p><br>&nbsp;</p>
<img src ="http://www.cppblog.com/deane/aggbug/78943.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-04-04 13:58 <a href="http://www.cppblog.com/deane/articles/78943.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>捕获控制台程序的消息</title><link>http://www.cppblog.com/deane/articles/78593.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Wed, 01 Apr 2009 11:46:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/78593.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/78593.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/78593.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/78593.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/78593.html</trackback:ping><description><![CDATA[<h1 class=title_txt><img height=14 alt=转载 src="http://blog.csdn.net/images/turnship.gif" width=15 border=0>&nbsp;捕获控制台程序的消息</h1>
<div class=blogstory>
<script>function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}</script>
<div style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">控制台程序在</span><span style="FONT-SIZE: 12pt">Windows</span><span style="FONT-SIZE: 12pt">程序的角色中是非常強大且方便的，像</span><span style="FONT-SIZE: 12pt">VC</span><span style="FONT-SIZE: 12pt">，</span><span style="FONT-SIZE: 12pt">C#</span><span style="FONT-SIZE: 12pt">，</span><span style="FONT-SIZE: 12pt">Delphi</span><span style="FONT-SIZE: 12pt">等等，好多功能強大的語言都支持控制台程序。她沒有複雜的</span><span style="FONT-SIZE: 12pt">GUI</span><span style="FONT-SIZE: 12pt">，完全是</span><span style="FONT-SIZE: 12pt">32</span><span style="FONT-SIZE: 12pt">位的程序，能夠調用除</span><span style="FONT-SIZE: 12pt">GDI </span><span style="FONT-SIZE: 12pt">函數之外的</span><span style="FONT-SIZE: 12pt">API</span><span style="FONT-SIZE: 12pt">，支持多線程，支持</span><span style="FONT-SIZE: 12pt">MFC</span><span style="FONT-SIZE: 12pt">等等。用她來調試程序、學習程序設計、做實驗等是再合適不過的了。我經常把我試驗性的程序用控制台方式來寫，非常方便。</span></div>
<div><span style="FONT-SIZE: 12pt">&nbsp;</span></div>
<div style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">Console</span><span style="FONT-SIZE: 12pt">程序不像</span><span style="FONT-SIZE: 12pt">Win32 GUI</span><span style="FONT-SIZE: 12pt">程序那樣具有消息隊列，所以當程序中斷的時候也無從得知。假如我們程序正在處理一個長時間的作業，當用戶要退出系統，或按了</span><span style="FONT-SIZE: 12pt">Ctrl + C(Ctrl + Break)</span><span style="FONT-SIZE: 12pt">，或系統將要關閉的時候，我們的數據就可能會因此而丟失。難道沒有辦法了么？哦，不，有辦法的。看下面，下面我將跟大家談談關於</span><span style="FONT-SIZE: 12pt">Console</span><span style="FONT-SIZE: 12pt">程序的事件處理。</span></div>
<div><span style="FONT-SIZE: 12pt">&nbsp;</span></div>
<div style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">要讓控制台程序具有事件處理能力需要用到下面幾個</span><span style="FONT-SIZE: 12pt">API</span><span style="FONT-SIZE: 12pt">函數，他們的原型聲明如下：</span></div>
<div style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">&nbsp;</span></div>
<pre><font style="BACKGROUND-COLOR: #fbedbb" size=2>BOOL SetConsoleCtrlHandler(</font></pre>
<pre><font size=2><font style="BACKGROUND-COLOR: #fbedbb"><span>&nbsp;&nbsp;&nbsp; PHANDLER_ROUTINE HandlerRoutine, &nbsp;</span><span><em><font color=#008000>// handler function</font></em></span></font></font></pre>
<pre><font size=2><font style="BACKGROUND-COLOR: #fbedbb"><span>&nbsp;&nbsp;&nbsp; BOOL Add  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span><em><font color=#008000>// add or remove handler</font></em></span></font></font></pre>
<div>);</div>
<div>&nbsp;</div>
<div>HandlerRoutine指向一個事件處理函數，是的，可能你已經猜到了，這個函數相當於Win32 GUI程序中的窗口處理函數。這個函數的原型如下：</div>
<div>&nbsp;</div>
<pre><font style="BACKGROUND-COLOR: #fbedbb" size=2>BOOL WINAPI HandlerRoutine(</font></pre>
<pre><font size=2><font style="BACKGROUND-COLOR: #fbedbb"><span>&nbsp;&nbsp;&nbsp; DWORD dwCtrlType&nbsp;&nbsp;  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span><em><font color=#008000>//&nbsp;control signal type</font></em></span></font></font></pre>
<div>);</div>
<div>&nbsp;</div>
<div>這個函數的dwCtrlType指示出接收到的控制信號，這個參數可能是下面值中的某一個：</div>
<div><span style="FONT-SIZE: 12pt">&nbsp;</span></div>
<table style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: #e0e0e0; MARGIN-LEFT: 5.4pt; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" cellSpacing=0 cellPadding=0 bgColor=#e0e0e0 border=1>
    <tbody>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 142.65pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=190>
            <div><strong><span style="FONT-SIZE: 12pt">信</span></strong><strong><span style="FONT-SIZE: 12pt">號</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #d4d0c8; PADDING-BOTTOM: 0cm; WIDTH: 278.05pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=371>
            <div><strong><span style="FONT-SIZE: 12pt">描</span></strong><strong><span style="FONT-SIZE: 12pt">述</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: white; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 142.65pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=190>
            <div><font size=3>CTRL_C_EVENT</font></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #d4d0c8; BACKGROUND: white; PADDING-BOTTOM: 0cm; WIDTH: 278.05pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=371>
            <div><span style="FONT-SIZE: 12pt">一個</span><span style="FONT-SIZE: 12pt">Ctrl + C</span><span style="FONT-SIZE: 12pt">的信號被接收，該信號或來自鍵盤，或來自</span><font size=3><strong>GenerateConsoleCtrlEvent </strong>函數</font></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: white; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 142.65pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=190>
            <div><font size=3>CTRL_BREAK_EVENT</font></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #d4d0c8; BACKGROUND: white; PADDING-BOTTOM: 0cm; WIDTH: 278.05pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=371>
            <div><span style="FONT-SIZE: 12pt">一個</span><span style="FONT-SIZE: 12pt"> Ctrl + Break </span><span style="FONT-SIZE: 12pt">信號被接收，該信號或來自鍵盤，或來自</span><font size=3><strong>GenerateConsoleCtrlEvent </strong>函數</font></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: white; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 142.65pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=190>
            <div><font size=3>CTRL_CLOSE_EVENT</font></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #d4d0c8; BACKGROUND: white; PADDING-BOTTOM: 0cm; WIDTH: 278.05pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=371>
            <div><span style="FONT-SIZE: 12pt">當用戶系統關閉</span><span style="FONT-SIZE: 12pt">Console</span><span style="FONT-SIZE: 12pt">時，系統會發送此信號到此</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: white; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 142.65pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=190>
            <div><font size=3>CTRL_LOGOFF_EVENT</font></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #d4d0c8; BACKGROUND: white; PADDING-BOTTOM: 0cm; WIDTH: 278.05pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=371>
            <div><span style="FONT-SIZE: 12pt">當用戶退出系統時系統會發送這個信號給所有的</span><span style="FONT-SIZE: 12pt">Console</span><span style="FONT-SIZE: 12pt">程序。該信號不能顯示是哪個用戶退出。</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: white; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 142.65pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=190>
            <div><font size=3>CTRL_SHUTDOWN_EVENT</font></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #d4d0c8; BACKGROUND: white; PADDING-BOTTOM: 0cm; WIDTH: 278.05pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=371>
            <div><span style="FONT-SIZE: 12pt">當系統將要關閉時會發送此信號到所有</span><span style="FONT-SIZE: 12pt">Console</span><span style="FONT-SIZE: 12pt">程序。</span></div>
            </td>
        </tr>
    </tbody>
</table>
<div><span style="FONT-SIZE: 12pt">&nbsp;</span></div>
<div style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">在程序中，</span>HandlerRoutine接收到上面那些事件的時候就可以進行相應的處理或忽略該事件。如果選擇忽略該事件，則HandlerRoutine必須返回FALSE，否則返回TRUE。</div>
<div style="TEXT-INDENT: 21pt">&nbsp;</div>
<div style="TEXT-INDENT: 21pt">我們用SetConsoleCtrlHandler安裝HandlerRoutine時，Add參數應設爲TRUE，想要刪除已經安裝的HandlerRoutine，請再用這個函數，只需把Add設爲FALSE即可。</div>
<div style="TEXT-INDENT: 21pt">&nbsp;</div>
<div style="TEXT-INDENT: 21pt">另外，得用<strong>GenerateConsoleCtrlEvent</strong>函數可以産生CTRL_C_EVENT和CTRL_BREAK_EVENT事件，利用這個函數我們就可以在我們程序中更加巧妙靈活的控制程序了。這個API使用方法非常簡單，我在這裏就不說了，可以參加MSDN或API手冊。</div>
<div style="TEXT-INDENT: 21pt">&nbsp;</div>
<div style="TEXT-INDENT: 21pt">下面我們就來寫一個非常簡單的程序：</div>
<div style="TEXT-INDENT: 21pt">&nbsp;</div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt; COLOR: blue">int</span><span style="FONT-SIZE: 9pt"> _tmain(<span style="COLOR: blue">int</span> argc, _TCHAR* argv[])</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">{</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">char</span>&nbsp;&nbsp;&nbsp;&nbsp; buf[256];</span></div>
<div style="BACKGROUND: #f3f3f3" align=left>&nbsp;</div>
<div style="BACKGROUND: #d2e2ee" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">if</span> (SetConsoleCtrlHandler(&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: green">// 安裝事件處理</span></span></div>
<div style="BACKGROUND: #d2e2ee" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; (PHANDLER_ROUTINE)ConsoleHandler, TRUE) == FALSE)</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; {</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: green">// 安裝失敗</span></span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; printf("Unable to install event handler!\n");</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">return</span> -1;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; }</span></div>
<div style="BACKGROUND: #f3f3f3" align=left>&nbsp;</div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: green">// 手工産生一個事件</span></span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; scanf("%s", buf);&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;&nbsp;&nbsp; <span style="COLOR: green">// 模擬等待事件發生，如果不要這句運行</span></span></div>
<div style="BACKGROUND: #f3f3f3; TEXT-INDENT: 252pt" align=left><span style="FONT-SIZE: 9pt; COLOR: green">// 程序的時候，程序一閃即過，來不急觀</span></div>
<div style="BACKGROUND: #f3f3f3; TEXT-INDENT: 252pt" align=left><span style="FONT-SIZE: 9pt; COLOR: green">// 察程序。</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; </span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">return</span> 0;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">}</span></div>
<div style="BACKGROUND: #f3f3f3" align=left>&nbsp;</div>
<div style="BACKGROUND: #f3f3f3" align=left>&nbsp;</div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">BOOL WINAPI ConsoleHandler(DWORD CEvent)</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">{</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">switch</span>(CEvent)</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; {</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">case</span> CTRL_C_EVENT:</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox(NULL,</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "CTRL + C received!", "signal", MB_OK);</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">break</span>;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">case</span> CTRL_BREAK_EVENT:</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox(NULL,</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "CTRL+BREAK received!", "signal", MB_OK);</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">break</span>;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">case</span> CTRL_CLOSE_EVENT:</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox(NULL,</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Program being closed!", "signal", MB_OK);</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">break</span>;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">case</span> CTRL_LOGOFF_EVENT:</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox(NULL,</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "User is logging off!", "signal", MB_OK);</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">break</span>;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">case</span> CTRL_SHUTDOWN_EVENT:</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox(NULL,</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "User is logging off!", "signal", MB_OK);</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">break</span>;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left>&nbsp;</div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; }</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">return</span> TRUE;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">}</span></div>
<div style="BACKGROUND: #f3f3f3; TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">&nbsp;</span></div>
<div style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">&nbsp;</span></div>
<div style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">這段程序非常簡單，但原理非常重要有用，但愿你们每個人都能看懂我在说什么，好了，不說了，最近工作非常忙，有時間的時候我想跟大家討論一下吧，希望對大家有所幫助。</span></div>
&nbsp;</div>
<img src ="http://www.cppblog.com/deane/aggbug/78593.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-04-01 19:46 <a href="http://www.cppblog.com/deane/articles/78593.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>捕获控制台程序的消息</title><link>http://www.cppblog.com/deane/articles/78594.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Wed, 01 Apr 2009 11:46:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/78594.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/78594.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/78594.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/78594.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/78594.html</trackback:ping><description><![CDATA[<h1 class=title_txt><img height=14 alt=转载 src="http://blog.csdn.net/images/turnship.gif" width=15 border=0>&nbsp;捕获控制台程序的消息</h1>
<div class=blogstory>
<script>function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}</script>
<div style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">控制台程序在</span><span style="FONT-SIZE: 12pt">Windows</span><span style="FONT-SIZE: 12pt">程序的角色中是非常強大且方便的，像</span><span style="FONT-SIZE: 12pt">VC</span><span style="FONT-SIZE: 12pt">，</span><span style="FONT-SIZE: 12pt">C#</span><span style="FONT-SIZE: 12pt">，</span><span style="FONT-SIZE: 12pt">Delphi</span><span style="FONT-SIZE: 12pt">等等，好多功能強大的語言都支持控制台程序。她沒有複雜的</span><span style="FONT-SIZE: 12pt">GUI</span><span style="FONT-SIZE: 12pt">，完全是</span><span style="FONT-SIZE: 12pt">32</span><span style="FONT-SIZE: 12pt">位的程序，能夠調用除</span><span style="FONT-SIZE: 12pt">GDI </span><span style="FONT-SIZE: 12pt">函數之外的</span><span style="FONT-SIZE: 12pt">API</span><span style="FONT-SIZE: 12pt">，支持多線程，支持</span><span style="FONT-SIZE: 12pt">MFC</span><span style="FONT-SIZE: 12pt">等等。用她來調試程序、學習程序設計、做實驗等是再合適不過的了。我經常把我試驗性的程序用控制台方式來寫，非常方便。</span></div>
<div><span style="FONT-SIZE: 12pt">&nbsp;</span></div>
<div style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">Console</span><span style="FONT-SIZE: 12pt">程序不像</span><span style="FONT-SIZE: 12pt">Win32 GUI</span><span style="FONT-SIZE: 12pt">程序那樣具有消息隊列，所以當程序中斷的時候也無從得知。假如我們程序正在處理一個長時間的作業，當用戶要退出系統，或按了</span><span style="FONT-SIZE: 12pt">Ctrl + C(Ctrl + Break)</span><span style="FONT-SIZE: 12pt">，或系統將要關閉的時候，我們的數據就可能會因此而丟失。難道沒有辦法了么？哦，不，有辦法的。看下面，下面我將跟大家談談關於</span><span style="FONT-SIZE: 12pt">Console</span><span style="FONT-SIZE: 12pt">程序的事件處理。</span></div>
<div><span style="FONT-SIZE: 12pt">&nbsp;</span></div>
<div style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">要讓控制台程序具有事件處理能力需要用到下面幾個</span><span style="FONT-SIZE: 12pt">API</span><span style="FONT-SIZE: 12pt">函數，他們的原型聲明如下：</span></div>
<div style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">&nbsp;</span></div>
<pre><font style="BACKGROUND-COLOR: #fbedbb" size=2>BOOL SetConsoleCtrlHandler(</font></pre>
<pre><font size=2><font style="BACKGROUND-COLOR: #fbedbb"><span>&nbsp;&nbsp;&nbsp; PHANDLER_ROUTINE HandlerRoutine, &nbsp;</span><span><em><font color=#008000>// handler function</font></em></span></font></font></pre>
<pre><font size=2><font style="BACKGROUND-COLOR: #fbedbb"><span>&nbsp;&nbsp;&nbsp; BOOL Add  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span><em><font color=#008000>// add or remove handler</font></em></span></font></font></pre>
<div>);</div>
<div>&nbsp;</div>
<div>HandlerRoutine指向一個事件處理函數，是的，可能你已經猜到了，這個函數相當於Win32 GUI程序中的窗口處理函數。這個函數的原型如下：</div>
<div>&nbsp;</div>
<pre><font style="BACKGROUND-COLOR: #fbedbb" size=2>BOOL WINAPI HandlerRoutine(</font></pre>
<pre><font size=2><font style="BACKGROUND-COLOR: #fbedbb"><span>&nbsp;&nbsp;&nbsp; DWORD dwCtrlType&nbsp;&nbsp;  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span><em><font color=#008000>//&nbsp;control signal type</font></em></span></font></font></pre>
<div>);</div>
<div>&nbsp;</div>
<div>這個函數的dwCtrlType指示出接收到的控制信號，這個參數可能是下面值中的某一個：</div>
<div><span style="FONT-SIZE: 12pt">&nbsp;</span></div>
<table style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: #e0e0e0; MARGIN-LEFT: 5.4pt; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" cellSpacing=0 cellPadding=0 bgColor=#e0e0e0 border=1>
    <tbody>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 142.65pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=190>
            <div><strong><span style="FONT-SIZE: 12pt">信</span></strong><strong><span style="FONT-SIZE: 12pt">號</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #d4d0c8; PADDING-BOTTOM: 0cm; WIDTH: 278.05pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=371>
            <div><strong><span style="FONT-SIZE: 12pt">描</span></strong><strong><span style="FONT-SIZE: 12pt">述</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: white; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 142.65pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=190>
            <div><font size=3>CTRL_C_EVENT</font></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #d4d0c8; BACKGROUND: white; PADDING-BOTTOM: 0cm; WIDTH: 278.05pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=371>
            <div><span style="FONT-SIZE: 12pt">一個</span><span style="FONT-SIZE: 12pt">Ctrl + C</span><span style="FONT-SIZE: 12pt">的信號被接收，該信號或來自鍵盤，或來自</span><font size=3><strong>GenerateConsoleCtrlEvent </strong>函數</font></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: white; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 142.65pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=190>
            <div><font size=3>CTRL_BREAK_EVENT</font></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #d4d0c8; BACKGROUND: white; PADDING-BOTTOM: 0cm; WIDTH: 278.05pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=371>
            <div><span style="FONT-SIZE: 12pt">一個</span><span style="FONT-SIZE: 12pt"> Ctrl + Break </span><span style="FONT-SIZE: 12pt">信號被接收，該信號或來自鍵盤，或來自</span><font size=3><strong>GenerateConsoleCtrlEvent </strong>函數</font></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: white; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 142.65pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=190>
            <div><font size=3>CTRL_CLOSE_EVENT</font></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #d4d0c8; BACKGROUND: white; PADDING-BOTTOM: 0cm; WIDTH: 278.05pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=371>
            <div><span style="FONT-SIZE: 12pt">當用戶系統關閉</span><span style="FONT-SIZE: 12pt">Console</span><span style="FONT-SIZE: 12pt">時，系統會發送此信號到此</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: white; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 142.65pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=190>
            <div><font size=3>CTRL_LOGOFF_EVENT</font></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #d4d0c8; BACKGROUND: white; PADDING-BOTTOM: 0cm; WIDTH: 278.05pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=371>
            <div><span style="FONT-SIZE: 12pt">當用戶退出系統時系統會發送這個信號給所有的</span><span style="FONT-SIZE: 12pt">Console</span><span style="FONT-SIZE: 12pt">程序。該信號不能顯示是哪個用戶退出。</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: white; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 142.65pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=190>
            <div><font size=3>CTRL_SHUTDOWN_EVENT</font></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #d4d0c8; BACKGROUND: white; PADDING-BOTTOM: 0cm; WIDTH: 278.05pt; BORDER-TOP-COLOR: #d4d0c8; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" vAlign=top width=371>
            <div><span style="FONT-SIZE: 12pt">當系統將要關閉時會發送此信號到所有</span><span style="FONT-SIZE: 12pt">Console</span><span style="FONT-SIZE: 12pt">程序。</span></div>
            </td>
        </tr>
    </tbody>
</table>
<div><span style="FONT-SIZE: 12pt">&nbsp;</span></div>
<div style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">在程序中，</span>HandlerRoutine接收到上面那些事件的時候就可以進行相應的處理或忽略該事件。如果選擇忽略該事件，則HandlerRoutine必須返回FALSE，否則返回TRUE。</div>
<div style="TEXT-INDENT: 21pt">&nbsp;</div>
<div style="TEXT-INDENT: 21pt">我們用SetConsoleCtrlHandler安裝HandlerRoutine時，Add參數應設爲TRUE，想要刪除已經安裝的HandlerRoutine，請再用這個函數，只需把Add設爲FALSE即可。</div>
<div style="TEXT-INDENT: 21pt">&nbsp;</div>
<div style="TEXT-INDENT: 21pt">另外，得用<strong>GenerateConsoleCtrlEvent</strong>函數可以産生CTRL_C_EVENT和CTRL_BREAK_EVENT事件，利用這個函數我們就可以在我們程序中更加巧妙靈活的控制程序了。這個API使用方法非常簡單，我在這裏就不說了，可以參加MSDN或API手冊。</div>
<div style="TEXT-INDENT: 21pt">&nbsp;</div>
<div style="TEXT-INDENT: 21pt">下面我們就來寫一個非常簡單的程序：</div>
<div style="TEXT-INDENT: 21pt">&nbsp;</div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt; COLOR: blue">int</span><span style="FONT-SIZE: 9pt"> _tmain(<span style="COLOR: blue">int</span> argc, _TCHAR* argv[])</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">{</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">char</span>&nbsp;&nbsp;&nbsp;&nbsp; buf[256];</span></div>
<div style="BACKGROUND: #f3f3f3" align=left>&nbsp;</div>
<div style="BACKGROUND: #d2e2ee" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">if</span> (SetConsoleCtrlHandler(&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: green">// 安裝事件處理</span></span></div>
<div style="BACKGROUND: #d2e2ee" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; (PHANDLER_ROUTINE)ConsoleHandler, TRUE) == FALSE)</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; {</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: green">// 安裝失敗</span></span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; printf("Unable to install event handler!\n");</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">return</span> -1;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; }</span></div>
<div style="BACKGROUND: #f3f3f3" align=left>&nbsp;</div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: green">// 手工産生一個事件</span></span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; scanf("%s", buf);&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;&nbsp;&nbsp; <span style="COLOR: green">// 模擬等待事件發生，如果不要這句運行</span></span></div>
<div style="BACKGROUND: #f3f3f3; TEXT-INDENT: 252pt" align=left><span style="FONT-SIZE: 9pt; COLOR: green">// 程序的時候，程序一閃即過，來不急觀</span></div>
<div style="BACKGROUND: #f3f3f3; TEXT-INDENT: 252pt" align=left><span style="FONT-SIZE: 9pt; COLOR: green">// 察程序。</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; </span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">return</span> 0;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">}</span></div>
<div style="BACKGROUND: #f3f3f3" align=left>&nbsp;</div>
<div style="BACKGROUND: #f3f3f3" align=left>&nbsp;</div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">BOOL WINAPI ConsoleHandler(DWORD CEvent)</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">{</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">switch</span>(CEvent)</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; {</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">case</span> CTRL_C_EVENT:</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox(NULL,</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "CTRL + C received!", "signal", MB_OK);</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">break</span>;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">case</span> CTRL_BREAK_EVENT:</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox(NULL,</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "CTRL+BREAK received!", "signal", MB_OK);</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">break</span>;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">case</span> CTRL_CLOSE_EVENT:</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox(NULL,</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Program being closed!", "signal", MB_OK);</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">break</span>;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">case</span> CTRL_LOGOFF_EVENT:</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox(NULL,</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "User is logging off!", "signal", MB_OK);</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">break</span>;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">case</span> CTRL_SHUTDOWN_EVENT:</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox(NULL,</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "User is logging off!", "signal", MB_OK);</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">break</span>;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left>&nbsp;</div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; }</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp; <span style="COLOR: blue">return</span> TRUE;</span></div>
<div style="BACKGROUND: #f3f3f3" align=left><span style="FONT-SIZE: 9pt">}</span></div>
<div style="BACKGROUND: #f3f3f3; TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">&nbsp;</span></div>
<div style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">&nbsp;</span></div>
<div style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt">這段程序非常簡單，但原理非常重要有用，但愿你们每個人都能看懂我在说什么，好了，不說了，最近工作非常忙，有時間的時候我想跟大家討論一下吧，希望對大家有所幫助。</span></div>
&nbsp;</div>
<img src ="http://www.cppblog.com/deane/aggbug/78594.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-04-01 19:46 <a href="http://www.cppblog.com/deane/articles/78594.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC2005运行库-解决方案</title><link>http://www.cppblog.com/deane/articles/77760.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Tue, 24 Mar 2009 11:34:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/77760.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/77760.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/77760.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/77760.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/77760.html</trackback:ping><description><![CDATA[<p><br>&nbsp;</p>
<p>VC2005运行库是VC2005编译出来的程序运行所依赖的库（不包括.NET支持），采用manifest方式来指定dll文件。如果将程序.exe文件直接拷贝到没有安装过VC2005的计算机上运行运行，往往会出现找不到msvcr80.dll、mfc80.dll文件等错误。</p>
<p>解决方案：<br>解决方式一、在目标系统上安装2005版vcredist_x86.exe（全称Microsoft Visual C++ 2005 Redistributable Package (x86)）。微软官方下载地址：<a title=http://download.microsoft.com/download/d/3/4/d342efa6-3266-4157-a2ec-5174867be706/vcredist_x86.exe href="http://download.microsoft.com/download/d/3/4/d342efa6-3266-4157-a2ec-5174867be706/vcredist_x86.exe"><u><font color=#0000ff>http://download.microsoft.com/download/d/3/4/d342efa6-3266-4157-a2ec-5174867be706/vcredist_x86.exe</font></u></a></p>
<p>解决方案二、直接拷贝VS8目录下的VC \ redist \ x86 \ 目录下的 Microsoft.VC80.MFC、Microsoft.VC80.CRT、Microsoft.VC80.MFCLOC几个文件夹，到exe所在的目录下，目录结构如下：<br>.\myapp.exe<br>.\myapp.dll<br>.\Microsoft.VC80.CRT\<br>.\Microsoft.VC80.MFC\<br>.\Microsoft.VC80.MFC\Microsoft.VC80.MFCLOC\<br>然后修改Microsoft.VC80.MFCLOC目录下的Microsoft.VC80.MFCLOC.manifest文件，将其中的version=&#8221;8.0.50727.42&#8243;，修改为version=&#8221;8.0.50608.0&#8243;。<br>为了避免保证VS8的完整性，建议先将Microsoft.VC80.MFCLOC的文件拷出来后再修改。<br><br></p>
<img src ="http://www.cppblog.com/deane/aggbug/77760.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-03-24 19:34 <a href="http://www.cppblog.com/deane/articles/77760.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>STL中的排序算法一览[By ACM郭老师] </title><link>http://www.cppblog.com/deane/articles/76345.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Thu, 12 Mar 2009 07:55:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/76345.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/76345.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/76345.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/76345.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/76345.html</trackback:ping><description><![CDATA[<br><br>这篇文章我很喜欢，是郭老师的新作！希望大家喜欢！<br>详细的从算法的效率方面来说明了排序算法！<br><a name=entrymore></a><br>STL中有多种排序算法，各有各的适用范围，下面听我一一道来：<br><br><span style="COLOR: #ff0000">I、完全排序</span><br>sort() <br>首先要隆重推出的当然是最最常用的sort了，sort有两种形式，第一种形式有两个迭代器参数，构成一个前开后闭的区间，按照元素的 less 关系排序；第二种形式多加一个指定排序准则的谓词。sort基本是最通用的排序函数，它使用快速排序算法，并且在递归过程中，当元素数目小于一个阈值（一般是16，我的试验是24）时，转成直接插入排序。伟大的数学家Knuth已经证明，在平均意义上，快速排序是最快的了；当然，最坏复杂性比较差。sort要求随机迭代器，因此对于很多编译器来说，对于前向迭代器（如list）使用sort是一个编译错误。(不过，在vc2005里面，这个错误信息实在很糟糕)<br><br>sort的基本使用方式如下：<br><br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>view plain</font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080>copy to clipboard</font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>print</font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080>?</font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span>C++: &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class=alt><span></span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;&lt;vector&gt; </font></strong></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span></span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;&lt;algorithm&gt; </font></strong></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span></span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;&lt;functional&gt; </font></strong></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span></span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;&lt;cstdlib&gt; </font></strong></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span></span><span class=keyword><strong><font color=#5697d9>using</font></strong></span><span>&nbsp;</span><span class=keyword><strong><font color=#5697d9>namespace</font></strong></span><span>&nbsp;std; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span></span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;func1() &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>{ &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;vector&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&gt;&nbsp;ar; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//向数组里面插入一些随机数 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;generate_n(back_inserter(ar),&nbsp;100,&nbsp;rand); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//按从小到大排序 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(ar.begin(),&nbsp;ar.end()); &nbsp;&nbsp;</span></li>
    <li class=alt><span>}&nbsp; &nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>C++:
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;functional&gt;
#include &lt;cstdlib&gt;
using namespace std;
void func1()
{
&nbsp; &nbsp;vector&lt;int&gt; ar;
&nbsp; &nbsp;//向数组里面插入一些随机数
&nbsp; &nbsp;generate_n(back_inserter(ar), 100, rand);
&nbsp; &nbsp;//按从小到大排序
&nbsp; &nbsp;sort(ar.begin(), ar.end());
}
</textarea><br>经常有人问如何从大到小逆排序，这个其实有很多中方式实现，如下面的例子：<br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>view plain</font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080>copy to clipboard</font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>print</font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080>?</font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span>C++: &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class=alt><span></span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;func2() &nbsp;&nbsp;</span></span></li>
    <li class=""><span>{ &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;vector&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&gt;&nbsp;ar; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//向数组里面插入一些随机数 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;generate_n(back_inserter(ar),&nbsp;100,&nbsp;rand); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp; &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//方法1：使用函数作为谓词 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(ar.begin(),&nbsp;ar.end(),&nbsp;GreateThan); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//方法2：使用仿函数作为谓词 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//注意下面两种方法都需要有个括号，实际上是要产生一个临时对象 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(ar.begin(),&nbsp;ar.end(),&nbsp;CompareInt()); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//方法3：使用预定义的Adapter,&nbsp;定义在&nbsp;&lt;functional&gt;&nbsp;中 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(ar.begin(),&nbsp;ar.end(),&nbsp;greater&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&gt;()); &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//方法4：正常排序，然后翻转过来 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(ar.begin(),&nbsp;ar.end()); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;reverse(ar.begin(),&nbsp;ar.end()); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//方法5：使用逆迭代器 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(ar.rbegin(),&nbsp;ar.rend()); &nbsp;&nbsp;</span></li>
    <li class=alt><span>}&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>C++:
void func2()
{
&nbsp; &nbsp;vector&lt;int&gt; ar;
&nbsp; &nbsp;//向数组里面插入一些随机数
&nbsp; &nbsp;generate_n(back_inserter(ar), 100, rand);
&nbsp; &nbsp;//方法1：使用函数作为谓词
&nbsp; &nbsp;sort(ar.begin(), ar.end(), GreateThan);
&nbsp; &nbsp;//方法2：使用仿函数作为谓词
&nbsp; &nbsp;//注意下面两种方法都需要有个括号，实际上是要产生一个临时对象
&nbsp; &nbsp;sort(ar.begin(), ar.end(), CompareInt());
&nbsp; &nbsp;//方法3：使用预定义的Adapter, 定义在 &lt;functional&gt; 中
&nbsp; &nbsp;sort(ar.begin(), ar.end(), greater&lt;int&gt;());
&nbsp; &nbsp;//方法4：正常排序，然后翻转过来
&nbsp; &nbsp;sort(ar.begin(), ar.end());
&nbsp; &nbsp;reverse(ar.begin(), ar.end());
&nbsp; &nbsp;//方法5：使用逆迭代器
&nbsp; &nbsp;sort(ar.rbegin(), ar.rend());
}
</textarea><br>最后一种方法是我比较欣赏的，可以不能直接对原生数组使用，也就是说，如果ar的定义是int ar[MAXN]，上面其他的排序算法都可以简单的改成sort(ar, ar+MAXN, ...），但最后一个不行，要用另外一种比较丑陋的方式：<br><br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>view plain</font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080>copy to clipboard</font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>print</font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080>?</font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span>C++: &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class=alt><span></span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;&lt;iterator&gt; </font></strong></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span></span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;func3(){ &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;ax[5]={1,3,4,5,2}; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(reverse_iterator&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>*&gt;(ax+5),&nbsp;reverse_iterator&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>*&gt;(ax+0)); &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>}&nbsp; &nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>C++:
#include &lt;iterator&gt;
void func3(){
&nbsp; &nbsp;int ax[5]={1,3,4,5,2};
&nbsp; &nbsp;sort(reverse_iterator&lt;int*&gt;(ax+5), reverse_iterator&lt;int*&gt;(ax+0));
}
</textarea><br>stable_sort <br>sort优点一大堆，一个缺点就是它不是一种稳定的排序。什么是排序的稳定性，就是如果出现两个元素相等时，要求排序之后他们之间保持原来的次序（比如我们先按学号排序，然后按成绩排序，这时就希望成绩相同的还是按照学号的次序排）。很可惜，快速排序算法就不是稳定的，要追求这个，只好用stable_sort了。<br><br>在各种排序算法中，合并排序是稳定的，但一般的合并排序需要额外的O(N)的存储空间，而这个条件不是一定能够满足的（可能是比较奢侈的）。所以在stable_sort内部，首先判断是否有足够的额外空间（如vecotr中的cap-size()部分），有的话就使用普通合并函数，总的时间复杂性和快速排序一个数量级，都是O(N*logN)。如果没有额外空间，使用了一个merge_without_buffer的关键函数进行就地合并（如何实现是比较有技巧的，完全可以专门谈一谈），这个合并过程不需要额外的存储空间，但时间复杂度变成O(N*logN)，这种情况下，总的stable_sort时间复杂度是O(N*logN*logN)。<br><br>总之，stable_sort稍微慢一点儿，但能够保证稳定，使用方法和sort一样。但很多时候可以不用这种方式和这个函数，比如上面的例子，完全可以在排序比较准则中写入成绩和学号两个条件就OK了<br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>view plain</font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080>copy to clipboard</font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>print</font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080>?</font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span>C++: &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class=alt><span></span><span class=keyword><strong><font color=#5697d9>class</font></strong></span><span>&nbsp;CStudent &nbsp;&nbsp;</span></span></li>
    <li class=""><span>{ &nbsp;&nbsp;</span></li>
    <li class=alt><span></span><span class=keyword><strong><font color=#5697d9>public</font></strong></span><span>: &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;CStudent(); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//注意这个比较函数中的const </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>bool</font></strong></span><span>&nbsp;operator&lt;(</span><span class=keyword><strong><font color=#5697d9>const</font></strong></span><span>&nbsp;CStudent&amp;&nbsp;rhs)&nbsp;</span><span class=keyword><strong><font color=#5697d9>const</font></strong></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>if</font></strong></span><span>&nbsp;(m_score&nbsp;!=&nbsp;rhs.m_score) &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>return</font></strong></span><span>&nbsp;(m_score&nbsp;&lt;rhs.m_score); &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>return</font></strong></span><span>&nbsp;m_name&nbsp;&lt;rhs.m_name; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li class=""><span></span><span class=keyword><strong><font color=#5697d9>protected</font></strong></span><span>: &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;std::string&nbsp;m_name; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;m_score; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>}; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp; &nbsp;&nbsp;</span></li>
    <li class=alt><span></span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;func4() &nbsp;&nbsp;</span></span></li>
    <li class=""><span>{ &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;vector&lt;CStudent&gt;&nbsp;arStu; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(arStu.begin(),&nbsp;arStu.end()); &nbsp;&nbsp;</span></li>
    <li class=alt><span>}&nbsp; &nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>C++:
class CStudent
{
public:
&nbsp; &nbsp;CStudent();
&nbsp; &nbsp;//注意这个比较函数中的const
&nbsp; &nbsp;bool operator&lt;(const CStudent&amp; rhs) const
&nbsp; &nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp;if (m_score != rhs.m_score)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return (m_score &lt;rhs.m_score);
&nbsp; &nbsp; &nbsp; &nbsp;return m_name &lt;rhs.m_name;
&nbsp; &nbsp;}
protected:
&nbsp; &nbsp;std::string m_name;
&nbsp; &nbsp;int m_score;
};
void func4()
{
&nbsp; &nbsp;vector&lt;CStudent&gt; arStu;
&nbsp; &nbsp;sort(arStu.begin(), arStu.end());
}
</textarea><br>sort_heap <br>堆排序也是一种快速的排序算法，复杂度也是O(N*logN)。STL中有一些和堆相关的函数，能够构造堆，如果在构造好的堆上每次取出来根节点放在尾部，所有元素循环一遍，最后的结果也就有序了。这就是sort_heap了。它的使用要求区间前面已经构造成堆，如：<br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>view plain</font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080>copy to clipboard</font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>print</font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080>?</font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span>C++: &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class=alt><span></span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;func5() &nbsp;&nbsp;</span></span></li>
    <li class=""><span>{ &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;vector&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&gt;&nbsp;ar; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;generate_n(back_inserter(ar),&nbsp;100,&nbsp;rand); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;make_heap(ar.begin(),&nbsp;ar.end()); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;sort_heap(ar.begin(),&nbsp;ar.end()); &nbsp;&nbsp;</span></li>
    <li class=alt><span>}&nbsp; &nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>C++:
void func5()
{
&nbsp; &nbsp;vector&lt;int&gt; ar;
&nbsp; &nbsp;generate_n(back_inserter(ar), 100, rand);
&nbsp; &nbsp;make_heap(ar.begin(), ar.end());
&nbsp; &nbsp;sort_heap(ar.begin(), ar.end());
}
</textarea><br>list.sort <br>对于list容器，是不能直接使用sort的（包括stable_sort），从技术的角度来说是由于sort要求随机迭代器；从算法的角度来说，list这种链表结构就不适合用快速排序。因此，list容器内部实现了专门的sort算法，这个算法采用的是合并排序，应该是稳定的（不确定）。<br><br>其他 <br>优先队列（priority_queue）每次弹出的都是max值。实际上就是heap的一个容器方式的包装。 <br>关联式容器自身就必须是有序的（针对key），对其迭代时，key是递增的。 <br><span style="COLOR: #ff0000">II、部分排序</span><br>这些部分排序功能能够完成一段数据（而不是所有）的排序，在适当的适合使用可以节省计算量。不过用的人不多。<br><br>partial_sort(), partial_sort_copy() <br>这两个函数能够将整个区间中给定数目的元素进行排序，也就是说，结果中只有最小的M个元素是有序的。你当然也可以使用sort，区别就在于效率。如果M显著地小于N，时间就比较短；当然M太小了也不好，那还不如挨个找最小值了。<br><br>partial_sort接受三个参数，分别是区间的头，中间和结尾。执行后，将前面M（M=中间－头）个元素有序地放在前面，后面的元素肯定是比前面的大，但他们内部的次序没有保证。partial_sort_copy的区别在于把结果放到另外指定的迭代器区间中： <br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>view plain</font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080>copy to clipboard</font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>print</font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080>?</font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span>C++: &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class=alt><span></span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;func6() &nbsp;&nbsp;</span></span></li>
    <li class=""><span>{ &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;ar[12]={69,23,80,42,17,15,26,51,19,12,35,8}; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//只排序前7个数据 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;partial_sort(ar,&nbsp;ar+7,&nbsp;ar+12); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//结果是&nbsp;8&nbsp;12&nbsp;15&nbsp;17&nbsp;19&nbsp;23&nbsp;26&nbsp;80&nbsp;69&nbsp;51&nbsp;42&nbsp;35，后5个数据不定 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;vector&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&gt;&nbsp;res(7); &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//前7项排序后放入res </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;partial_sort_copy(ar,&nbsp;ar+7,&nbsp;res.begin(),&nbsp;res.end(),&nbsp;greater&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&gt;()&nbsp;); &nbsp;&nbsp;</span></span></li>
    <li class=""><span>}&nbsp; &nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>C++:
void func6()
{
&nbsp; &nbsp;int ar[12]={69,23,80,42,17,15,26,51,19,12,35,8};
&nbsp; &nbsp;//只排序前7个数据
&nbsp; &nbsp;partial_sort(ar, ar+7, ar+12);
&nbsp; &nbsp;//结果是 8 12 15 17 19 23 26 80 69 51 42 35，后5个数据不定
&nbsp; &nbsp;vector&lt;int&gt; res(7);
&nbsp; &nbsp;//前7项排序后放入res
&nbsp; &nbsp;partial_sort_copy(ar, ar+7, res.begin(), res.end(), greater&lt;int&gt;() );
}
</textarea><br>这两个函数的实现使用的是堆的方法，先将前M个元素构造成堆，然后挨个检查后面的元素，看看是否小于堆的最大值，是的话就彼此交换，然后重排堆；最后将前面已经是最小的M个元素构成的堆作一次sort_heap就可以了。算法的复杂度差不多是O(N*logM)<br><br>nth_element <br>这个函数只真正排序出一个元素来，就是第n个。函数有三个迭代器的输入（当然还可以加上一个谓词），执行完毕后，中间位置指向的元素保证和完全排序后这个位置的元素一致，前面区间的元素都小于（精确地说，是不大于）后面区间的元素。<br><br>熟悉快速排序的马上就能发现，这实际上是一个按位置划分的算法。STL的规范中要求此函数的平均复杂度是线性的，和快速排序一样，这种算法的最坏复杂度比较差。在一般的实现（如SGI）中，采用三种取1的方法寻找划分元素，最坏复杂度是O(N^N)。虽然理论上有一些算法可以保证最坏线性复杂度，但算法过于复杂，STL一般也不采用。 <br><br><span style="COLOR: #ff0000">III、排序辅助功能</span><br>partition, stable_partition <br>merge, inplace_merge <br><span style="COLOR: #ff0000">IV、有序区间操作</span><br><br>这个准备单独写一篇<br><br>
<div class=quote>
<div class=quote-title>引用</div>
<div class=quote-content><br>让我们继续期待郭老师的补充！<br><br>郭老师的boke地址 www.skywind.name/blog<br></div>
</div>
<br><br><br>
<img src ="http://www.cppblog.com/deane/aggbug/76345.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-03-12 15:55 <a href="http://www.cppblog.com/deane/articles/76345.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>STL概览-关联容器set,multiset,map,multimap </title><link>http://www.cppblog.com/deane/articles/76343.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Thu, 12 Mar 2009 07:52:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/76343.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/76343.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/76343.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/76343.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/76343.html</trackback:ping><description><![CDATA[<h1 class=postTitle><a class=postTitle2 id=AjaxHolder_ctl01_TitleUrl href="http://www.cnblogs.com/jcss2008/archive/2008/12/12/1353490.html"><font color=#6466b3>STL概览-关联容器set,multiset,map,multimap</font></a> </h1>
<div class=clear><br></div>
<div class=postBody>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-WEIGHT: bold; FONT-FAMILY: SimSun">关联式容器</span><span style="FONT-WEIGHT: bold; FONT-FAMILY: Verdana">associative container:</span><span style="FONT-FAMILY: SimSun">被插入的元素并没有一个固定的位置。这不仅是指操作者可能更改其中元素的位置，还有可能——每当新插入一个元素时，容器都会自动的按照某种排序规则将新来的元素放置在合适的位置。也即，这种容器内元素的排列顺序由容器自己的排序规则决定，操作者无能为力。</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: SimSun">&nbsp;</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">==============================================================</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">Set(multiset)</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|-&gt;</span><span style="FONT-FAMILY: SimSun">名称</span><span style="FONT-FAMILY: Verdana">-----&gt;set</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|-&gt;</span><span style="FONT-FAMILY: SimSun">个性</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt; </span><span style="FONT-FAMILY: 宋体">①</span><span style="FONT-FAMILY: Verdana">set</span><span style="FONT-FAMILY: 宋体">的底层实现是基于平衡二叉树的</span><span style="FONT-FAMILY: Verdana">(</span><span style="FONT-FAMILY: 宋体">当然标准书中并没有这么规定）</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt; </span><span style="FONT-FAMILY: 宋体">②</span><span style="FONT-FAMILY: 宋体">由于关联式容器是顺序的，那么</span><span style="FONT-FAMILY: Verdana">set</span><span style="FONT-FAMILY: 宋体">就不允许对其中的元素作直接的修改，否则所谓</span><span style="FONT-FAMILY: Verdana">&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: 宋体">的关联式将不再关联——容器中的元素已经不再符合某种顺序了。</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt; </span><span style="FONT-FAMILY: 宋体">③</span><span style="FONT-FAMILY: Verdana">set</span><span style="FONT-FAMILY: 宋体">的排序规则有两种确定方式，一是采用模板参数，一是采用构造参数。前者使得排|&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 序</span><span style="FONT-FAMILY: 宋体">规则成为</span><span style="FONT-FAMILY: Verdana">set</span><span style="FONT-FAMILY: 宋体">类型的一部分（在对整个容器作比较等运算的时候，这个是比需的）；|&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;后者</span><span style="FONT-FAMILY: 宋体">仅在构造一个实例对像的时候用到，但其类型不会改变（虽然排序规则发生了变&nbsp; |&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 化），此</span><span style="FONT-FAMILY: 宋体">处的排序规则通常比模板参数中的规则具有更高的优先级。</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt; </span><span style="FONT-FAMILY: 宋体">④</span><span style="FONT-FAMILY: 宋体">由于</span><span style="FONT-FAMILY: Verdana">STL</span><span style="FONT-FAMILY: 宋体">基本上采用的是</span><span style="FONT-FAMILY: Verdana">reference</span><span style="FONT-FAMILY: 宋体">语义，故要求其元素必须具备</span><span style="FONT-FAMILY: Verdana">&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;assignable,copyable,comparable</span><span style="FONT-FAMILY: 宋体">。</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|-&gt;</span><span style="FONT-FAMILY: 宋体">陷阱</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt; </span><span style="FONT-FAMILY: 宋体">①</span><span style="FONT-FAMILY: Verdana">insert</span><span style="FONT-FAMILY: 宋体">和</span><span style="FONT-FAMILY: Verdana">erase</span><span style="FONT-FAMILY: 宋体">的参数类型不一样的时候其返回的类型也是不一样的。对于</span><span style="FONT-FAMILY: Verdana">set</span><span style="FONT-FAMILY: 宋体">而言，&nbsp; </span><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;insert(value)</span><span style="FONT-FAMILY: 宋体">会返回一个</span><span style="FONT-FAMILY: Verdana">pair(pos,bool)</span><span style="FONT-FAMILY: 宋体">，而</span><span style="FONT-FAMILY: Verdana">insert(pos)</span><span style="FONT-FAMILY: 宋体">则同样返回一个</span><span style="FONT-FAMILY: Verdana">iterator</span><span style="FONT-FAMILY: 宋体">的</span><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos</span><span style="FONT-FAMILY: 宋体">；</span><span style="FONT-FAMILY: Verdana">erase(value)</span><span style="FONT-FAMILY: 宋体">会返回被删除元素的个数，而</span><span style="FONT-FAMILY: Verdana">erase(pos)</span><span style="FONT-FAMILY: 宋体">则不会返回任何东西，|&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;它</span><span style="FONT-FAMILY: 宋体">实际上是一个过程式的成员函数。</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt; </span><span style="FONT-FAMILY: 宋体">②</span><span style="FONT-FAMILY: 宋体">两个</span><span style="FONT-FAMILY: Verdana">set</span><span style="FONT-FAMILY: 宋体">容器的比较是按照字典的方式进行的。但一定要注意，比较的两个</span><span style="FONT-FAMILY: Verdana">set</span><span style="FONT-FAMILY: 宋体">容器必&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 须要</span><span style="FONT-FAMILY: 宋体">具有相同的类型，特别是在模板参数中给出了排序规则参数的时候，就得注意这&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 些参数是</span><span style="FONT-FAMILY: 宋体">否一致——该参数同样决定着你所用的</span><span style="FONT-FAMILY: Verdana">set</span><span style="FONT-FAMILY: 宋体">的类型。</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|-&gt;</span><span style="FONT-FAMILY: SimSun">说明</span><span style="FONT-FAMILY: Verdana">-----&gt;multiset</span><span style="FONT-FAMILY: SimSun">与</span><span style="FONT-FAMILY: Verdana">set</span><span style="FONT-FAMILY: SimSun">的用法基本一样，不同的是它允许出现相同的值得元素。</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|-&gt;Type-----&gt;class</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|-&gt;Include---&gt;&lt;set&gt; </p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|-&gt;Define----&gt;set&lt;class T,optional compare,optional&gt; </p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|-&gt;Sub</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt;constructor</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |-&gt;default,copy,assignmet</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|-&gt;Fun</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt;NoModify operate</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |-&gt;.size() .max_size() .empty() (</span><span style="FONT-FAMILY: SimSun">各种</span><span style="FONT-FAMILY: Verdana">compare operator)</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt;seek operator</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |-&gt;.count(elem) .find(elem) .lower_bound(elem) .upper_bound(elem) </p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">&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; .equal_range(elem)</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt;iterator</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |-&gt;.begin() .end() .rbegin() .rend() </p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt;add&amp;del</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |-&gt;.insert(elem) .insert(pos,elem) .insert(beg,end) </p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |-&gt;.erase(elem) .erase(pos) .erase(beg,end) .clear()</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: SimSun">&nbsp;</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: SimSun">&nbsp;</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">==============================================================</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">Map(mulitmap)</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|-&gt;</span><span style="FONT-FAMILY: SimSun">名称</span><span style="FONT-FAMILY: Verdana">-----&gt;map</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|-&gt;</span><span style="FONT-FAMILY: SimSun">个性</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt; </span><span style="FONT-FAMILY: 宋体">①</span><span style="FONT-FAMILY: Verdana">map</span><span style="FONT-FAMILY: 宋体">与</span><span style="FONT-FAMILY: Verdana">set</span><span style="FONT-FAMILY: 宋体">的最大区别在于</span><span style="FONT-FAMILY: Verdana">map</span><span style="FONT-FAMILY: 宋体">是一种特殊的</span><span style="FONT-FAMILY: Verdana">set</span><span style="FONT-FAMILY: 宋体">，它的所有元素都是</span><span style="FONT-FAMILY: Verdana">pair&lt;key,value&gt;</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt; </span><span style="FONT-FAMILY: 宋体">②</span><span style="FONT-FAMILY: Verdana">map</span><span style="FONT-FAMILY: 宋体">最大的特性在于</span><span style="FONT-FAMILY: Verdana">map</span><span style="FONT-FAMILY: 宋体">提供了下标</span><span style="FONT-FAMILY: Verdana">subscript</span><span style="FONT-FAMILY: 宋体">操作的能力，你可以向数组一样操作&nbsp; </span><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;map[key]</span><span style="FONT-FAMILY: 宋体">来引用相应的值。这个除了方便以外同样也是问题的根源。</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt; </span><span style="FONT-FAMILY: 宋体">③</span><span style="FONT-FAMILY: 宋体">几乎所有针对</span><span style="FONT-FAMILY: Verdana">map</span><span style="FONT-FAMILY: 宋体">的操作都是基于</span><span style="FONT-FAMILY: Verdana">key</span><span style="FONT-FAMILY: 宋体">的。比如，排序就是通过比较</span><span style="FONT-FAMILY: Verdana">key</span><span style="FONT-FAMILY: 宋体">来进行的。</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt; </span><span style="FONT-FAMILY: 宋体">④</span><span style="FONT-FAMILY: 宋体">对于</span><span style="FONT-FAMILY: Verdana">set</span><span style="FONT-FAMILY: 宋体">成立的操作在</span><span style="FONT-FAMILY: Verdana">map</span><span style="FONT-FAMILY: 宋体">中基本上都成立</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|-&gt;</span><span style="FONT-FAMILY: 宋体">陷阱</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt; </span><span style="FONT-FAMILY: 宋体">①</span><span style="FONT-FAMILY: 宋体">如果你采用了这样的语句</span><span style="FONT-FAMILY: Verdana">erase(pos)</span><span style="FONT-FAMILY: 宋体">——其中的</span><span style="FONT-FAMILY: Verdana">pos</span><span style="FONT-FAMILY: 宋体">是个</span><span style="FONT-FAMILY: Verdana">iterator</span><span style="FONT-FAMILY: 宋体">，那么最好不要在 |&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对该</span><span style="FONT-FAMILY: Verdana">pos</span><span style="FONT-FAMILY: 宋体">最任何操作，应为</span><span style="FONT-FAMILY: Verdana">erase(pos)</span><span style="FONT-FAMILY: 宋体">已经将这个</span><span style="FONT-FAMILY: Verdana">pos</span><span style="FONT-FAMILY: 宋体">删除了，此后任何关于</span><span style="FONT-FAMILY: Verdana">pos</span><span style="FONT-FAMILY: 宋体">的操作|&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 都视</span><span style="FONT-FAMILY: 宋体">为定义的。这种情况要是发生在</span><span style="FONT-FAMILY: Verdana">for</span><span style="FONT-FAMILY: 宋体">循环中，</span><span style="FONT-FAMILY: Verdana">for(pos=.begin(),pos!=.end&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (),pos++)</span><span style="FONT-FAMILY: 宋体">就</span><span style="FONT-FAMILY: 宋体">能解决问题了。</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt; </span><span style="FONT-FAMILY: 宋体">②</span><span style="FONT-FAMILY: 宋体">假设代码中有这样的语句，</span><span style="FONT-FAMILY: Verdana">cout&lt;&lt;map[key],</span><span style="FONT-FAMILY: 宋体">按理这是没有问题的，但是如果你的 </span><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;key</span><span style="FONT-FAMILY: 宋体">在</span><span style="FONT-FAMILY: Verdana">map</span><span style="FONT-FAMILY: 宋体">中原本是不存在的，那么这句代码会&#8220;自作聪明&#8221;的帮你在</span><span style="FONT-FAMILY: Verdana">map</span><span style="FONT-FAMILY: 宋体">中将上一个&nbsp; |&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 该</span><span style="FONT-FAMILY: Verdana">key</span><span style="FONT-FAMILY: 宋体">的</span><span style="FONT-FAMILY: Verdana">value</span><span style="FONT-FAMILY: 宋体">为</span><span style="FONT-FAMILY: Verdana">default</span><span style="FONT-FAMILY: 宋体">的元素，这恐怕不是件好事。</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt; </span><span style="FONT-FAMILY: 宋体">③</span><span style="FONT-FAMILY: Verdana">map[key]=value</span><span style="FONT-FAMILY: 宋体">的操作要比</span><span style="FONT-FAMILY: Verdana">insert(value)</span><span style="FONT-FAMILY: 宋体">的方式慢。</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">|-&gt;</span><span style="FONT-FAMILY: SimSun">说明</span><span style="FONT-FAMILY: Verdana">------&gt;multimap</span><span style="FONT-FAMILY: SimSun">的操作与</span><span style="FONT-FAMILY: Verdana">map</span><span style="FONT-FAMILY: SimSun">大致一样，不同在于</span><span style="FONT-FAMILY: Verdana">multimap</span><span style="FONT-FAMILY: SimSun">允许有相同的</span><span style="FONT-FAMILY: Verdana">key</span><span style="FONT-FAMILY: SimSun">在容器中存在。</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|-&gt;Type-----&gt;class</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|-&gt;Include---&gt;&lt;map&gt;</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|-&gt;Define----&gt;map&lt;key,value,optional compare ,optional&gt;</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|-&gt;Sub</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in; FONT-FAMILY: Verdana">|-&gt;Fun</p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt;map</span><span style="FONT-FAMILY: SimSun">和</span><span style="FONT-FAMILY: Verdana">set</span><span style="FONT-FAMILY: SimSun">基本具有相同的操作。</span></p>
<p style="FONT-SIZE: 10pt; MARGIN: 0in"><span style="FONT-FAMILY: Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |------&gt; </span><span style="FONT-FAMILY: SimSun">不同的是</span><span style="FONT-FAMILY: Verdana">map</span><span style="FONT-FAMILY: SimSun">的</span><span style="FONT-FAMILY: Verdana">insert(elem)</span><span style="FONT-FAMILY: SimSun">不再返回一个</span><span style="FONT-FAMILY: Verdana">pair</span><span style="FONT-FAMILY: SimSun">而是一个</span><span style="FONT-FAMILY: Verdana">pos的</span><span style="FONT-FAMILY: Verdana">iterator</span><span style="FONT-FAMILY: SimSun">。</span>&nbsp;</p>
<br><br></div>
<img height=1 src="http://www.cnblogs.com/jcss2008/aggbug/1353490.html?type=1&amp;webview=1" width=1> <!--
<rdf:rdf xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
<rdf:description
rdf:about="http://www.cnblogs.com/jcss2008/archive/2008/12/12/1353490.html"
dc:identifier="http://www.cnblogs.com/jcss2008/archive/2008/12/12/1353490.html"
dc:title=""
trackback:ping="http://www.cnblogs.com/jcss2008/services/trackbacks/1353490.aspx" />
</rdf:rdf>
--><!--end: topics 文章、评论容器-->
<img src ="http://www.cppblog.com/deane/aggbug/76343.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-03-12 15:52 <a href="http://www.cppblog.com/deane/articles/76343.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>自动类型转换</title><link>http://www.cppblog.com/deane/articles/75766.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Fri, 06 Mar 2009 11:07:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/75766.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/75766.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/75766.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/75766.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/75766.html</trackback:ping><description><![CDATA[<br>在C和C + +中，如果编译器看到一个表达式或函数调用使用了一个不合适的类型，它经常<br>会执行一个自动类型转换。在C + +中，可以通过定义自动类型转换函数来为用户定义类型达到<br>相同效果。这些函数有两种类型：特殊类型的构造函数和重载的运算符。<br>11.6.1 构造函数转换<br>如果我们定义一个构造函数，这个构造函数能把另一类型对象（或引用）作为它的单个参<br>数，那么这个构造函数允许编译器执行自动类型转换。如下例：<br><br>class One{<br>&nbsp;public:<br>One(){}<br>};<br><br>class Two{<br>public:<br>Two(const One&amp;){}<br>};<br><br>void f(Two t){<br>}<br><br>main(){<br>One one;<br>f(one);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Wants a Two, has a One<br>}<br><br>当编译器看到f( )以为对象o n e参数调用时，编译器检查f( )的声明并注意到它需要一个t w o<br>对象作为参数。然后，编译器检查是否有从对象one 到t w o的方法。它发现了构造函数<br>t w o : : t w o ( o n e )，t w o : : t w o ( o n e )被悄悄地调用，结果对象t w o被传递给f( )。<br>在这个情况里，自动类型转换避免了定义两个f( )重载版本的麻烦。然而，代价是隐藏了<br>构造函数对t w o的调用，如果我们关心f( )的调用效率的话，那就不要使用这种方法。<br>&#8226; 阻止构造函数转换<br>有时通过构造函数自动转换类型可能出现问题。为了避开这个麻烦，可以通过在前面加关<br>键字explicit （只能用于构造函数）来修改构造函数。上例类t w o的构造函数作了修改，如下：<br><br>class One{<br>&nbsp;public:<br>One(){}<br>};<br><br>class Two{<br>public:<br>Two(const One&amp;){}<br>};<br><br>void f(Two t){<br>}<br><br>main(){<br>One one;<br>//f(one);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//no auto conversion allowed<br>f(Two(one));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//OK user perform conversion<br>}<br><br><br>通过使类t w o的构造函数显式化，编译器被告知不能使用那个构造函数（那个类中其他非<br>显式化的构造函数仍可以执行自动类型转换）执行任何自动转换。如果用户想进行转换必须写<br>出代码。上面代码f ( t w o ( O n e ) )创建一个从类型O n e到t w o的临时对象，就像编译器在前面版本中<br>做的那样。<br><br><br><br>11.6.2 运算符转换<br>第二种自动类型转换的方法是通过运算符重载。我们可以创建一个成员函数，这个函数通<br>过在关键字o p e r a t o r后跟随想要转换到的类型的方法，将当前类型转换为希望的类型。这种形<br>式的运算符重载是独特的，因为没有指定一个返回类型——返回类型就是我们正在重载的运算<br>符的名字。这儿有一个例子：<br><br>class Three{<br>int i;<br>public:<br>Three(int I = 0, int = 0) : i(I){}<br>};<br><br>class Four{<br>int x;<br>public:<br>Four(int X) : x(X){}<br>operator Three(){ return Three(x); }<br>};<br><br>void g(three){}<br><br>void main(){<br>Four four(1);<br>g(four);<br>g(1);<br>}<br><br><br>用构造函数技术，目的类执行转换。然而使用运算符技术，是源类执行转换。构造函数技<br>术的价值是在创建一个新类时为现有系统增加了新的转换途径。然而，创建一个单一参数的构<br>造函数总是定义一个自动类型转换（即使它有不止一个参数也是一样，因为其余的参数将被缺<br>省处理），这可能并不是我们所想要的。另外，使用构造函数技术没有办法实现从用户定义类<br>型向内置类型转换，这只有运算符重载可能做到。<br><br><br><br>
<img src ="http://www.cppblog.com/deane/aggbug/75766.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-03-06 19:07 <a href="http://www.cppblog.com/deane/articles/75766.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++程序设计之四书五经</title><link>http://www.cppblog.com/deane/articles/75477.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Wed, 04 Mar 2009 00:57:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/75477.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/75477.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/75477.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/75477.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/75477.html</trackback:ping><description><![CDATA[<p>&nbsp;</p>
<p>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p>C++是一门广泛用于工业软件研发的大型语言。它自身的复杂性和解决现实问题的能力，使其极具学术研究价值和工业价值。和C语言一样，C++已经在许多重要的领域大获成功。 </p>
<p>然而，一个不可否认的现实是，在低阶程序设计领域，C++挤压着C同时也在承受着C的强烈反弹，而在高阶程序设计领域，Java和C#正在不断蚕食着C++的地盘。也许C++与C合为一体永远都是一个梦想，也许Java和C#的狂潮终将迫使C++回归本位 — 回到它有着根本性优势的开发领域：低级系统程序设计、高级大规模高性能应用设计、嵌入式程序设计以及数值科学计算等。果真如此，我认为这未尝不是一件好事。 </p>
<p>C++吸引如此之多的智力投入，以至于这个领域的优秀作品，包括重量级的软件产品、程序库以及书籍等，数不胜数。文题&#8220;C++程序设计之四书五经&#8221;一个不太严格的含义是：C++程序设计之四书 ⅹ 五经。是的，在本文（及其下篇）中，我将分门别类推荐20多本C++好书，你可以根据自己的需要选读。 </p>
<p><strong>TCPL和D&amp;E</strong> </p>
<p>TCPL和D&amp;E分别是《The C++ Programming Language》和《The Design and Evolution of C++》的简称，均出自Bjarne Stroustrup之手。我将它们单列出来，首先是因为Bjarne是C++语言的创建者，然后是因为比&#8220;首先&#8221;那个原因更重要的原因：这两本书是C++领域毋庸置疑的杰作。说它们是C++语言圣经，并不为过。 </p>
<p>Bjarne Stroustrup, The C++ Programming Language (Special 3rd Edition) </p>
<p>《C++程序设计语言（特别版）》，机械工业出版社 </p>
<p>《C++程序设计语言（特别版）（英文影印版）》，高等教育出版社 </p>
<p>迄今为止，TCPL是除了C++标准文献之外最权威的C++参考手册。和大多数人的看法不大一样，我认为Bjarne的文字语言并不逊色于他所创建的程序语言，至少我喜欢这种学院气息浓厚的作品。本书对C++语言的描述轮廓鲜明、直截了当。它从C++语言创建者的角度来观察C++，这是任何别的作者和书籍做不到的 — 没有任何人比Bjarne自己更清楚该怎么来使用C++。 </p>
<p>这是一本严肃的著作，以中、高级C++开发人员为目标读者。如果你是一名有经验的C++程序员，需要了解更加本质的C++知识，本书正是为你而写。它不是那种让你看了会不断窃喜的小书，需要用心体会，反复咀嚼。在阅读过程中，请特别留心Bjarne先生强调了什么，又对什么一语带过。我个人比较喜欢这本书的第四部分&#8220;使用C++做设计&#8221;，这样的内容在类似的程序设计语言书籍中很难看到 — 我甚至认为Bjarne应该将这部分独立出来单独写一本书。 </p>
<p>　 </p>
<p>Bjarne Stroustrup, The Design and Evolution of C++ </p>
<p>《C++语言的设计和演化》，机械工业出版社 </p>
<p>《C++语言的设计和演化（英文版）》，机械工业出版社 </p>
<p>D&amp;E是一本关于C++语言设计原理、设计决策和设计哲学的专著。它清晰地回答了C++为什么会成为今天这个样子而没有变成另外一种语言。作为C++语言的创建者，Bjarne淋漓尽致地展示了他独到而深刻的见解。除了广受赞誉的语言特性外，Bjarne没有回避那些引起争议的甚至被拒绝的C++特性，他一一给出了逻辑严密、令人信服的解释。内容涵盖C++的史前时代、带类的C、C++的设计规则、标准化、库、内存管理、多重继承、模板等，对包括异常机制、运行时类型信息和名字空间在内的重要的新特性都分别进行了深入探讨。每一名C++程序员都应该可以从Bjarne的阐释中加深对手中这门语言的认识。 </p>
<p>需要再次提醒的是，这两本书知识浓缩，信息量极大，请不要错过Bjarne每一句看似漫不经意的话。 </p>
<p><strong>入门教程</strong> </p>
<p>学习任何一门语言都需要一个从入门到精通、从新手到高手循序渐进的过程。不过，对于一个所谓的新手而言，究竟是一个完完全全的新手，还是一个熟悉某种别的语言的&#8220;新手&#8221;，甚至是在某种语言程序设计领域已经颇有建树的高手，很难一概而论？不同的C++新手需要不同的入门书籍。 </p>
<p>Andrew Koenig, Barbara E. Moo, Accelerated C++: Practical Programming by Example </p>
<p>《Accelerated C++中文版》，中国电力出版社 </p>
<p>和市面上大多数C++教程不同，本书不是从&#8220;C++中的C&#8221;开始讲解，而是始于地道的C++特性。从一开始就使用标准库来写程序，随着讲述的逐渐深入，又一一解释这些标准库组件所依赖的基础概念。另外，和其他C++教材不同的是，这本书以实例拉动语言和标准库的讲解，对后两者的讲解是为了给实例程序提供支持，而不是像绝大多数C++教材那样，例子只是用作演示语言特性和标准库用法的辅助工具。 </p>
<p>作者在C++领域的编程实践、教育培训以及技术写作方面都是世界一流水准。我喜欢这种大量使用标准库和C++语言原生特性的清新的写作风格。在这本教材面前，几乎迄今为止的所有C++教材都黯然失色或显得过时。尽管这本教材也许对于国内的高校教育来说有些前卫，不过我仍然极力向我的同行们推荐。顺带一提，在Bjarne和我最近的一封通信里，他这样评价本书：对于有经验的程序员学习C++而言，这本书可能是世界上最好的一本。 </p>
<p>Stanley B.Lippman, Josee Lajoie, C++ Primer (3rd Edition) </p>
<p>《C++ Primer (3RD)中文版》，中国电力出版社 </p>
<p>这本书的名字多少有点让人误解。尽管作者声称这本书是为C++新手而写，但无论是它的厚度还是讲解的深度都暴露了似乎并非如此。也许说它是一本&#8220;从入门到精通&#8221;的C++教程会更合适一些。我个人认为它并不适合完全不懂C++的初学者 — 在阅读这本书之前，你至少应该先有那么一点C或C++的背景知识，或者至少要具有一些其他语言的编程经验。 </p>
<p>尽管这本书省略了一些高级C++特性的讨论，但仍然可以称得上是迄今为止最全面的C++学习教程。事实上，如果一名C++初学者能够扎扎实实地读完本书并对照《C++ Primer Answer Book》完成全部习题的话，他的水平肯定可以进入职业C++程序员的行列。我个人认为，即使你已经拥有了TCPL，这本书依然有拥有的价值，因为在许多方面它比TCPL来得更详细、更易懂。 </p>
<p>Stanley B. Lippman, Essential C++ </p>
<p>《Essential C++中文版》，华中科技大学出版社 </p>
<p>《Essential C++（影印版）》，中国电力出版社 </p>
<p>可以不太严格地认为这本书是《C++ Primer》的精简版。本书一一讲述了C++中最具代表性的主题，包括过程式编程、泛型编程、基于对象编程、面向对象编程、模板编程以及异常处理等。Stanley将门槛调低到&#8220;具有其他语言程序设计经验&#8221;的C++新手所能接受的最基本的层次，使他们能够迅速开始使用C++编程而又免于阅读《C++ Primer》那样的大部头。它以实例引导学习，力图使读者在最短的时间内把握C++的精粹。 </p>
<p>也许换一个人来概述C++编程范型（paradigm）的方方面面需要好几百页才能说清楚，但这本小书不可思议地做到了这一点。我个人非常喜欢这种满是技术、简明扼要并且&#8220;有话好好说&#8221;的书。这本书同样具有一个明显的风格：所有程序例子全部采用标准库组件，让人耳目一新。 </p>
<p>以上三本书都不是为了完完全全的编程新手而写。完全的C++编程新手可以阅读Francis Glassborow的新书（尚未出版）：《A Beginners Introduction to Computer Programming : You Can Do It!》。这也是Bjarne的推荐。Francis Glassborow是ACCU主席，多年来他对几乎每一本C++经典名著评头论足，他自己的这一本自然会引起C++社群的极大兴趣。 </p>
<p><strong>高效、健壮编程</strong> </p>
<p>两年前我在负责一个省级电力调度系统项目时编写了一个网关程序，它从SCADA系统获取电力实时信息。通讯接口采用了不常用的数据库直连方式（这个网关程序一端连接SQL Server 6.5，另一端连接Oralce 8.1.6）。由于实时测点近万，每次将全部取样更新或插入一遍显然是低效的。我在网关程序里建了一个内存库，获取到的数据首先在其中进行比较，然后决定是否更新物理数据库（同时还做了别的更复杂的事情&#8230;&#8230;），从而在效率和资源占用两方面达到了预期效果。 </p>
<p>这个程序一直运行得很好，但在离开现场之后的某一天，系统管理员打来电话，说大概因为网络故障等原因，有时这个网关程序会崩溃掉 — 它自己崩掉也就罢了，问题是它还会把Windows 2000 Advanced Server搞成&#8220;蓝屏&#8221;！坦白地说，我还从来没看过哪个非蓄意的程序有这个&#8220;能耐&#8221;。由于当时正忙于另外一个大项目，无法去现场调试，最后只有凭经验对内存库代码小心翼翼地封装以异常处理代码（同时也做了一些别的修改&#8230;&#8230;）。这样，虽然没有彻底解决问题，但程序终究不再死得那么难看了。 </p>
<p>在这儿讲这么一段花絮有什么意思呢（当初为那个可怕的bug朝思暮想时我可不认为这是一个&#8220;花絮&#8221;）？我想说的是，对于任何软件而言，离开强健，效率也就无从谈起。而对于C++程序员来说，也许编写一个高效的程序并不难，但要编写一个需要7 ⅹ 24小时持续运行的服务端软件就不是那么容易了，需要考虑许多因素，有时这些因素甚至远远超出C++语言和开发工具的本身。作为一名开发实际项目软件的程序员，并非非得自己碰钉子才能积累经验，只要我们足够虚心，别人的经验往往都是我们很好的借鉴。鉴于此，我推荐以下几本书供你选读，它们可以让你从强健和效率两方面受益（当然了，它们涵盖的内容远不限于异常处理J）。 </p>
<p>Scott Meyers, Effective C++: 50 Specific Ways to Improve Your Programs and Design (2nd Edition) </p>
<p>Scott Meyers, More Effective C++: 35 New Ways to Improve Your Programs and Designs </p>
<p>《Effective C++中文版》，华中科技大学出版社 </p>
<p>《More Effective C++中文版》，中国电力出版社 </p>
<p>《Effective C++（影印版）》，中国电力出版社 </p>
<p>如果说《Effective C++》主要讨论C++中一些相对基础的概念和技巧的话，那么《More Effective C++》则着重探讨了包括异常处理在内的一系列高级技术。与前者相比，后者具有两大主要区别：其一，它包含很多时新的标准C++的内容；第二，它讨论的主题倾向于&#8220;战略化&#8221;而非&#8220;战术化&#8221;，并且讨论得更深入、更彻底。尤其是对虚析构函数、智能指针、引用计数以及代理类（proxy classe）等技术和模式论述的深入程度，让人很难想象是出现于这样的一本小书之中。 </p>
<p>游刃有余的技术，高超的写作技巧，Scott无疑是世界上最优秀的C++技术作家之一。在简洁、清晰、易读等方面，这两本书都卓尔不群。总之，Scott提供的这85个可以改善编程技术和设计思维的方法，都是中、高级C++程序员必备的技能。我强烈推荐这两本书（实际上还有一本，稍后就会看到）。 </p>
<p>Herb Sutter, Exceptional C++: 47 Engineering Puzzles, Programming Problems, and Solutions </p>
<p>Herb Sutter, More Exceptional C++: 40 New Engineering Puzzles, Programming Problems, and Solutions </p>
<p>《Exceptional C++中文版》，中国电力出版社 </p>
<p>《More Exceptional C++中文版》，华中科技大学出版社 </p>
<p>你自认为是一名C++语言专家吗？读一读ISO C++标准委员会秘书长的这两本书再回答。在这两本书中，Herb采用了&#8220;问答&#8221;的方式指导你学习C++语言特性。对于每一个专题，Herb首先合理地设想出你的疑问和困惑，接着又猜测出你十有八九是错误的解答，然后给你以指点并提出最佳解决方案，最后还归纳出解决类似问题的普适性原则。 </p>
<p>这两本书是典型的深究C++语言细节的著作，很薄，但内容密集，远远超过Scott的那两本书，读起来很费脑筋 — 我个人认为它们要比Scott的书难懂得多。若要研习这薄薄的两本书所包含的知识，至少需要花费数月的时间！（在Scott的荐序中，他坦陈不止一次陷入GotW问题的陷阱，你应该知道这意味着什么）对于语言细节的深究有什么好处呢？尽管在大多数情况下，我们不必关心C++代码幕后的动作，然而当我们不得不关心时，这两本书可以为我们提供很好的线索，因为它们揭示了C++语言中微妙而又至关重要的东西。 </p>
<p>Stephen C. Dewhurst, C++ Gotchas: Avoiding Common Problems in Coding and Design </p>
<p>《C++程序设计陷阱》，中国青年出版社 </p>
<p>Stephen的理论素养和实践经验注定这是一本值得一读的好书。Stephen曾经是贝尔实验室中第一批C++使用者。他已经使用C++成功解决了包括编译器、证券交易、电子商务以及嵌入式系统等领域中的问题。本书汇集了作者来自开发一线的99条编程真知灼见，洞悉它们，你可以避免几乎所有常见的C++设计和编程问题。 </p>
<p>我甚至认为，对于C++编程菜鸟而言，阅读这本书会比阅读Scott和Herb的书更能轻松而立竿见影地获得更大的提高。我个人很喜欢这本书的写作风格 — Stephen的许多观点看似极端却无可辩驳。当然了，这种自信（以及冷幽默）来自于作者深厚的技术素养，而非自大的偏执。 </p>
<p>除了上面推荐的书籍外，Dov Bulka和 David Mayhew合著的《Efficient C++: Performance Programming Techniques》（《提高C++性能的编程技术》，清华大学出版社）也值得一看。这本超薄小书聚焦于高性能C++应用程序开发。两位作者都是IBM软件专家，都工作于对性能要求极高的系统构建领域，本书是他们的经验之谈。也有人不喜欢这本书，因为它花了不少的篇幅讲述和C++无关的东西，我却恰恰因为这一点而对这本书产生好感，正是这些东西让我开阔了眼界。 </p>
<p><strong>模板和泛型编程</strong> </p>
<p>模板和基于模板的泛型编程无疑是当今发展最活跃的C++程序设计技术。模板的第一个革命性的应用是STL，它将模板技术在泛型容器和算法领域的运用展现得淋漓尽致，而Boost、Loki等现代程序库则将模板技术的潜能不断发挥到极致。在模板和泛型编程领域，我推荐以下两本重量级著作： </p>
<p>David Vandevoorde, Nicolai M. Josuttis, C++ Templates: The Complete Guide </p>
<p>《C++ Templates全览（繁体版）》，台湾碁峰资讯股份有限公司 </p>
<p>《C++ Templates全览（简体版）》，人民邮电出版社 </p>
<p>有一种老套的赞美一本书的手法，大致是&#8220;没有看过这本书，你就怎么怎么地&#8221;，这里面往往夸张的成分居多。不过，倘若说&#8220;没有看过《C++ Templates: The Complete Guide》，你就不可能精通C++模板编程&#8221;，那么这个论断对于世界上绝大多数C++程序员来说是成立的。 </p>
<p>这本书填补了C++模板书籍领域由来已久的空白。此前，上有《Modern C++ Design》这样的专注于模板高级编程技术和泛型模式的著作，下有《The C++ Standard Library》这样的针对特定模板框架和组件的使用指南。然而，假如对模板机制缺乏深入的理解，你就很难&#8220;上下&#8221;自如。鉴于此，我向每一位渴望透彻理解C++模板技术的朋友推荐这本书。 </p>
<p>这本书在内地、台湾各有一个译本，但出自不同的译者之手。当你看到这篇文章时，两个译本应该都已经上市，对于读者来说当然也就多了一种选择。侯捷先生个人网站上开放了繁体译本大部分章节，不妨先睹为快。 </p>
<p>Andrei Alexandrescu, Modern C++ Design: Generic Programming and Design Patterns Applied </p>
<p>《C++设计新思维：泛型编程与设计模式之应用》，华中科技大学出版社 </p>
<p>《C++设计新思维（影印版）》，中国电力出版社 </p>
<p>你自认为是C++模板编程高手吗？请看过这本书再回答J 这是一本出自天才之手令人敬畏的杰作。泛型模式，无限延伸你的视野，足以挑战任何一名C++程序员的思维极限。 </p>
<p>这本书共分为两大部分，第一部分讨论了 Loki程序库采用的基础技术以及一些高级语言特性，包括基于策略的类设计、模板局部特化、编译期断言、Typelist以及小型对象分配技术等。第二部分则着重介绍了Loki中的重要组件和泛型模式技术，包括泛化仿函数（Generalization Functor）、单件（Singleton）、智能指针、对象工厂（Object Factory）、抽象工厂（Abstract Factory）、访问者（Visitor）以及多方法（Multimethods）等。每一种技术都让人大开眼界，叹为观止。 </p>
<p>在C++的学习方面，过犹不及往往成了不求甚解的借口。然而，面向对象并非C++的全部，模板和泛型编程亦占半壁江山。对于&#8220;严肃&#8221;的C++程序员而言，及时跟进这项早经例证的成功技术，不失为明智之举。 </p>
<p><strong>结语</strong> </p>
<p>这些著作是如此大名鼎鼎，也许根本不缺我一个推荐。然而，纵然C++程序员队伍的发展壮大速度不像其他更时髦的语言那样迅速，新人进总是多于旧人出。除了热忱地欢迎新人，我个人认为到了对C++书籍进行&#8220;盘点&#8221;的时候了，并且希望这样的&#8220;盘点&#8221;有益于感兴趣的读者。请保持耐心和宽厚。在下篇中，我将继续介绍标准库、网络编程以及其他方面的C++好书。有好书相伴，这个冬天不会冷。 </p>
<p><strong>C++程序设计之四书五经（下篇） </strong></p>
<p>我在上篇中&#8220;盘点&#8221;了TCPL和D&amp;E以及入门教程、高效和健壮编程、模板和泛型编程等方面共十几本C++好书。冬去春来，让我们继续C++书籍精彩之旅J </p>
<p><strong>标准库</strong> </p>
<p>当我还在研究院工作时，与同院另外两家研究所合作开发过一个大型水利枢纽调度集成项目。我们三家软件系统之间都要相互通信。在调试通讯模块时，细心的客户（一名好学的系统管理员）发现对于同一通信规约的解释代码，我的不超过30行，而对方的则超过了150行且很难看懂。这位系统管理员很纳闷，我说大家编程风格和习惯不一样，我使用了标准库，而他使用了传统C编程风格以及他所习惯的另外一些技术。 </p>
<p>别误会！我绝无贬低这位合作伙伴的意思。事实上，我对那些真正有着深厚的C编程功力的程序员常常怀有钦佩之心。毕竟，C++能有今天的成功在很大程度上缘于它深深地植根于C。作为一名C++程序员，倘若不熟悉C++中的C，我往往会认为他的基本功是不扎实的，他的技术底气是不足的。 </p>
<p>不过话又说回来，C++是一种多范型（paradigm）编程语言，具体采用哪种编程风格，专业程序员应该知道视具体情况而定。作为一名经常需要在现场做即兴开发的项目负责人，为了短平快地解决当务之急，我习惯尽量采用现有的库（和组件）。效率（以及强健性）久经验证的C++标准库已经摆在那儿了，何乐而不用呢？ </p>
<p>Nicolai M. Josuttis, The C++ Standard Library: A Tutorial and Reference </p>
<p>《C++标准程序库：自修教程与参考手册》，华中科技大学出版社 </p>
<p>这是一本百科全书式的C++标准库著作，是一本需要一再查阅的参考大全。它在完备性、细致性以及精确性方面都是无与伦比的。本书详细介绍了每一标准库组件的规格和用法，内容涵盖包括流和本地化在内的整个标准库而不仅仅是STL。正如本书副标题所示，它首先适合作为教程阅读，尔后又可用作参考手册。 </p>
<p>浅显易懂的写作风格使得这本书非常易读。如果你希望学习标准库的用法并尽可能地发挥其潜能，那你必须拥有这本书。正如网络上所言，这本书不仅仅应该摆在你的书橱中，更应该放到你的电脑桌上。我向每一位职业C++程序员强烈推荐。 </p>
<p>Angelika Langer, Klaus Kreft, Standard C++ IOStreams and Locales: Advanced Programmer's Guide and Reference </p>
<p>《标准C++输入输出流与本地化》，人民邮电出版社 </p>
<p>C++标准库由STL、流和本地化三部分构成。关于STL的书市面上已经有不少，但罕见流和本地化方面的专著。本书是这两个领域中最优秀的一本，迄今为止没有任何一本书比这一本更全面详尽地讨论了流和本地化。如果你不满足于停留在&#8220;会用&#8221;流库的层面，千万不要错过它。 </p>
<p>2001年夏天，我草草翻阅过这本书的中文版，从内容到包装都给我留下了比较深刻的印象 — 不过负面的居多一些。2003年秋天，无意中得知某网络书店正以超低价格甩卖这本书的中译本，情不自禁，一阵唏嘘。 </p>
<p>Scott Meyers, Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library </p>
<p>《Effective STL（影印版）》，中国电力出版社 </p>
<p>读完Scott 的《Effective C++》和《More Effective C++》的中译本之后，我一直期待这本书的中文版。我从潘爱民先生的个人主页上了解到，他和他的合作伙伴似乎早已完成了这本书的翻译工作，可惜至今市面上仍不得见。幸运的是，我们可以看到它的原版。 </p>
<p>本书是使用STL的程序员必读之作。在这本书中，Scott向我们讲述STL容器和算法的工作机制以及如何以最佳方式使用它们。和Scott的其他作品一样，这本书的写作风格清晰、精确，具有极佳的可读性。看过这本书以后，我想你也许会和我以及其他C++程序员一样产生这样的想法：Scott什么时候会写出一本&#8220;More Effective STL&#8221;？ </p>
<p>关于STL，我还提醒你留心Matthew H. Austern的《Generic Programming and the STL: Using and Extending the C++ Standard Template Library》（《泛型编程与STL》，中国电力出版社）。这本书散发着浓厚的学院气息。Andrew Koenig和Barbara Moo在《Accelerated C++: Practical Programming by Example》一书末尾郑重推荐另外两本进阶好书（除了他们自己的《Ruminations on C++》外），其中一本是TCPL，另外一本就是本书！ </p>
<p><strong>网络编程</strong> </p>
<p>在网络编程时代，C++应该扮演着怎样的角色，让ACE（Adaptive Communications Environment）来告诉你。 </p>
<p>Douglas C. Schmidt, Stephen D. Huston, C++ Network Programming, Volume 1: Mastering Complexity with ACE and Patterns </p>
<p>Douglas C. Schmidt, Stephen D. Huston, C++ Network Programming, Volume 2: Systematic Reuse with ACE and Frameworks </p>
<p>《C++网络编程，卷1：运用ACE和模式消除复杂性》，华中科技大学出版社 </p>
<p>《C++网络编程，卷2：基于 ACE 和框架的系统化复用》，电子工业出版社 </p>
<p>采用C++进行企业级网络编程，目前ACE（以及这两本书）是一个值得考虑的选择。ACE是一个面向对象、跨平台、开放源码的网络编程框架，目标在于构建高性能网络应用和中间件。Douglas是ACE的创始人，Stephen则已为ACE提供了数年的技术支持和顾问服务，两位都是ACE社群（是的，ACE的影响和实际应用的程度已经形成了一个社群）的专家。 </p>
<p>ACE并不单单被大学和研究所追捧，它已经被成功地应用于世界上成千上万个商业应用中。在电信、宇航、医药和财经领域的网络系统中，ACE已经并继续发挥着重要的作用。如果你准备开发高性能通讯系统，你应该考虑考虑这一汇集世界顶尖专家智慧的成果。 </p>
<p>除了使用C++面向对象设计技术和模板等高级语言特性外，ACE还运用了大量的模式。《C++网络编程》卷1和卷2并不仅仅教你关于ACE的方方面面，它还会教给你模式和通用框架设计等高级技术等。所以，作为一名中、高级C++程序员，即使你很少进行正儿八经的C++网络程序设计，阅读这两本书同样可以从中受益。 </p>
<p>是的，并非所有网络应用都要使用Web服务器（以及其他应用服务器）和重量级组件模型，换个思路，它们或许也可以从轻量级的ACE组件中获益。 </p>
<p><strong>杂项</strong> </p>
<p>以下这几本书之所以被列入&#8220;杂项&#8221;单元，只是因为我没有考虑出更合适的归类方法，它们和上面的书籍一样，值得一读。 </p>
<p>Bruce Eckel, Thinking in C++, Volume 1: Introduction to Standard C++ (2nd Edition) </p>
<p>Bruce Eckel, Thinking in C++, Volume 2: Practical Programming (Second Edition) </p>
<p>《C++编程思想（第2版）第1卷：标准C++导引》，机械工业出版社 </p>
<p>《C++编程思想（英文版 第2版）》，机械工业出版社 </p>
<p>《Thinking in C++》的第1版于1996年荣获&#8220;软件研发&#8221;杂志评选的图书震撼大奖。最新推出的第2版对内容进行了大幅改写和调整，以反映C++标准化带来的影响以及近几年面向对象领域最新研究和实践成果。&#8220;输入输入流&#8221;、&#8220;多重继承&#8221;、&#8220;异常处理&#8221;和&#8220;运行时类型识别&#8221;等高级主题连同C++标准化以后增加的一些内容则被放入第二卷中。Bruce是一名经验丰富的C++讲师和顾问，其培训和写作经验都是世界一流水准，他的作品比那些&#8220;玩票&#8221;的技术人员写的东西更能吸引读者。事实上，在同类图书中，对于大多数读者而言，这本书的可读性要超过TCPL和《C++ Primer》。顺带一提，访问作者的站点，你可以先睹第二卷的风采。 </p>
<p>Andrew Koenig, Barbara E. Moo, Ruminations on C++: A Decade of Programming Insight and Experience </p>
<p>《C++沉思录》，人民邮电出版社 </p>
<p>Andrew是世界上屈指可数的C++专家。这是一本关于C++编程思想和程序设计技术而非语言细节的著作。如果你已经具有一定的基础，这本书将教你在进行C++编程时应该怎样思考，应该如何表达解决方案。整本书技术表达透彻，文字通俗易懂。Bjarne这样评价这本书：本书遍布&#8220;C++是什么、C++能够做什么&#8221;的真知灼见。 </p>
<p>Stanley B. Lippman, Inside The C++ Object Model </p>
<p>《深度探索C++对象模型》，华中科技大学出版社 </p>
<p>《深度探索C++对象模型（影印版）》，中国电力出版社 </p>
<p>从编译器的角度观察C++可以使你知其然并知其所以然。本书探讨了大量的C++面向对象程序设计的底层运作机制，包括构造函数、函数、临时对象、继承、虚拟、模板的实例化、异常处理、运行期类型识别等，另外还介绍了一些在实现C++对象模型过程中做出的权衡折衷。喜欢刨根问底的C++程序员不要错过这本书。 </p>
<p>Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, Design Patterns: Elements of Reusable Object-Oriented software </p>
<p>《设计模式：可复用面向对象软件的基础》，机械工业出版社 </p>
<p>《设计模式：可复用面向对象软件的基础（英文版）》，机械工业出版社 </p>
<p>设计可复用的面向对象的软件，你需要掌握设计模式。本书并非专为C++程序员而写，但它采用了C++（以及Smalltalk）作为主要示例语言，C++程序员尤其易于从中受益。四位作者都是国际公认的面向对象软件领域专家，他们将面向对象软件的设计经验作为设计模式详细记录下来。这本书影响是如此深远，以至于四位作者以及本书都被昵称为GoF（Gang of Four）。本书学院气息浓厚，行文风格严谨简洁，虽然它不如某些讲解模式的书籍易读，但真正要精准地理解设计模式，本书是终极权威。学习设计模式，这本书需要一而再、再而三的咀嚼。顺带一句：请将设计模式化作开拓思维的钥匙，切莫成为封闭思维的枷锁。 </p>
<p>还有一些C++好书值得一读，恕此处无法一一列出。例如John Lakos的著作《Large-Scale C++ Software Design》（《大规模C++程序设计》，中国电力出版社）和侯捷先生的《STL 源码剖析》（华中科技大学出版社）等。 </p>
<p>《STL 源码剖析》是一本很有特色的书，但我认为它还可以更好。我个人期待侯捷先生自第一版发行以来经过对模板技术的沉淀和再思考之后，再写一本剖析得更深入、更透彻并且更全面的&#8220;第二版&#8221;。遗憾的是，侯捷先生在完成《C++ Templates: The Complete Guide》一书的翻译后似乎决定暂时告别模板、泛型编程和STL领域。 </p>
<p>2004年3月31日补充：我目前最常查阅的两本参考书是《C++标准程序库》和《STL源码剖析》。当然了，这与我年内的写作计划有很大的关系。 </p>
<p>使用C++成功开发大规模软件系统，不仅需要很好地理解大多数C++书籍中讲述的逻辑设计问题，更需要掌握《大规模C++程序设计》中讲述的物理设计技术。当然，这本书的确有点过时了，不过，如果你的精力和金钱都比较宽绰，买一本看看并无坏处。 </p>
<p>至此，我想有必要声明一下，有一些（好）书没有得到推荐，主要原因如下： </p>
<p>以上这些书已经足够多、足够好了。 </p>
<p>我不会推荐通过正常渠道很难购买到的书籍 — 不管是中文版还是英文版。 </p>
<p>作（译）者名气大小不影响我的推荐。我们是在看书，不是看人。 </p>
<p>我不会推荐我从来没有看过的书。我至少要看过其中的某个版本（包括电子档）。这个&#8220;看&#8221;，一般指&#8220;认真阅读&#8221;，不过有一些也只能算是&#8220;浏览&#8221;。 </p>
<p><strong>结语</strong> </p>
<p>作为一名普通技术写译者，我深知技术创作和翻译的艰辛（和快乐），并多多少少了解一些有关技术书籍创作、翻译、制作、出版以及市场推介背后的细节。今天，我不会再对一本看上去差强人意的图书信口开河。罗列同一本书的各种版本的用意只在于为你多提供一些信息，让你多一种选择。 </p>
<p>在本文成文的后期，我给Bjarne写了一封信，请教如果他来写这篇文章会怎么写。他给了我简明扼要的建议。在肯定以上列出的绝大部分图书都是世界顶尖水平的C++著作的同时，Bjarne提醒我别忘了向专家级程序员推荐《The C++ Standard : Incorporating Technical Corrigendum No. 1》。这本书是 C++标准规范的&#8220;图书版&#8221;，Bjarne亲自为之作序。 </p>
<p>Bjarne还友好地提醒我，在我的推荐列表中没有哪一本有助于C++程序员进行Windows编程 — 这正是我的本意。在这篇文章中，我只推荐、点评平台中立的C++著作（网络编程除外） — 和操作系统无关，和集成开发环境无关，我甚至幻想它们和编译器也无关。你可以根据业务开发需要，选读自己喜爱的领域相关的C++书籍。 </p>
<p>说到&#8220;系统无关、平台中立&#8221;，我不由得想起了&#8220;抽象层&#8221;的概念。开发实际应用的C++程序员通常工作于特定操作系统、特定开发环境和特定业务领域之中，而对标准C++和C++标准库扎实而深刻的把握，无疑是你得以在不同的操作系统、不同的开发环境以及不同的业务领域之间纵横驰骋的&#8220;抽象&#8221;本钱。 </p>
<br><br>
<img src ="http://www.cppblog.com/deane/aggbug/75477.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-03-04 08:57 <a href="http://www.cppblog.com/deane/articles/75477.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++运算符重载探讨</title><link>http://www.cppblog.com/deane/articles/75301.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Mon, 02 Mar 2009 02:42:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/75301.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/75301.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/75301.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/75301.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/75301.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;<br>前言</p>
<p>&nbsp;&nbsp;&nbsp; 　　多态性是面向对象程序设计的重要特征之一。它与前面讲过的封装性和继承性构成了面向对象程序设计的三大特征。这三大特征是相互关联的。封装性是基础，继承性是关键，多态性是补充，而多态又必须存在于继承的环境之中。</p>
<p>&nbsp;&nbsp;&nbsp; 　　所谓多态性是指发出同样的消息被不同类型的对象接收时导致完全不同的行为。这里所说的消息主要是指对类的成员函数的调用，而不同的行为是指不同的实现。利用多态性，用户只需发送一般形式的消息，而将所有的实现留给接收消息的对象。对象根据所接收到的消息而做出相应的动作(即操作)。</p>
<p>&nbsp;&nbsp;&nbsp; 　　函数重载和运算符重载是简单一类多态性。函数重载的概念及用法在《函数重载》一讲中已讨论过了，这里只作简单的补充，我们重点讨论的是运算符的重载。</p>
<p>&nbsp;&nbsp;&nbsp; 　　所谓函数重载简单地说就是赋给同一个函数名多个含义。具体地讲，<a class=channel_keylink href="http://c.chinaitlab.com/" target=_blank><font color=#0000ff>C++</font></a>中允许在相同的作用域内以相同的名字定义几个不同实现的函数，可以是成员函数，也可以是非成员函数。但是，定义这种重载函数时要求函数的参数或者至少有一个类型不同，或者个数不同。而对于返回值的类型没有要求，可以相同，也可以不同。那种参数个数和类型都相同，仅仅返回值不同的重载函数是非法的。因为编译程序在选择相同名字的重载函数时仅考虑函数表，这就是说要靠函数的参数表中，参数个数或参数类型的差异进行选择。 由此可以看出，重载函数的意义在于它可以用相同的名字访问一组相互关联的函数，由编译程序来进行选择，因而这将有助于解决程序复杂性问题。如：在定义类时，构造函数重载给初始化带来了多种方式，为用户提供更大的灵活性。</p>
<p>&nbsp;&nbsp;&nbsp; 　　下面我们重点讨论运算符重载。</p>
<p>&nbsp;&nbsp;&nbsp; 　　运算符重载就是赋予已有的运算符多重含义。<a class=channel_keylink href="http://c.chinaitlab.com/" target=_blank><font color=#0000ff>C++</font></a>中通过重新定义运算符，使它能够用于特定类的对象执行特定的功能，这便增强了C++语言的扩充能力。</p>
<p>&nbsp;&nbsp;&nbsp; 　　运算符重载的几个问题</p>
<p>&nbsp;&nbsp;&nbsp; 　　1. 运算符重载的作用是什么？</p>
<p>&nbsp;&nbsp;&nbsp; 　　它允许你为类的用户提供一个直觉的接口。</p>
<p>&nbsp;&nbsp;&nbsp; 　　运算符重载允许C/C++的运算符在用户定义类型(类)上拥有一个用户定义的意义。重载的运算符是函数调用的语法修饰：</p>
<p>&nbsp;&nbsp;&nbsp; class Fred<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; public:<br>&nbsp;&nbsp;&nbsp; // ...<br>&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp;&nbsp; #if 0<br>&nbsp;&nbsp;&nbsp; // 没有算符重载：<br>&nbsp;&nbsp;&nbsp; Fred add(Fred, Fred);<br>&nbsp;&nbsp;&nbsp; Fred mul(Fred, Fred);</p>
<p>&nbsp;&nbsp;&nbsp; Fred f(Fred a, Fred b, Fred c)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; return add(add(mul(a,b), mul(b,c)), mul(c,a)); // 哈哈，多可笑...<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; #else<br>&nbsp;&nbsp;&nbsp; // 有算符重载：<br>&nbsp;&nbsp;&nbsp; Fred operator+ (Fred, Fred);<br>&nbsp;&nbsp;&nbsp; Fred operator* (Fred, Fred);</p>
<p>&nbsp;&nbsp;&nbsp; Fred f(Fred a, Fred b, Fred c)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; return a*b + b*c + c*a;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; #endif<br>&nbsp;&nbsp;&nbsp; 　　2. 算符重载的好处是什么？</p>
<p>&nbsp;&nbsp;&nbsp; 　　通过重载类上的标准算符，你可以发掘类的用户的直觉。使得用户程序所用的语言是面向问题的，而不是面向机器的。</p>
<p>&nbsp;&nbsp;&nbsp; 　　最终目标是降低学习曲线并减少错误率。</p>
<p>&nbsp;&nbsp;&nbsp; 　　3. 哪些运算符可以用作重载？</p>
<p>&nbsp;&nbsp;&nbsp; 　　几乎所有的运算符都可用作重载。具体包含：</p>
<p>&nbsp;&nbsp;&nbsp; 　　算术运算符：+,-,*,/,%,++,--;<br>&nbsp;&nbsp;&nbsp; 　　位操作运算符：&amp;,,~,^,＜＜,＞＞<br>&nbsp;&nbsp;&nbsp; 　　逻辑运算符：!,&amp;&amp;,;<br>&nbsp;&nbsp;&nbsp; 　　比较运算符：＜,＞,＞=,＜=,==,!=;<br>&nbsp;&nbsp;&nbsp; 　　赋值运算符：=,+=,-=,*=,/=,%=,&amp;=,=,^=,＜＜=,＞＞=;<br>&nbsp;&nbsp;&nbsp; 　　其他运算符：[],(),-＞,,(逗号运算符),new,delete,new[],delete[],-＞*。</p>
<p>&nbsp;&nbsp;&nbsp; 　　下列运算符不允许重载：</p>
<p>&nbsp;&nbsp;&nbsp; 　　.,.*,::,?:</p>
<p>&nbsp;&nbsp;&nbsp; 　　4. 运算符重载后，优先级和结合性怎么办？</p>
<p>&nbsp;&nbsp;&nbsp; 　　用户重载新定义运算符，不改变原运算符的优先级和结合性。这就是说，对运算符重载不改变运算符的优先级和结合性，并且运算符重载后，也不改变运算符的语法结构，即单目运算符只能重载为单目运算符，双目运算符只能重载双目运算符。</p>
<p>&nbsp;&nbsp;&nbsp; 　　5. 编译程序如何选用哪一个运算符函数？</p>
<p>&nbsp;&nbsp;&nbsp; 　　运算符重载实际是一个函数，所以运算符的重载实际上是函数的重载。编译程序对运算符重载的选择，遵循着函数重载的选择原则。当遇到不很明显的运算时，编译程序将去寻找参数相匹配的运算符函数。</p>
<p>&nbsp;&nbsp;&nbsp; 　　6. 重载运算符有哪些限制？</p>
<p>&nbsp;&nbsp;&nbsp; 　　(1) 不可臆造新的运算符。必须把重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中。</p>
<p>&nbsp;&nbsp;&nbsp; 　　(2) 重载运算符坚持4个&#8220;不能改变&#8221;。</p>
<p>&nbsp;&nbsp;&nbsp; 　　&#183;不能改变运算符操作数的个数；<br>&nbsp;&nbsp;&nbsp; 　　&#183;不能改变运算符原有的优先级；<br>&nbsp;&nbsp;&nbsp; 　　&#183;不能改变运算符原有的结合性；<br>&nbsp;&nbsp;&nbsp; 　　&#183;不能改变运算符原有的语法结构。</p>
<p>&nbsp;&nbsp;&nbsp; 　　7. 运算符重载时必须遵循哪些原则？</p>
<p>&nbsp;&nbsp;&nbsp; 　　运算符重载可以使程序更加简洁，使表达式更加直观，增加可读性。但是，运算符重载使用不宜过多，否则会带来一定的麻烦。</p>
<p>&nbsp;&nbsp;&nbsp; 　　使用重载运算符时应遵循如下原则：</p>
<p>&nbsp;&nbsp;&nbsp; 　　(1) 重载运算符含义必须清楚。</p>
<p>&nbsp;&nbsp;&nbsp; 　　(2) 重载运算符不能有二义性。<br><br><br>运算符重载函数的两种形式</p>
<p>&nbsp;&nbsp;&nbsp; 　　运算符重载的函数一般地采用如下两种形式：成员函数形式和友元函数形式。这两种形式都可访问类中的私有成员。</p>
<p>&nbsp;&nbsp;&nbsp; 　　1. 重载为类的成员函数</p>
<p>&nbsp;&nbsp;&nbsp; 　　这里先举一个关于给复数运算重载复数的四则运算符的例子。复数由实部和虚部构造，可以定义一个复数类，然后再在类中重载复数四则运算的运算符。先看以下源代码：</p>
<p>&nbsp;&nbsp;&nbsp; #include ＜iostream.h＞</p>
<p>&nbsp;&nbsp;&nbsp; class complex<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; public:<br>&nbsp;&nbsp;&nbsp; complex() { real=imag=0; }<br>&nbsp;&nbsp;&nbsp; complex(double r, double i)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; real = r, imag = i;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; complex operator +(const complex &amp;c);<br>&nbsp;&nbsp;&nbsp; complex operator -(const complex &amp;c);<br>&nbsp;&nbsp;&nbsp; complex operator *(const complex &amp;c);<br>&nbsp;&nbsp;&nbsp; complex operator /(const complex &amp;c);<br>&nbsp;&nbsp;&nbsp; friend void print(const complex &amp;c);<br>&nbsp;&nbsp;&nbsp; private:<br>&nbsp;&nbsp;&nbsp; double real, imag;<br>&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp;&nbsp; inline complex complex::operator +(const complex &amp;c)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; return complex(real + c.real, imag + c.imag);<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; inline complex complex::operator -(const complex &amp;c)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; return complex(real - c.real, imag - c.imag);<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; inline complex complex::operator *(const complex &amp;c)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; return complex(real * c.real - imag * c.imag, real * c.imag + imag * c.real);<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; inline complex complex::operator /(const complex &amp;c)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; return complex((real * c.real + imag + c.imag) / (c.real * c.real + c.imag * c.imag),<br>&nbsp;&nbsp;&nbsp; (imag * c.real - real * c.imag) / (c.real * c.real + c.imag * c.imag));<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; void print(const complex &amp;c)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; if(c.imag＜0)<br>&nbsp;&nbsp;&nbsp; cout＜＜c.real＜＜c.imag＜＜'i';<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp; cout＜＜c.real＜＜'+'＜＜c.imag＜＜'i';<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; void main()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; complex c1(2.0, 3.0), c2(4.0, -2.0), c3;<br>&nbsp;&nbsp;&nbsp; c3 = c1 + c2;<br>&nbsp;&nbsp;&nbsp; cout＜＜"\nc1+c2=";<br>&nbsp;&nbsp;&nbsp; print(c3);<br>&nbsp;&nbsp;&nbsp; c3 = c1 - c2;<br>&nbsp;&nbsp;&nbsp; cout＜＜"\nc1-c2=";<br>&nbsp;&nbsp;&nbsp; print(c3);<br>&nbsp;&nbsp;&nbsp; c3 = c1 * c2;<br>&nbsp;&nbsp;&nbsp; cout＜＜"\nc1*c2=";<br>&nbsp;&nbsp;&nbsp; print(c3);<br>&nbsp;&nbsp;&nbsp; c3 = c1 / c2;<br>&nbsp;&nbsp;&nbsp; cout＜＜"\nc1/c2=";<br>&nbsp;&nbsp;&nbsp; print(c3);<br>&nbsp;&nbsp;&nbsp; c3 = (c1+c2) * (c1-c2) * c2/c1;<br>&nbsp;&nbsp;&nbsp; cout＜＜"\n(c1+c2)*(c1-c2)*c2/c1=";<br>&nbsp;&nbsp;&nbsp; print(c3);<br>&nbsp;&nbsp;&nbsp; cout＜＜endl;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; 　　该程序的运行结果为：<br></p>
<p>&nbsp;&nbsp;&nbsp; c1+c2=6+1i<br>&nbsp;&nbsp;&nbsp; c1-c2=-2+5i<br>&nbsp;&nbsp;&nbsp; c1*c2=14+8i<br>&nbsp;&nbsp;&nbsp; c1/c2=0.45+0.8i<br>&nbsp;&nbsp;&nbsp; (c1+c2)*(c1-c2)*c2/c1=9.61538+25.2308i<br>&nbsp;&nbsp;&nbsp; 　　在程序中，类complex定义了4个成员函数作为运算符重载函数。将运算符重载函数说明为类的成员函数格式如下：</p>
<p>&nbsp;&nbsp;&nbsp; 　　＜类名＞ operator ＜运算符＞(＜参数表＞)</p>
<p>&nbsp;&nbsp;&nbsp; 　　其中，operator是定义运算符重载函数的关键字。</p>
<p>&nbsp;&nbsp;&nbsp; 　　程序中出现的表达式：</p>
<p>&nbsp;&nbsp;&nbsp; 　　c1+c2</p>
<p>&nbsp;&nbsp;&nbsp; 　　编译程序将给解释为：</p>
<p>&nbsp;&nbsp;&nbsp; 　　c1.operator+(c2)</p>
<p>&nbsp;&nbsp;&nbsp; 　　其中，c1和c2是complex类的对象。operator+()是运算+的重载函数。</p>
<p>&nbsp;&nbsp;&nbsp; 　　该运算符重载函数仅有一个参数c2。可见，当重载为成员函数时，双目运算符仅有一个参数。对单目运算符，重载为成员函数时，不能再显式说明参数。重载为成员函数时，总时隐含了一个参数，该参数是this指针。this指针是指向调用该成员函数对象的指针。</p>
<p>&nbsp;&nbsp;&nbsp; 　　2. 重载为友元函数</p>
<p>&nbsp;&nbsp;&nbsp; 　　运算符重载函数还可以为友元函数。当重载友元函数时，将没有隐含的参数this指针。这样，对双目运算符，友元函数有2个参数，对单目运算符，友元函数有一个参数。但是，有些运行符不能重载为友元函数，它们是：=,(),[]和-＞。</p>
<p>&nbsp;&nbsp;&nbsp; 　　重载为友元函数的运算符重载函数的定义格式如下：</p>
<p>&nbsp;&nbsp;&nbsp; 　　friend ＜类型说明符＞ operator ＜运算符＞(＜参数表＞)<br>&nbsp;&nbsp;&nbsp; 　　{&#8230;&#8230;}</p>
<p>&nbsp;&nbsp;&nbsp; 　　下面用友元函数代码成员函数，重载编写上述的例子，程序如下：</p>
<p>&nbsp;&nbsp;&nbsp; #include ＜iostream.h＞</p>
<p>&nbsp;&nbsp;&nbsp; class complex<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; public:<br>&nbsp;&nbsp;&nbsp; complex() { real=imag=0; }<br>&nbsp;&nbsp;&nbsp; complex(double r, double i)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; real = r, imag = i;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; friend complex operator +(const complex &amp;c1, const complex &amp;c2);<br>&nbsp;&nbsp;&nbsp; friend complex operator -(const complex &amp;c1, const complex &amp;c2);<br>&nbsp;&nbsp;&nbsp; friend complex operator *(const complex &amp;c1, const complex &amp;c2);<br>&nbsp;&nbsp;&nbsp; friend complex operator /(const complex &amp;c1, const complex &amp;c2);<br>&nbsp;&nbsp;&nbsp; friend<br>&nbsp;&nbsp;&nbsp; void print(const complex &amp;c);<br>&nbsp;&nbsp;&nbsp; private:<br>&nbsp;&nbsp;&nbsp; double real, imag;<br>&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp;&nbsp; complex operator +(const complex &amp;c1, const complex &amp;c2)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; return complex(c1.real + c2.real, c1.imag + c2.imag);<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; complex operator -(const complex &amp;c1, const complex &amp;c2)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; return complex(c1.real - c2.real, c1.imag - c2.imag);<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; complex operator *(const complex &amp;c1, const complex &amp;c2)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; return complex(c1.real * c2.real - c1.imag * c2.imag, c1.real * c2.imag + c1.imag * c2.real);<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; complex operator /(const complex &amp;c1, const complex &amp;c2)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; return complex((c1.real * c2.real + c1.imag + c2.imag) / (c2.real * c2.real + c2.imag * c2.imag),<br>&nbsp;&nbsp;&nbsp; (c1.imag * c2.real - c1.real * c2.imag) / (c2.real * c2.real + c2.imag * c2.imag));<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; void print(const complex &amp;c)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; if(c.imag＜0)<br>&nbsp;&nbsp;&nbsp; cout＜＜c.real＜＜c.imag＜＜'i';<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp; cout＜＜c.real＜＜'+'＜＜c.imag＜＜'i';<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; void main()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; complex c1(2.0, 3.0), c2(4.0, -2.0), c3;<br>&nbsp;&nbsp;&nbsp; c3 = c1 + c2;<br>&nbsp;&nbsp;&nbsp; cout＜＜"\nc1+c2=";<br>&nbsp;&nbsp;&nbsp; print(c3);<br>&nbsp;&nbsp;&nbsp; c3 = c1 - c2;<br>&nbsp;&nbsp;&nbsp; cout＜＜"\nc1-c2=";<br>&nbsp;&nbsp;&nbsp; print(c3);<br>&nbsp;&nbsp;&nbsp; c3 = c1 * c2;<br>&nbsp;&nbsp;&nbsp; cout＜＜"\nc1*c2=";<br>&nbsp;&nbsp;&nbsp; print(c3);<br>&nbsp;&nbsp;&nbsp; c3 = c1 / c2;<br>&nbsp;&nbsp;&nbsp; cout＜＜"\nc1/c2=";<br>&nbsp;&nbsp;&nbsp; print(c3);<br>&nbsp;&nbsp;&nbsp; c3 = (c1+c2) * (c1-c2) * c2/c1;<br>&nbsp;&nbsp;&nbsp; cout＜＜"\n(c1+c2)*(c1-c2)*c2/c1=";<br>&nbsp;&nbsp;&nbsp; print(c3);<br>&nbsp;&nbsp;&nbsp; cout＜＜endl;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; 　　该程序的运行结果与上例相同。前面已讲过，对又目运算符，重载为成员函数时，仅一个参数，另一个被隐含；重载为友元函数时，有两个参数，没有隐含参数。因此，程序中出现的 c1+c2</p>
<p>&nbsp;&nbsp;&nbsp; 　　编译程序解释为：</p>
<p>&nbsp;&nbsp;&nbsp; 　　operator+(c1, c2)</p>
<p>&nbsp;&nbsp;&nbsp; 　　调用如下函数，进行求值，</p>
<p>&nbsp;&nbsp;&nbsp; 　　complex operator +(const coplex &amp;c1, const complex &amp;c2)</p>
<p>&nbsp;</p>
<br>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; 3. 两种重载形式的比较</p>
<p>&nbsp;&nbsp;&nbsp; 　　一般说来，单目运算符最好被重载为成员；对双目运算符最好被重载为友元函数，双目运算符重载为友元函数比重载为成员函数更方便此，但是，有的双目运算符还是重载为成员函数为好，例如，赋值运算符。因为，它如果被重载为友元函数，将会出现与赋值语义不一致的地方。 其他运算符的重载举例</p>
<p>&nbsp;&nbsp;&nbsp; 　　1).下标运算符重载</p>
<p>&nbsp;&nbsp;&nbsp; 　　由于C语言的数组中并没有保存其大小，因此，不能对数组元素进行存取范围的检查，无法保证给数组动态赋值不会越界。利用C++的类可以定义一种更<a class=channel_keylink href="http://security.chinaitlab.com/" target=_blank><font color=#0000ff>安全</font></a>、功能强的数组类型。为此，为该类定义重载运算符[]。</p>
<p>&nbsp;&nbsp;&nbsp; 　　下面先看看一个例子：</p>
<p>&nbsp;&nbsp;&nbsp; #include ＜iostream.h＞</p>
<p>&nbsp;&nbsp;&nbsp; class CharArray<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; public:<br>&nbsp;&nbsp;&nbsp; CharArray(int l)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; Length = l;<br>&nbsp;&nbsp;&nbsp; Buff = new char[Length];<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; ~CharArray() { delete Buff; }<br>&nbsp;&nbsp;&nbsp; int GetLength() { return Length; }<br>&nbsp;&nbsp;&nbsp; char &amp; operator [](int i);<br>&nbsp;&nbsp;&nbsp; private:<br>&nbsp;&nbsp;&nbsp; int Length;<br>&nbsp;&nbsp;&nbsp; char * Buff;<br>&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp;&nbsp; char &amp; CharArray::operator [](int i)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; static char ch = 0;<br>&nbsp;&nbsp;&nbsp; if(i＜Length&amp;&amp;i＞=0)<br>&nbsp;&nbsp;&nbsp; return Buff[i];<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; cout＜＜"\nIndex out of range.";<br>&nbsp;&nbsp;&nbsp; return ch;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; void main()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; int cnt;<br>&nbsp;&nbsp;&nbsp; CharArray string1(6);<br>&nbsp;&nbsp;&nbsp; char * string2 = "string";<br>&nbsp;&nbsp;&nbsp; for(cnt=0; cnt＜8; cnt++)<br>&nbsp;&nbsp;&nbsp; string1[cnt] = string2[cnt];<br>&nbsp;&nbsp;&nbsp; cout＜＜"\n";<br>&nbsp;&nbsp;&nbsp; for(cnt=0; cnt＜8; cnt++)<br>&nbsp;&nbsp;&nbsp; cout＜＜string1[cnt];<br>&nbsp;&nbsp;&nbsp; cout＜＜"\n";<br>&nbsp;&nbsp;&nbsp; cout＜＜string1.GetLength()＜＜endl;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; 　　该数组类的优点如下：</p>
<p>&nbsp;&nbsp;&nbsp; 　　(1) 其大小不心是一个常量。<br>&nbsp;&nbsp;&nbsp; 　　(2) 运行时动态指定大小可以不用运算符new和delete。<br>&nbsp;&nbsp;&nbsp; 　　(3) 当使用该类数组作函数参数时，不心分别传递数组变量本身及其大小，因为该对象中已经保存大小。</p>
<p>&nbsp;&nbsp;&nbsp; 　　在重载下标运算符函数时应该注意：</p>
<p>&nbsp;&nbsp;&nbsp; 　　(1) 该函数只能带一个参数，不可带多个参数。<br>&nbsp;&nbsp;&nbsp; 　　(2) 不得重载为友元函数，必须是非static类的成员函数。 2). 重载增1减1运算符</p>
<p>&nbsp;&nbsp;&nbsp; 　　增1减1运算符是单目运算符。它们又有前缀和后缀运算两种。为了区分这两种运算，将后缀运算视为又目运算符。表达式</p>
<p>&nbsp;&nbsp;&nbsp; 　　obj++或obj--</p>
<p>&nbsp;&nbsp;&nbsp; 　　被看作为：</p>
<p>&nbsp;&nbsp;&nbsp; 　　obj++0或obj--0</p>
<p>&nbsp;&nbsp;&nbsp; 　　下面举一例子说明重载增1减1运算符的应用。</p>
<p>&nbsp;&nbsp;&nbsp; #include ＜iostream.h＞</p>
<p>&nbsp;&nbsp;&nbsp; class counter<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; public:<br>&nbsp;&nbsp;&nbsp; counter() { v=0; }<br>&nbsp;&nbsp;&nbsp; counter operator ++();<br>&nbsp;&nbsp;&nbsp; counter operator ++(int );<br>&nbsp;&nbsp;&nbsp; void print() { cout＜＜v＜＜endl; }<br>&nbsp;&nbsp;&nbsp; private:<br>&nbsp;&nbsp;&nbsp; unsigned v;<br>&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp;&nbsp; counter counter::operator ++()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; v++;<br>&nbsp;&nbsp;&nbsp; return *this;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; counter counter::operator ++(int)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; counter t;<br>&nbsp;&nbsp;&nbsp; t.v = v++;<br>&nbsp;&nbsp;&nbsp; return t;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; void main()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; counter c;<br>&nbsp;&nbsp;&nbsp; for(int i=0; i＜8; i++)<br>&nbsp;&nbsp;&nbsp; c++;<br>&nbsp;&nbsp;&nbsp; c.print();<br>&nbsp;&nbsp;&nbsp; for(i=0; i＜8; i++)<br>&nbsp;&nbsp;&nbsp; ++c;<br>&nbsp;&nbsp;&nbsp; c.print();<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; 　　4). 重载函数调用运算符</p>
<p>&nbsp;&nbsp;&nbsp; 　　可以将函数调用运算符()看成是下标运算[]的扩展。函数调用运算符可以带0个至多个参数。下面通过一个实例来熟悉函数调用运算符的重载。<br>&nbsp;&nbsp;&nbsp; #include ＜iostream.h＞</p>
<p>&nbsp;&nbsp;&nbsp; class F<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; public:<br>&nbsp;&nbsp;&nbsp; double operator ()(double x, double y) const;<br>&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp;&nbsp; double F::operator ()(double x, double y) const<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; return (x+5)*y;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; void main()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; F f;<br>&nbsp;&nbsp;&nbsp; cout＜＜f(1.5, 2.2)＜＜endl;<br>&nbsp;&nbsp;&nbsp; }</p>
<img src ="http://www.cppblog.com/deane/aggbug/75301.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-03-02 10:42 <a href="http://www.cppblog.com/deane/articles/75301.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++的运算符重载</title><link>http://www.cppblog.com/deane/articles/75298.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Mon, 02 Mar 2009 02:26:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/75298.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/75298.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/75298.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/75298.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/75298.html</trackback:ping><description><![CDATA[&nbsp;
<h1 class=title_txt><img height=14 alt=转载 src="http://blog.csdn.net/images/turnship.gif" width=15 border=0>&nbsp;<a href="http://blog.csdn.net/zgl_dm/archive/2007/08/31/1767201.aspx"><font color=#800080><u>C++的运算符重载</u></font></a></h1>
<div class=blogstory>
<script>function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}</script>
<p class=MsoNormal style="LINE-HEIGHT: 130%"><span lang=EN-US style="FONT-SIZE: 12pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体"><u><font face=Tahoma color=#0000ff size=2></font></u></span><span style="FONT-SIZE: 12pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体"><span lang=EN-US><o:p></o:p></span></p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><u><font face=Tahoma color=#0000ff size=2><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top></font></u><span style="COLOR: rgb(0,0,0)">　　C</span><span style="COLOR: rgb(0,0,0)">++</span><span style="COLOR: rgb(0,0,0)">中预定义的运算符的操作对象只能是基本数据类型。但实际上，对于许多用户自定义类型（例如类），也需要类似的运算操作。这时就必须在C</span><span style="COLOR: rgb(0,0,0)">++</span><span style="COLOR: rgb(0,0,0)">中重新定义这些运算符，赋予已有运算符新的功能，使它能够用于特定类型执行特定的操作。运算符重载的实质是函数重载，它提供了C</span><span style="COLOR: rgb(0,0,0)">++</span><span style="COLOR: rgb(0,0,0)">的可扩展性，也是C</span><span style="COLOR: rgb(0,0,0)">++</span><span style="COLOR: rgb(0,0,0)">最吸引人的特性之一。&nbsp;</span></div>
</div>
<br>　　运算符重载是通过创建运算符函数实现的，运算符函数定义了重载的运算符将要进行的操作。运算符函数的定义与其他函数的定义类似，惟一的区别是运算符函数的函数名是由关键字<span lang=EN-US>operator</span>和其后要重载的运算符符号构成的。运算符函数定义的一般格式如下： <span lang=EN-US><o:p></o:p></span></span>
<p>&#160;</p>
<p class=MsoNormal style="LINE-HEIGHT: 130%"><span lang=EN-US style="FONT-SIZE: 12pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; </p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">返回类型说明符</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">operator</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">运算符符号</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)">(</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">参数表</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)">)<br><img id=_35_50_Open_Image onclick="this.style.display='none'; document.getElementById('_35_50_Open_Text').style.display='none'; document.getElementById('_35_50_Closed_Image').style.display='inline'; document.getElementById('_35_50_Closed_Text').style.display='inline';" alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=_35_50_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_35_50_Closed_Text').style.display='none'; document.getElementById('_35_50_Open_Image').style.display='inline'; document.getElementById('_35_50_Open_Text').style.display='inline';" alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=_35_50_Closed_Text style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; DISPLAY: none; BORDER-LEFT: rgb(128,128,128) 1px solid; BORDER-BOTTOM: rgb(128,128,128) 1px solid; BACKGROUND-COLOR: rgb(255,255,255)">...</span><span id=_35_50_Open_Text><span style="COLOR: rgb(0,0,0)">{<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">函数体</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span></div>
</div>
<o:p></o:p></span>
<p>&#160;</p>
<p class=MsoNormal style="LINE-HEIGHT: 130%"><span lang=EN-US style="FONT-SIZE: 12pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体"></span><span style="FONT-SIZE: 12pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体">　<span style="FONT-WEIGHT: bold; COLOR: rgb(0,128,128)">运算符重载时要遵循以下规则：</span> <span lang=EN-US><o:p></o:p></span></span></p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><span style="COLOR: rgb(0,0,0)">(</span><span style="COLOR: rgb(0,0,0)">1</span><span style="COLOR: rgb(0,0,0)">)&nbsp;除了类属关系运算符</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">.</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">、成员指针运算符</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">.*</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">、作用域运算符</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">::</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">、sizeof运算符和三目运算符</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">?:</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">以外，C</span><span style="COLOR: rgb(0,0,0)">++</span><span style="COLOR: rgb(0,0,0)">中的所有运算符都可以重载。<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>(</span><span style="COLOR: rgb(0,0,0)">2</span><span style="COLOR: rgb(0,0,0)">)&nbsp;重载运算符限制在C</span><span style="COLOR: rgb(0,0,0)">++</span><span style="COLOR: rgb(0,0,0)">语言中已有的运算符范围内的允许重载的运算符之中，不能创建新的运算符。<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>(</span><span style="COLOR: rgb(0,0,0)">3</span><span style="COLOR: rgb(0,0,0)">)&nbsp;运算符重载实质上是函数重载，因此编译程序对运算符重载的选择，遵循函数重载的选择原则。<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>(</span><span style="COLOR: rgb(0,0,0)">4</span><span style="COLOR: rgb(0,0,0)">)&nbsp;重载之后的运算符不能改变运算符的优先级和结合性，也不能改变运算符操作数的个数及语法结构。<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>(</span><span style="COLOR: rgb(0,0,0)">5</span><span style="COLOR: rgb(0,0,0)">)&nbsp;运算符重载不能改变该运算符用于内部类型对象的含义。它只能和用户自定义类型的对象一起使用，或者用于用户自定义类型的对象和内部类型的对象混合使用时。<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>(</span><span style="COLOR: rgb(0,0,0)">6</span><span style="COLOR: rgb(0,0,0)">)&nbsp;运算符重载是针对新类型数据的实际需要对原有运算符进行的适当的改造，重载的功能应当与原有功能相类似，避免没有目的地使用重载运算符。</span></div>
</div>
<span style="FONT-SIZE: 12pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体"><br>　　运算符函数重载一般有两种形式：重载为类的成员函数和重载为类的非成员函数。非成员函数通常是友元。（可以把一个运算符作为一个非成员、非友元函数重载。但是，这样的运算符函数访问类的私有和保护成员时，必须使用类的公有接口中提供的设置数据和读取数据的函数，调用这些函数时会降低性能。可以内联这些函数以提高性能。）<span lang=EN-US>&nbsp;&nbsp;&nbsp;<o:p></o:p></span></span>
<p class=MsoNormal style="LINE-HEIGHT: 130%"><span style="FONT-SIZE: 12pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体"><span style="FONT-WEIGHT: bold; COLOR: rgb(0,128,128)">成员函数运算符</span> <span lang=EN-US><o:p></o:p></span></span></p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><span style="COLOR: rgb(0,0,0)">　运算符重载为类的成员函数的一般格式为：<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">函数类型</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">operator</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">运算符</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)">(</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">参数表</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)">)<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img id=_59_78_Open_Image onclick="this.style.display='none'; document.getElementById('_59_78_Open_Text').style.display='none'; document.getElementById('_59_78_Closed_Image').style.display='inline'; document.getElementById('_59_78_Closed_Text').style.display='inline';" alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=_59_78_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_59_78_Closed_Text').style.display='none'; document.getElementById('_59_78_Open_Image').style.display='inline'; document.getElementById('_59_78_Open_Text').style.display='inline';" alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=_59_78_Closed_Text style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; DISPLAY: none; BORDER-LEFT: rgb(128,128,128) 1px solid; BORDER-BOTTOM: rgb(128,128,128) 1px solid; BACKGROUND-COLOR: rgb(255,255,255)">...</span><span id=_59_78_Open_Text><span style="COLOR: rgb(0,0,0)">{<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">函数体</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span></div>
</div>
<p class=MsoNormal style="LINE-HEIGHT: 130%"><span style="FONT-SIZE: 12pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体">　　当运算符重载为类的成员函数时，函数的参数个数比原来的操作数要少一个（后置单目运算符除外），这是因为成员函数用<span lang=EN-US>this</span>指针隐式地访问了类的一个对象，它充当了运算符函数最左边的操作数。因此： <span lang=EN-US><o:p></o:p></span></span></p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><span style="COLOR: rgb(0,0,0)">(</span><span style="COLOR: rgb(0,0,0)">1</span><span style="COLOR: rgb(0,0,0)">)&nbsp;双目运算符重载为类的成员函数时，函数只显式说明一个参数，该形参是运算符的右操作数。<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>(</span><span style="COLOR: rgb(0,0,0)">2</span><span style="COLOR: rgb(0,0,0)">)&nbsp;前置单目运算符重载为类的成员函数时，不需要显式说明参数，即函数没有形参。<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>(</span><span style="COLOR: rgb(0,0,0)">3</span><span style="COLOR: rgb(0,0,0)">)&nbsp;后置单目运算符重载为类的成员函数时，函数要带有一个整型形参。<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;调用成员函数运算符的格式如下：<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">对象名</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)">.</span><span style="COLOR: rgb(0,0,255)">operator</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">运算符</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)">(</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">参数</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)">)<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;它等价于<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">对象名</span><span style="COLOR: rgb(0,0,0)">&gt;&lt;</span><span style="COLOR: rgb(0,0,0)">运算符</span><span style="COLOR: rgb(0,0,0)">&gt;&lt;</span><span style="COLOR: rgb(0,0,0)">参数</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;例如：a</span><span style="COLOR: rgb(0,0,0)">+</span><span style="COLOR: rgb(0,0,0)">b等价于a.</span><span style="COLOR: rgb(0,0,255)">operator</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,0)">+</span><span style="COLOR: rgb(0,0,0)">(b)。一般情况下，我们采用运算符的习惯表达方式。</span></div>
</div>
<p class=MsoNormal style="LINE-HEIGHT: 130%"><span style="FONT-SIZE: 12pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体"><span style="FONT-WEIGHT: bold; COLOR: rgb(0,128,128)">友元函数运算符</span> <span lang=EN-US><o:p></o:p></span></span></p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><span style="COLOR: rgb(0,0,0)">　运算符重载为类的友元函数的一般格式为：<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;friend&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">函数类型</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">operator</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">运算符</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)">(</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">参数表</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)">)<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img id=_66_85_Open_Image onclick="this.style.display='none'; document.getElementById('_66_85_Open_Text').style.display='none'; document.getElementById('_66_85_Closed_Image').style.display='inline'; document.getElementById('_66_85_Closed_Text').style.display='inline';" alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=_66_85_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_66_85_Closed_Text').style.display='none'; document.getElementById('_66_85_Open_Image').style.display='inline'; document.getElementById('_66_85_Open_Text').style.display='inline';" alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=_66_85_Closed_Text style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; DISPLAY: none; BORDER-LEFT: rgb(128,128,128) 1px solid; BORDER-BOTTOM: rgb(128,128,128) 1px solid; BACKGROUND-COLOR: rgb(255,255,255)">...</span><span id=_66_85_Open_Text><span style="COLOR: rgb(0,0,0)">{<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">函数体</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span></div>
</div>
<p class=MsoNormal style="LINE-HEIGHT: 130%"><span style="FONT-SIZE: 12pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体">　　当运算符重载为类的友元函数时，由于没有隐含的<span lang=EN-US>this</span>指针，因此操作数的个数没有变化，所有的操作数都必须通过函数的形参进行传递，函数的参数与操作数自左至右一一对应。 <span lang=EN-US><o:p></o:p></span></span></p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><span style="COLOR: rgb(0,0,0)">　调用友元函数运算符的格式如下：<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">operator</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">运算符</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)">(</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">参数1</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)">,</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">参数2</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)">)<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;它等价于<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">参数1</span><span style="COLOR: rgb(0,0,0)">&gt;&lt;</span><span style="COLOR: rgb(0,0,0)">运算符</span><span style="COLOR: rgb(0,0,0)">&gt;&lt;</span><span style="COLOR: rgb(0,0,0)">参数2</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;例如：a</span><span style="COLOR: rgb(0,0,0)">+</span><span style="COLOR: rgb(0,0,0)">b等价于operator&nbsp;</span><span style="COLOR: rgb(0,0,0)">+</span><span style="COLOR: rgb(0,0,0)">(a,b)。</span></div>
</div>
<p class=MsoNormal style="LINE-HEIGHT: 130%"><span style="FONT-SIZE: 12pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体"><span style="FONT-WEIGHT: bold; COLOR: rgb(0,128,128)">两种重载形式的比较</span> <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="LINE-HEIGHT: 130%"><span style="FONT-SIZE: 12pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体">　　在多数情况下，将运算符重载为类的成员函数和类的友元函数都是可以的。但成员函数运算符与友元函数运算符也具有各自的一些特点： <span lang=EN-US><o:p></o:p></span></span></p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><span style="COLOR: rgb(0,0,0)">(</span><span style="COLOR: rgb(0,0,0)">1</span><span style="COLOR: rgb(0,0,0)">)&nbsp;一般情况下，单目运算符最好重载为类的成员函数；双目运算符则最好重载为类的友元函数。<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>(</span><span style="COLOR: rgb(0,0,0)">2</span><span style="COLOR: rgb(0,0,0)">)&nbsp;以下一些双目运算符不能重载为类的友元函数：</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">、()、[]、</span><span style="COLOR: rgb(0,0,0)">-&gt;</span><span style="COLOR: rgb(0,0,0)">。<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>(</span><span style="COLOR: rgb(0,0,0)">3</span><span style="COLOR: rgb(0,0,0)">)&nbsp;类型转换函数只能定义为一个类的成员函数而不能定义为类的友元函数。<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>(</span><span style="COLOR: rgb(0,0,0)">4</span><span style="COLOR: rgb(0,0,0)">)&nbsp;若一个运算符的操作需要修改对象的状态，选择重载为成员函数较好。<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>(</span><span style="COLOR: rgb(0,0,0)">5</span><span style="COLOR: rgb(0,0,0)">)&nbsp;若运算符所需的操作数（尤其是第一个操作数）希望有隐式类型转换，则只能选用友元函数。<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>(</span><span style="COLOR: rgb(0,0,0)">6</span><span style="COLOR: rgb(0,0,0)">)&nbsp;当运算符函数是一个成员函数时，最左边的操作数（或者只有最左边的操作数）必须是运算符类的一　个类对象（或者是对该类对象的引用）。如果左边的操作数必须是一个不同类的对象，或者是一个内部　类型的对象，该运算符函数必须作为一个友元函数来实现。<br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align=top>(</span><span style="COLOR: rgb(0,0,0)">7</span><span style="COLOR: rgb(0,0,0)">)&nbsp;当需要重载运算符具有可交换性时，选择重载为友元函数。</span></div>
</div>
<br>
<p class=MsoNormal style="LINE-HEIGHT: 130%"><span style="FONT-SIZE: 12pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体"><span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="LINE-HEIGHT: 130%"><span lang=EN-US style="FONT-SIZE: 12pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体"><o:p>&nbsp;</o:p></span></p>
</div>
<img src ="http://www.cppblog.com/deane/aggbug/75298.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-03-02 10:26 <a href="http://www.cppblog.com/deane/articles/75298.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>异步消息的传递－回调机制</title><link>http://www.cppblog.com/deane/articles/49221.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Thu, 08 May 2008 08:23:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/49221.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/49221.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/49221.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/49221.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/49221.html</trackback:ping><description><![CDATA[<p>&nbsp;</p>
<p>&nbsp;</p>
from:http://www.ibm.com/developerworks/cn/linux/l-callback/index.html<br><br>
<p>级别： 初级</p>
<p><a href="http://www.ibm.com/developerworks/cn/linux/l-callback/index.html#author" cmImpressionSent="1"><u><font color=#996699>陈家朋</font></u></a> (<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#106;&#97;&#112;&#101;&#110;&#64;&#118;&#105;&#112;&#46;&#115;&#105;&#110;&#97;&#46;&#99;&#111;&#109;&#63;&#115;&#117;&#98;&#106;&#101;&#99;&#116;&#61;&#24322;&#27493;&#28040;&#24687;&#30340;&#20256;&#36882;&#65293;&#22238;&#35843;&#26426;&#21046;" cmImpressionSent="1"><u><font color=#5c81a7>japen@vip.sina.com</font></u></a>), 系统架构师和技术顾问, 杭州迈可行通信技术有限公司<br></p>
<p>2003 年 3 月 01 日</p>
<blockquote>软件模块之间总是存在着一定的接口，从调用方式上，可以把他们分为三类：同步调用、回调和异步调用。同步调用是一种阻塞式调用，调用方要等待对方执行完毕才返回，它是一种单向调用；回调是一种双向调用模式，也就是说，被调用方在接口被调用时也会调用对方的接口；异步调用是一种类似消息或事件的机制，不过它的调用方向刚好相反，接口的服务在收到某种讯息或发生某种事件时，会主动通知客户方（即调用客户方的接口）。回调和异步调用的关系非常紧密，通常我们使用回调来实现异步消息的注册，通过异步调用来实现消息的通知。同步调用是三者当中最简单的，而回调又常常是异步调用的基础，因此，下面我们着重讨论回调机制在不同软件架构中的实现。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p><a name=1><span class=atitle>1 什么是回调</span></a></p>
<p>软件模块之间总是存在着一定的接口，从调用方式上，可以把他们分为三类：同步调用、回调和异步调用。同步调用是一种阻塞式调用，调用方要等待对方执行完毕才返回，它是一种单向调用；回调是一种双向调用模式，也就是说，被调用方在接口被调用时也会调用对方的接口；异步调用是一种类似消息或事件的机制，不过它的调用方向刚好相反，接口的服务在收到某种讯息或发生某种事件时，会主动通知客户方（即调用客户方的接口）。回调和异步调用的关系非常紧密，通常我们使用回调来实现异步消息的注册，通过异步调用来实现消息的通知。同步调用是三者当中最简单的，而回调又常常是异步调用的基础，因此，下面我们着重讨论回调机制在不同软件架构中的实现。 </p>
<br><img alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/image001.gif"> <br>
<p>对于不同类型的语言（如结构化语言和对象语言）、平台（Win32、JDK）或构架（CORBA、DCOM、WebService），客户和服务的交互除了同步方式以外，都需要具备一定的异步通知机制，让服务方（或接口提供方）在某些情况下能够主动通知客户，而回调是实现异步的一个最简捷的途径。 </p>
<p>对于一般的结构化语言，可以通过回调函数来实现回调。回调函数也是一个函数或过程，不过它是一个由调用方自己实现，供被调用方使用的特殊函数。 </p>
<p>在面向对象的语言中，回调则是通过接口或抽象类来实现的，我们把实现这种接口的类成为回调类，回调类的对象成为回调对象。对于象C++或Object Pascal这些兼容了过程特性的对象语言，不仅提供了回调对象、回调方法等特性，也能兼容过程语言的回调函数机制。 </p>
<p>Windows平台的消息机制也可以看作是回调的一种应用，我们通过系统提供的接口注册消息处理函数（即回调函数），从而实现接收、处理消息的目的。由于Windows平台的API是用C语言来构建的，我们可以认为它也是回调函数的一个特例。 </p>
<p>对于分布式组件代理体系CORBA，异步处理有多种方式，如回调、事件服务、通知服务等。事件服务和通知服务是CORBA用来处理异步消息的标准服务，他们主要负责消息的处理、派发、维护等工作。对一些简单的异步处理过程，我们可以通过回调机制来实现。 </p>
<p>下面我们集中比较具有代表性的语言（C、Object Pascal）和架构（CORBA）来分析回调的实现方式、具体作用等。 </p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/blue_rule.gif" width="100%"><br><img height=6 alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-callback/index.html#main" cmImpressionSent="1"><strong><font color=#996699><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=2><span class=atitle>2 过程语言中的回调（C）</span></a></p>
<p><a name=N1006A><span class=smalltitle><strong><font face=Arial>2.1 函数指针</font></strong></span></a></p>
<p>回调在C语言中是通过函数指针来实现的,通过将回调函数的地址传给被调函数从而实现回调。因此，要实现回调，必须首先定义函数指针，请看下面的例子： </p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>void Func(char *s)；// 函数原型
            void (*pFunc) (char *);//函数指针
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>可以看出，函数的定义和函数指针的定义非常类似。 </p>
<p>一般的化，为了简化函数指针类型的变量定义，提高程序的可读性，我们需要把函数指针类型自定义一下。 </p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>typedef void(*pcb)(char *);
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>回调函数可以象普通函数一样被程序调用，但是只有它被当作参数传递给被调函数时才能称作回调函数。 </p>
<p>被调函数的例子：</p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>void GetCallBack(pcb callback)
            {
            /*do something*/
            }
            用户在调用上面的函数时，需要自己实现一个pcb类型的回调函数：
            void fCallback(char *s)
            {
            /* do something */
            }
            然后，就可以直接把fCallback当作一个变量传递给GetCallBack,
            GetCallBack（fCallback）;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>如果赋了不同的值给该参数，那么调用者将调用不同地址的函数。赋值可以发生在运行时，这样使你能实现动态绑定。 </p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/blue_rule.gif" width="100%"><br><img height=6 alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-callback/index.html#main" cmImpressionSent="1"><strong><font color=#996699><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=2.2><span class=atitle>2.2 参数传递规则</span></a></p>
<p>到目前为止，我们只讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。许多编译器有几种调用规范。如在Visual C++中，可以在函数类型前加_cdecl，_stdcall或者_pascal来表示其调用规范（默认为_cdecl）。C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名，参数传递的顺序（从右到左或从左到右），堆栈清理责任（调用者或者被调用者）以及参数传递机制（堆栈，CPU寄存器等）。 </p>
<p>将调用规范看成是函数类型的一部分是很重要的；不能用不兼容的调用规范将地址赋值给函数指针。例如： </p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>// 被调用函数是以int为参数，以int为返回值
            __stdcall int callee(int);
            // 调用函数以函数指针为参数
            void caller( __cdecl int(*ptr)(int));
            // 在p中企图存储被调用函数地址的非法操作
            __cdecl int(*p)(int) = callee; // 出错
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>指针p和callee()的类型不兼容，因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针p，尽管两者有相同的返回值和参数列 </p>
<p><a name=N100A1><span class=smalltitle><strong><font face=Arial>2.3 应用举例</font></strong></span></a></p>
<p>C语言的标准库函数中很多地方就采用了回调函数来让用户定制处理过程。如常用的快速排序函数、二分搜索函数等。 </p>
<p>快速排序函数原型：</p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>void qsort(void *base, size_t nelem, size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
            二分搜索函数原型：
            void *bsearch(const void *key, const void *base, size_t nelem,
            size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>其中fcmp就是一个回调函数的变量。 </p>
<p>下面给出一个具体的例子： </p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>#include &lt;stdio.h&gt;
            #include &lt;stdlib.h&gt;
            int sort_function( const void *a, const void *b);
            int list[5] = { 54, 21, 11, 67, 22 };
            int main(void)
            {
            int  x;
            qsort((void *)list, 5, sizeof(list[0]), sort_function);
            for (x = 0; x &lt; 5; x++)
            printf("%i\n", list[x]);
            return 0;
            }
            int sort_function( const void *a, const void *b)
            {
            return *(int*)a-*(int*)b;
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>2.4 面向对象语言中的回调（Delphi） </p>
<p>Dephi与C++一样，为了保持与过程语言Pascal的兼容性，它在引入面向对象机制的同时，保留了以前的结构化特性。因此，对回调的实现，也有两种截然不同的模式，一种是结构化的函数回调模式，一种是面向对象的接口模式。 </p>
<p><strong>2.4.1 回调函数</strong> </p>
<p>回调函数类型定义： </p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>type
            TCalcFunc=function (a:integer;b:integer):integer;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>按照回调函数的格式自定义函数的实现，如</p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>function Add(a:integer;b:integer):integer
            begin
            result:=a+b;
            end;
            function Sub(a:integer;b:integer):integer
            begin
            result:=a-b;
            end;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>回调的使用</p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>function Calc(calc:TcalcFunc;a:integer;b:integer):integer
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>下面，我们就可以在我们的程序里按照需要调用这两个函数了</p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>c:=calc(add,a,b);//c=a+b
            c:=calc(sub,a,b);//c=a-b
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p><strong>2.4.2 回调对象</strong> </p>
<p>什么叫回调对象呢，它具体用在哪些场合？首先，让我们把它与回调函数对比一下，回调函数是一个定义了函数的原型，函数体则交由第三方来实现的一种动态应用模式。要实现一个回调函数，我们必须明确知道几点：该函数需要那些参数，返回什么类型的值。同样，一个回调对象也是一个定义了对象接口，但是没有具体实现的抽象类（即接口）。要实现一个回调对象，我们必须知道：它需要实现哪些方法，每个方法中有哪些参数，该方法需要放回什么值。 </p>
<p>因此，在回调对象这种应用模式中，我们会用到接口。接口可以理解成一个定义好了但是没有实现的类，它只能通过继承的方式被别的类实现。Delphi中的接口和COM接口类似，所有的接口都继承与IInterface（等同于IUnknow），并且要实现三个基本的方法QueryInterface, _AddRef, 和_Release。 </p>
<ul>
    <li>定义一个接口 <br>
    <table cellSpacing=0 cellPadding=0 width="80%" border=0>
        <tbody>
            <tr>
                <td class=code-outline>
                <pre class=displaycode>type IShape=interface(IInterface)
                procedure Draw;
                end
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br>
    <li>实现回调类 <br>
    <table cellSpacing=0 cellPadding=0 width="80%" border=0>
        <tbody>
            <tr>
                <td class=code-outline>
                <pre class=displaycode>type TRect=class(TObject,IShape)
                protected
                function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
                function _AddRef: Integer; stdcall;
                function _Release: Integer; stdcall;
                public
                procedure Draw;
                end;
                type TRound=class(TObject,IShape)
                protected
                function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
                function _AddRef: Integer; stdcall;
                function _Release: Integer; stdcall;
                public
                procedure Draw;
                end;
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br>
    <li>使用回调对象 <br>
    <table cellSpacing=0 cellPadding=0 width="80%" border=0>
        <tbody>
            <tr>
                <td class=code-outline>
                <pre class=displaycode>procedure MyDraw(shape:IShape);
                var
                shape:IShape;
                begin
                shape.Draw;
                end;
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br></li>
</ul>
<p>如果传入的对象为TRect，那么画矩形；如果为TRound，那么就为圆形。用户也可以按照自己的意图来实现IShape接口，画出自己的图形： </p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>MyDraw(Trect.Create);
            MyDraw(Tround.Create);
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p><strong>2.4.3 回调方法</strong> </p>
<p>回调方法(Callback Method)可以看作是回调对象的一部分，Delphi对windows消息的封装就采用了回调方法这个概念。在有些场合，我们不需要按照给定的要求实现整个对象，而只要实现其中的一个方法就可以了，这是我们就会用到回调方法。 </p>
<p>回调方法的定义如下：</p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>TNotifyEvent = procedure(Sender: TObject) of object;
            TMyEvent=procedure(Sender:Tobject;EventId:Integer) of object;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>TNotifyEvent 是Delphi中最常用的回调方法，窗体、控件的很多事件，如单击事件、关闭事件等都是采用了TnotifyEvent。回调方法的变量一般通过事件属性的方式来定义，如TCustomForm的创建事件的定义： </p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>property OnCreate: TNotifyEvent read FOnCreate write FOnCreate stored IsForm;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>我们通过给事件属性变量赋值就可以定制事件处理器。</p>
<p>用户定义对象（包含回调方法的对象）：</p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>type TCallback=Class
            procedure ClickFunc(sender:TObject);
            end;
            procedure Tcallback.ClickFunc(sender:TObject);
            begin
            showmessage('the caller is clicked!');
            end;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>窗体对象：</p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>type TCustomFrm=class(TForm)
            public
            procedure RegisterClickFunc(cb:procedure(sender:Tobject) of object);
            end;
            procedure TcustomFrm..RegisterClickFunc(cb:TNotifyEvent);
            begin
            self.OnClick=cb;
            end;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>使用方法：</p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>var
            frm:TcustomFrm;
            begin
            frm:=TcustomFrm.Create(Application);
            frm.RegisterClickFunc(Tcallback.Create().ClickFunc);
            end;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/blue_rule.gif" width="100%"><br><img height=6 alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-callback/index.html#main" cmImpressionSent="1"><strong><font color=#996699><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=3><span class=atitle>3 回调在分布式计算中的应用（CORBA）</span></a></p>
<p><a name=N1014C><span class=smalltitle><strong><font face=Arial>3.1 回调接口模型</font></strong></span></a></p>
<p>CORBA的消息传递机制有很多种，比如回调接口、事件服务和通知服务等。回调接口的原理很简单，CORBA客户和服务器都具有双重角色，即充当服务器也是客户客户。 </p>
<p>回调接口的反向调用与正向调用往往是同时进行的，如果服务端多次调用该回调接口，那么这个回调接口就变成异步接口了。因此，回调接口在CORBA中常常充当事件注册的用途，客户端调用该注册函数时，客户函数就是回调函数，在此后的调用中，由于不需要客户端的主动参与，该函数就是实现了一种异步机制。 </p>
<p>从CORBA规范我们知道，一个CORBA接口在服务端和客户端有不同的表现形式，在客户端一般使用桩（Stub）文件，服务端则用到框架（Skeleton）文件，接口的规格采用IDL来定义。而回调函数的引入，使得服务端和客户端都需要实现一定的桩和框架。下面是回调接口的实现模型： </p>
<br><a name=N1015D><strong>3.1.1 范例</strong></a><br><img alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/image002.gif"> <br>
<p>下面给出了一个使用回调的接口文件，服务端需要实现Server接口的框架，客户端需要实现CallBack的框架：</p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>module cb
            {
            interface CallBack;
            interface Server;
            interface CallBack
            {
            void OnEvent(in long Source,in long msg);
            };
            interface Server
            {
            long RegisterCB(in CallBack cb);
            void UnRegisterCB(in long hCb);
            };
            };
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>客户端首先通过同步方式调用服务端的接口RegistCB，用来注册回调接口CallBack。服务端收到该请求以后，就会保留该接口引用，如果发生某种事件需要向客户端通知的时候就通过该引用调用客户方的OnEvent函数，以便对方及时处理。 </p>
<p>本文源码 <a href="http://www.ibm.com/developerworks/cn/linux/l-callback/samplecode.rar" cmImpressionSent="1"><u><font color=#5c81a7>下载</font></u></a>。 </p>
<br><br>
<p><a name=author><span class=atitle>关于作者</span></a></p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td colSpan=3><img height=5 alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/c.gif" width="100%"></td>
        </tr>
        <tr vAlign=top align=left>
            <td>
            <p>&nbsp;</p>
            </td>
            <td><img height=5 alt="" src="file:///F:/My%20Document/异步消息的传递－回调机制%20-%20牵着老婆满街逛%20-%20C++博客.files/c.gif" width=4></td>
            <td width="100%">
            <p>陈家朋，系统架构师和技术顾问，目前担任杭州迈可行通信技术有限公司MPS2000业务交换平台的首席设计人员。擅长架构设计、提供技术解决方案等工作，熟知计算机和通信领域的各种前沿技术。可以通过 <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#106;&#97;&#112;&#101;&#110;&#64;&#118;&#105;&#112;&#46;&#115;&#105;&#110;&#97;&#46;&#99;&#111;&#109;&#63;&#99;&#99;&#61;" cmImpressionSent="1"><u><font color=#5c81a7>japen@vip.sina.com</font></u></a>跟他联系。</p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/deane/aggbug/49221.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2008-05-08 16:23 <a href="http://www.cppblog.com/deane/articles/49221.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于C++的RTTI</title><link>http://www.cppblog.com/deane/articles/49209.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Thu, 08 May 2008 06:01:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/49209.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/49209.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/49209.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/49209.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/49209.html</trackback:ping><description><![CDATA[<p><br>&nbsp;</p>
<p style="LINE-HEIGHT: 12pt; TEXT-ALIGN: center" align=center><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>关于<span lang=EN-US>C++</span>的<span lang=EN-US>RTTI<o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>RTTI(Run-Time Type Identification)</font></span><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>是面向对象程序设计中一种重要的技术。现行的<span lang=EN-US>C++</span>标准对<span lang=EN-US>RTTI</span>已经有了明确的支持。不过在某些情况下出于特殊的开发需要，我们需要自己编码来实现。本文介绍了一些关于<span lang=EN-US>RTTI</span>的基础知识及其原理和实现。</font><span lang=EN-US><br><font face=宋体>RTTI</font></span><font face=宋体>需求：<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　和很多其他语言一样，<span lang=EN-US>C++</span>是一种静态类型语言。其数据类型是在编译期就确定的，不能在运行时更改。然而由于面向对象程序设计中多态性的要求，<span lang=EN-US>C++</span>中的指针或引用<span lang=EN-US>(Reference)</span>本身的类型，可能与它实际代表<span lang=EN-US>(</span>指向或引用<span lang=EN-US>)</span>的类型并不一致。有时我们需要将一个多态指针转换为其实际指向对象的类型，就需要知道运行时的类型信息，这就产生了运行时类型识别的要求。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　<span lang=EN-US>C++</span>对<span lang=EN-US>RTTI</span>的支持：<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　<span lang=EN-US>C++</span>提供了两个关键字<span lang=EN-US>typeid</span>和<span lang=EN-US>dynamic_cast</span>和一个<span lang=EN-US>type_info</span>类来支持<span lang=EN-US>RTTI</span>：<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　<span lang=EN-US>dynamic_cast</span>操作符：它允许在运行时刻进行类型转换，从而使程序能够在一个类层次结构安全地转换类型。<span lang=EN-US>dynamic_cast</span>提供了两种转换方式，把基类指针转换成派生类指针，或者把指向基类的左值转换成派生类的引用。见下例讲述：<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>void company::payroll(employee *pe) {<br>//</font></span><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>对指针转换失败，<span lang=EN-US>dynamic_cast</span>返回</font><font face=宋体><span lang=EN-US>NULL<br>if(programmer *pm=dynamic_cast(pe)){<br>pm-&gt;bonus(); <br>}<br>}<br>void company::payroll(employee &amp;re) {<br>try{<br>//</span>对引用转换失败的话，则会以抛出异常来报告错误</font><span lang=EN-US><br><font face=宋体>programmer &amp;rm=dynamic_cast(re);<br>pm-&gt;bonus();<br>}<br>catch(std::bad_cast){<o:p></o:p></font></span></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><font face=宋体><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black">}<br>}<br><br></span><span style="FONT-SIZE: 9pt; COLOR: black">　　这里<span lang=EN-US>bonus</span>是<span lang=EN-US>programmer</span>的成员函数，基类<span lang=EN-US>employee</span>不具备这个特性。所以我们必须使用安全的由基类到派生类类型转换，识别出<span lang=EN-US>programmer</span>指针。<span lang=EN-US><o:p></o:p></span></span></font></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　<span lang=EN-US>typeid</span>操作符：它指出指针或引用指向的对象的实际派生类型。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　例如：<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><font face=宋体><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black">employee* pe=new manager;<br>typeid(*pe)==typeid(manager) //true <br></span><span style="FONT-SIZE: 9pt; COLOR: black">　　<span lang=EN-US>typeid</span>可以用于作用于各种类型名，对象和内置基本数据类型的实例、指针或者引用，当作用于指针和引用将返回它实际指向对象的类型信息。<span lang=EN-US>typeid</span>的返回是<span lang=EN-US>type_info</span>类型。<span lang=EN-US><o:p></o:p></span></span></font></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　<span lang=EN-US>type_info</span>类：这个类的确切定义是与编译器实现相关的，下面是《<span lang=EN-US>C++ Primer</span>》中给出的定义<span lang=EN-US>(</span>参考资料<span lang=EN-US>[2]</span>中谈到编译器必须提供的最小信息量<span lang=EN-US>)</span>：<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><font face=宋体><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black">class type_info {<br>private:<br>type_info(const type_info&amp;);<br>type_info&amp; operator=( const type_info&amp; );<br>public:<br>virtual ~type_info();<br>int operator==( const type_info&amp; ) const;<br>int operator!=( const type_info&amp; ) const;<br>const char* name() const;<br>};<br><br></span><span style="FONT-SIZE: 9pt; COLOR: black">实现目标：<span lang=EN-US><o:p></o:p></span></span></font></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　实现的方案<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　方案一：利用多态来取得指针或应用的实际类型信息<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　这是一个最简单的方法，也是作者目前所采用的办法。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　实现：<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>enum ClassType{<br>UObjectClass,<br>URectViewClass,<br>UDialogClass,<br></font></span><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>&#8230;&#8230;</font><span lang=EN-US><br><font face=宋体>};<br>class UObject{<br>virtual char* GetClassName() const {<br>return "UObject";<br>};<br>virtual ClassType TypeOfClass(){<br>return UObjectClass;<br>};<br>};<br>class UDialog{<br>virtual char* GetClassName() const {<br>return "UDialog";<br>};<br>virtual ClassType TypeOfClass(){<br>return UDialogClass;<br>};<br>}; <br></font></span><font face=宋体>　　示例：<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><font face=宋体><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black">UObject po=new UObject;<br>UObject pr=new URectView;<br>UObject pd=new UDialog;<br>cout &lt;&lt; "po is a " &lt;&lt; po-&gt;GetClassName() &lt;&lt; endl;<br>cout &lt;&lt; "pr is a " &lt;&lt; pr-&gt;GetClassName() &lt;&lt; endl;<br>cout &lt;&lt; "pd is a " &lt;&lt; pd-&gt;GetClassName() &lt;&lt; endl;<br>cout&lt;TypeOfClass()==UObjectClass&lt; cout&lt;TypeOfClass()==URectViewClass&lt; cout&lt;TypeOfClass()==UDialogClass&lt; cout&lt;TypeOfClass()==UObjectClass&lt; cout&lt;TypeOfClass()==UDialogClass&lt;&nbsp;&nbsp;<br></span><span style="FONT-SIZE: 9pt; COLOR: black">　　输出：<span lang=EN-US><o:p></o:p></span></span></font></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><font face=宋体><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black">po is a UObjectClass<br>pr is a URectViewClass<br>pd is a UDialogClass<br>true<br>true<br>true<br>false<br>false <br></span><span style="FONT-SIZE: 9pt; COLOR: black">　　这种实现方法也就是在基类中提供一个多态的方法，这个方法返回一个类型信息。这样我们能够知道一个指针所指向对象的具体类型，可以满足一些简单的要求。<span lang=EN-US><o:p></o:p></span></span></font></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　但是很显然，这样的方法只实现了<span lang=EN-US>typeid</span>的部分功能，还存在很多缺点：<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　<span lang=EN-US>1</span>、 用户每增加一个类必须覆盖<span lang=EN-US>GetClassName</span>和<span lang=EN-US>TypeOfClass</span>两个方法，如果忘了，会导致程序错误。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　<span lang=EN-US>2</span>、 这里的类名和类标识信息不足以实现<span lang=EN-US>dynamic_cast</span>的功能，从这个意义上而言此方案根本不能称为<span lang=EN-US>RTTI</span>。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　<span lang=EN-US>3</span>、 用户必须手工维护每个类的类名与标识，这限制了以库的方式提供给用户的可能。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　<span lang=EN-US>4</span>、 用户必须手工添加<span lang=EN-US>GetClassName</span>和<span lang=EN-US>TypeOfClass</span>两个方法，使用并不方便。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　其中上面的部分问题我们可以采用<span lang=EN-US>C/C++</span>中的宏技巧<span lang=EN-US>(Macro Magic)</span>来解决，这个可以在我们的最终解决方案的代码中看到。下面采用方案二中将予以解决上述问题。</font><span lang=EN-US><br></span><font face=宋体>　方案二：以一个类型表来存储类型信息<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　这种方法考虑使用一个类结构，除了保留原有的整型类<span lang=EN-US>ID</span>，类名字符串外，增加了一个指向基类<span lang=EN-US>TypeInfo</span>成员的指针。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><font face=宋体><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black">struct TypeInfo<br>{<br>char* className;<br>int type_id;<br>TypeInfo* pBaseClass;<br>operator== (const TypeInfo&amp; info){<br>return this==&amp;info;<br>}<br>operator!= (const TypeInfo&amp; info){<br>return this!=&amp;info;<br>}<br>}; <br></span><span style="FONT-SIZE: 9pt; COLOR: black">　　从这里可以看到，以这种方式实现的<span lang=EN-US>RTTI</span>不支持多重继承。所幸多重继承在程序设计中并非必须，而且也不推荐。下面的代码中，我将为<span lang=EN-US>DP9900</span>软件项目组中类层次结构中的几个类添加<span lang=EN-US>RTTI</span>功能。<span lang=EN-US>DP9900</span>项目中，绝大部分的类都以单继承方式从<span lang=EN-US>UObject</span>这个根类直接或间接继承而来。这样我们就可以从<span lang=EN-US>UObject</span>开始，加入我们<span lang=EN-US>RTTI</span>支持所需要的数据和方法。<span lang=EN-US><o:p></o:p></span></span></font></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>class UObject<br>{<br>public:<br>bool IsKindOf(TypeInfo&amp; cls); //</font></span><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>判别某个对象是否属于某一个类</font><span lang=EN-US><br><font face=宋体>public:<br>virtual int GetTypeID(){return rttiTypeInfo.type_id;}<br>virtual char* GetTypeName(){return rttiTypeInfo.className;}<br>virtual TypeInfo&amp; GetTypeInfo(){return rttiTypeInfo;}<br>static TypeInfo&amp; GetTypeInfoClass(){return rttiTypeInfo;}<br>private:<br>static TypeInfo rttiTypeInfo; <br>};<br>//</font></span><font face=宋体>依次为<span lang=EN-US>className</span>、<span lang=EN-US>type_id</span>、<span lang=EN-US>pBaseClass</span>赋值</font><span lang=EN-US><br><font face=宋体>TypeInfo UObject::rttiTypeInfo={"UObject",0,NULL}; <br></font></span><font face=宋体>　　考虑从<span lang=EN-US>UObject</span>将这个<span lang=EN-US>TypeInfo</span>类作为每一个新增类的静态成员，这样一个类的所有对象将共享<span lang=EN-US>TypeInfo</span>的唯一实例。我们希望能够在程序运行之前就为<span lang=EN-US>type_id,className</span>做好初始化，并让<span lang=EN-US>pBaseClass</span>指向基类的这个<span lang=EN-US>TypeInfo</span>。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　每个类的<span lang=EN-US>TypeInfo</span>成员约定使用<span lang=EN-US>rttiTypeInfo</span>的命名，为了避免命名冲突，我们将其作为<span lang=EN-US>private</span>成员。有了基类的支持并不够，当用户需要<span lang=EN-US>RTTI</span>支持，还需要自己来做一些事情：<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　<span lang=EN-US>1</span>、 派生类需要从<span lang=EN-US>UObject</span>继承。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　<span lang=EN-US>2</span>、 添加<span lang=EN-US>rttiTypeInfo</span>变量。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　<span lang=EN-US>3</span>、 在类外正确初始化<span lang=EN-US>rttiTypeInfo</span>静态成员。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　<span lang=EN-US>4</span>、 覆盖<span lang=EN-US>GetTypeID</span>、<span lang=EN-US>GetTypeName</span>、<span lang=EN-US>GetTypeInfo</span>、<span lang=EN-US>GetTypeInfoClass</span>四个成员函数。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　如下所示：<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><font face=宋体><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black">class UView:public UObject<br>{<br>public:<br>virtual int GetTypeID(){return rttiTypeInfo.type_id;} <br>virtual char* GetTypeName(){return rttiTypeInfo.className;} <br>virtual TypeInfo&amp; GetTypeInfo(){return rttiTypeInfo;} <br>static TypeInfo&amp; GetTypeInfoClass(){return rttiTypeInfo;} <br>private: <br>static TypeInfo rttiTypeInfo; <br>}; <br></span><span style="FONT-SIZE: 9pt; COLOR: black">　　有了前三步，这样我们就可以得到一个不算太复杂的链表――这是一棵类型信息构成的<span lang=EN-US>"</span>树<span lang=EN-US>"</span>，与数据结构中的树的唯一差别就是其指针方向相反。<span lang=EN-US><o:p></o:p></span></span></font></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　这样，从任何一个<span lang=EN-US>UObject</span>的子类，顺着<span lang=EN-US>pBaseClass</span>往上找，总能遍历它的所有父类，最终到达<span lang=EN-US>UObject</span>。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　在这个链表的基础上，要判别某个对象是否属于某一个类就很简单。下面给出<span lang=EN-US>UObject::IsKindOf()</span>的实现。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><font face=宋体><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black">bool UObject::IsKindOf(TypeInfo&amp; cls)<br>{<br>TypeInfo* p=&amp;(this-&gt;GetTypeInfo());<br>while(p!=NULL){<br>if(p-&gt;type_id==cls.type_id)<br>return true;<br>p=p-&gt;pBaseClass;<br>}<br>return false;<br>}<br><br></span><span style="FONT-SIZE: 9pt; COLOR: black">　　有了<span lang=EN-US>IsKindOf</span>的支持，<span lang=EN-US>dynamic_cast</span>的功能也就可以用一个简单的<span lang=EN-US>safe_cast</span>来实现：<span lang=EN-US><o:p></o:p></span></span></font></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><font face=宋体><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black">template <br>inline T* safe_cast(UObject* ptr,TypeInfo&amp; cls)<br>{<br>return (ptr-&gt;IsKindOf(cls)?(T*)ptr:NULL);<br>} <br></span><span style="FONT-SIZE: 9pt; COLOR: black">　　至此，我们已经能够从功能上完成前面的目标了，不过用户要使用这个类库的<span lang=EN-US>RTTI</span>功能还很麻烦，要敲入一大堆对他们毫无意义的函数代码，要在初始化<span lang=EN-US>rttiTypeInfo</span>静态成员时手工设置类<span lang=EN-US>ID</span>与类名。其实这些麻烦完全不必交给我们的用户，适当采用一些宏技巧<span lang=EN-US>(Macro Magic)</span>，就可以让<span lang=EN-US>C++</span>的预处理器来替我们写很多枯燥的代码。关于宏不是本文的重点，你可以从最终代码清单看到它们。下面再谈谈关于类<span lang=EN-US>ID</span>的问题。<span lang=EN-US><o:p></o:p></span></span></font></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　类<span lang=EN-US>ID<o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　为了使不同类型的对象可区分，用一个给每个<span lang=EN-US>TypeInfo</span>对象一个类<span lang=EN-US>ID</span>来作为比较的依据是必要的。</font><span lang=EN-US><br></span><font face=宋体>其实对于我们这里的需求和实现方法而言，其实类<span lang=EN-US>ID</span>并不是必须的。每一个支持<span lang=EN-US>RTTI</span>的类都包含了一个静态<span lang=EN-US>TypeInfo</span>对象，这个对象的地址就是在进程中全局唯一。但考虑到其他一些技术如：动态对象创建、对象序列化等，它们可能会要求<span lang=EN-US>RTTI</span>给出一个静态不变的<span lang=EN-US>ID</span>。在本文的实现中，对此作了有益的尝试。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　首先声明一个用来产生递增类<span lang=EN-US>ID</span>的全局变量。再声明如下一个结构，没有数据成员，只有一个构造函数用于初始化<span lang=EN-US>TypeInfo</span>的类<span lang=EN-US>ID</span>：<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><font face=宋体><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black">extern int TypeInfoOrder=0;<br>struct InitTypeInfo<br>{<br>InitTypeInfo(TypeInfo* info)<br>{<br>info-&gt;type_id=TypeInfoOrder++;<br>}<br>}; <br></span><span style="FONT-SIZE: 9pt; COLOR: black">　　为<span lang=EN-US>UObject</span>添加一个<span lang=EN-US>private</span>的静态成员及其初始化：<span lang=EN-US><o:p></o:p></span></span></font></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>class UObject<br>{<br>//</font></span><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>&#8230;&#8230;</font><span lang=EN-US><br><font face=宋体>private:<br>static InitTypeInfo initClassInfo;<br>};<br>InitTypeInfo UObject::initClassInfo(&amp;(UObject::rttiTypeInfo)); <br></font></span><font face=宋体>　　并且对每一个从<span lang=EN-US>UObject</span>派生的子类也进行同样的添加。这样您将看到，在<span lang=EN-US>C++</span>主函数执行前，启动代码将替我们调用每一个类的<span lang=EN-US>initClassInfo</span>成员的构造函数<span lang=EN-US>InitTypeInf:InitTypeInfo(TypeInfo* info)</span>，而正是这个函数替我们产生并设置了类<span lang=EN-US>ID</span>。<span lang=EN-US>InitTypeInfo</span>的构造函数还可以替我们做其他一些有用的初始化工作，比如将所有的<span lang=EN-US>TypeInfo</span>信息登录到一个表格里，让我们可以很方便的遍历它。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　但实践与查阅资料让我们发现，由于<span lang=EN-US>C++</span>中对静态成员初始化的顺序没有明确的规定，所以这样的方式产生出来的类<span lang=EN-US>ID</span>并非完全静态，换一个编译器编译执行产生的结果可能完全不同。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　还有一个可以考虑的方案是采用某种无冲突<span lang=EN-US>HASH</span>算法，将类名转换成为一个唯一整数。使用标准<span lang=EN-US>CRC32</span>算法从类型名计算出一个整数作为类<span lang=EN-US>ID</span>也许是个不错的想法<span lang=EN-US>[3]</span>。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　程序清单<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>// URtti.h <br>#ifndef __URTTI_H__<br>#define __URTTI_H__<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>class UObject;<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>struct TypeInfo<br>{<br>char* className;<br>int type_id;<br>TypeInfo* pBaseClass;<br>operator== (const TypeInfo&amp; info){<br>return this==&amp;info;<br>}<br>operator!= (const TypeInfo&amp; info){<br>return this!=&amp;info;<br>}<br>};<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>inline std::ostream&amp; operator&lt;&lt; (std::ostream&amp; os,TypeInfo&amp; info)<br>{<br>return (os&lt;&lt; "[" &lt;&lt; &amp;info &lt;&lt; "]" &lt;&lt; "\t"<br>&lt;&lt; info.type_id &lt;&lt; ":"<br>&lt;&lt; info.className &lt;&lt; ":"<br>&lt;&lt; info.pBaseClass &lt;&lt; std::endl);<br>}<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>extern int TypeInfoOrder;<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>struct InitTypeInfo<br>{<br>InitTypeInfo(/*TypeInfo* base,*/TypeInfo* info)<br>{<br>info-&gt;type_id=TypeInfoOrder++;<br>}<br>};<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>#define TYPEINFO_OF_CLASS(class_name) (class_name::GetTypeInfoClass())<br>#define TYPEINFO_OF_OBJ(obj_name) (obj_name.GetTypeInfo())<br>#define TYPEINFO_OF_PTR(ptr_name) (ptr_name-&gt;GetTypeInfo())<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>#define DECLARE_TYPEINFO(class_name) public: virtual int GetTypeID(){return TYPEINFO_MEMBER(class_name).type_id;} virtual char* GetTypeName(){return TYPEINFO_MEMBER(class_name).className;} virtual TypeInfo&amp; GetTypeInfo(){return TYPEINFO_MEMBER(class_name);} static TypeInfo&amp; GetTypeInfoClass(){return TYPEINFO_MEMBER(class_name);} private: static TypeInfo TYPEINFO_MEMBER(class_name); static InitTypeInfo initClassInfo; <br>#define IMPLEMENT_TYPEINFO(class_name,base_name) TypeInfo class_name::TYPEINFO_MEMBER(class_name)= {#class_name,0,&amp;(base_name::GetTypeInfoClass())}; InitTypeInfo class_name::initClassInfo(&amp;(class_name::TYPEINFO_MEMBER(class_name)));<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>#define DYNAMIC_CAST(object_ptr,class_name) safe_cast(object_ptr,TYPEINFO_OF_CLASS(class_name))<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>#define TYPEINFO_MEMBER(class_name) rttiTypeInfo<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>class UObject<br>{<br>public:<br>bool IsKindOf(TypeInfo&amp; cls);<br>public:<br>virtual int GetTypeID(){return TYPEINFO_MEMBER(UObject).type_id;}<br>virtual char* GetTypeName(){return TYPEINFO_MEMBER(UObject).className;}<br>virtual TypeInfo&amp; GetTypeInfo(){return TYPEINFO_MEMBER(UObject);}<br>static TypeInfo&amp; GetTypeInfoClass(){return TYPEINFO_MEMBER(UObject);}<br>private:<br>static TypeInfo TYPEINFO_MEMBER(UObject);<br>static InitTypeInfo initClassInfo;<br>};<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>template <br>inline T* safe_cast(UObject* ptr,TypeInfo&amp; cls)<br>{<br>return (ptr-&gt;IsKindOf(cls)?(T*)ptr:NULL);<br>}<br>#endif<br>// URtti.cpp <br>＃i nclude "urtti.h"<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>extern int TypeInfoOrder=0;<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>TypeInfo UObject::TYPEINFO_MEMBER(UObject)={"UObject",0,NULL};<br>InitTypeInfo UObject::initClassInfo(&amp;(UObject::TYPEINFO_MEMBER(UObject)));<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>bool UObject::IsKindOf(TypeInfo&amp; cls)<br>{<br>TypeInfo* p=&amp;(this-&gt;GetTypeInfo());<br>while(p!=NULL){<br>if(p-&gt;type_id==cls.type_id)<br>return true;<br>p=p-&gt;pBaseClass;<br>}<br>return false;<br>}<br>// mail.cpp <br>＃i nclude <br>＃i nclude "urtti.h"<br>using namespace std;<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>class UView:public UObject<br>{<br>DECLARE_TYPEINFO(UView)<br>};<br>IMPLEMENT_TYPEINFO(UView,UObject)<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>class UGraph:public UObject<br>{<br>DECLARE_TYPEINFO(UGraph)<br>};<br>IMPLEMENT_TYPEINFO(UGraph,UObject)<o:p></o:p></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><font face=宋体><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black">void main()<br>{<br>UObject* po=new UObject;<br>UView* pv=new UView;<br>UObject* pg=new UGraph;<br>if(DYNAMIC_CAST(po,UView)) <br>cout &lt;&lt; "po =&gt; UView succeed" &lt;&lt; std::endl;<br>else<br>cout &lt;&lt; "po =&gt; UView failed" &lt;&lt; std::endl;<br>if(DYNAMIC_CAST(pv,UView))<br>cout &lt;&lt; "pv =&gt; UView succeed" &lt;&lt; std::endl;<br>else<br>cout &lt;&lt; "pv =&gt; UView failed" &lt;&lt; std::endl;<br>if(DYNAMIC_CAST(po,UGraph)) <br>cout &lt;&lt; "po =&gt; UGraph succeed" &lt;&lt; std::endl;<br>else<br>cout &lt;&lt; "po =&gt; UGraph failed" &lt;&lt; std::endl;<br>if(DYNAMIC_CAST(pg,UGraph))<br>cout &lt;&lt; "pg =&gt; UGraph succeed" &lt;&lt; std::endl;<br>else<br>cout &lt;&lt; "pg =&gt; UGraph failed" &lt;&lt; std::endl;<br>} <br></span><span style="FONT-SIZE: 9pt; COLOR: black">　　实现结果<span lang=EN-US><o:p></o:p></span></span></font></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　本文实现了如下几个宏来支持<span lang=EN-US>RTTI</span>，它们的使用方法都可以在上面的代码中找到：</font><span lang=EN-US><br></span><font face=宋体>　　<span lang=EN-US> <o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>宏函数 功能及参数说明</font><font face=宋体><span lang=EN-US> <br>DECLARE_TYPEINFO(class_name)&nbsp;&nbsp;</span>为类添加<span lang=EN-US>RTTI</span>功能放在类声明的起始位置</font><font face=宋体><span lang=EN-US> <br>IMPLEMENT_TYPEINFO(class_name,base) </span>同上，放在类定义任何位置</font><font face=宋体><span lang=EN-US> <br>TYPEINFO_OF_CLASS(class_name) </span>相当于<span lang=EN-US>typeid(</span>类名</font><font face=宋体><span lang=EN-US>) <br>TYPEINFO_OF_OBJ(obj_name) </span>相当于<span lang=EN-US>typeid(</span>对象</font><font face=宋体><span lang=EN-US>) <br>TYPEINFO_OF_PTR(ptr_name) </span>相当于<span lang=EN-US>typeid(</span>指针</font><font face=宋体><span lang=EN-US>) <br>DYNAMIC_CAST(object_ptr,class_name) </span>相当于</font><font face=宋体><span lang=EN-US>dynamic_castobject_ptr <br></span>性能测试<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　测试代码：<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　这里使用相同次数的<span lang=EN-US>DYNAMIC_CAST</span>和<span lang=EN-US>dynamic_cast</span>进行对比测试，在<span lang=EN-US>VC6.0</span>下编译运行，使用默认的<span lang=EN-US>Release</span>编译配置选项。为了避免编译器优化导致的不公平测试结果，我在循环中加入了无意义的计数操作。<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>void main()<br>{<br>UObject* po=new UObject;<br>UView* pv=new UView;<br>UObject* pg=new UGraph;<br>int a,b,c,d;<br>a=b=c=d=0;<br>const int times=30000000;<br>cerr &lt;&lt; "</font></span><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>时间测试输出：</font><font face=宋体><span lang=EN-US>" &lt;&lt; endl;<br>cerr &lt;&lt; "start my DYNAMIC_CAST at: " &lt;&lt; time(NULL) &lt;&lt; endl;<br>for(int i=0;i if(DYNAMIC_CAST(po,UView)) a++; else a--;<br>if(DYNAMIC_CAST(pv,UView)) b++; else b--;<br>if(DYNAMIC_CAST(po,UGraph)) c++; else c--;<br>if(DYNAMIC_CAST(pg,UGraph)) d++; else d--;<br>}<br>cerr &lt;&lt; "end my DYNAMIC_CAST at: " &lt;&lt; time(NULL) &lt;&lt; endl;<br>cerr &lt;&lt; "start c++ dynamic_cast at: " &lt;&lt; time(NULL) &lt;&lt; endl;<br>for(i=0;i if(dynamic_cast(po)) a++; else a--;<br>if(dynamic_cast(pv)) b++; else b--;<br>if(dynamic_cast(po)) c++; else c--;<br>if(dynamic_cast(pg)) d++; else d--;<br>}<br>cerr &lt;&lt; "end c++ dynamic_cast at: " &lt;&lt; time(NULL) &lt;&lt; endl;<br>cerr &lt;&lt; a &lt;&lt; b &lt;&lt; c &lt;&lt; d &lt;&lt; endl;<br>} <br></span>　　运行结果：<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><font face=宋体><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black">start my DYNAMIC_CAST at: 1021512140<br>end my DYNAMIC_CAST at: 1021512145<br>start c++ dynamic_cast at: 1021512145<br>end c++ dynamic_cast at: 1021512160 <br></span><span style="FONT-SIZE: 9pt; COLOR: black">　　这是上述条件下的测试输出，我们可以看到，本文实现的这个精简<span lang=EN-US>RTTI</span>方案运行<span lang=EN-US>DYNAMIC_CAST</span>的时间开销只有<span lang=EN-US>dynamic_cast</span>的<span lang=EN-US>1/3</span>。为了得到更全面的数据，还进行了<span lang=EN-US>DEBUG</span>编译配置选项下的测试。<span lang=EN-US><o:p></o:p></span></span></font></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　输出：<span lang=EN-US><o:p></o:p></span></font></span></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><font face=宋体><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black">start my DYNAMIC_CAST at: 1021512041<br>end my DYNAMIC_CAST at: 1021512044<br>start c++ dynamic_cast at: 1021512044<br>end c++ dynamic_cast at: 1021512059 <br></span><span style="FONT-SIZE: 9pt; COLOR: black">　　这种情况下<span lang=EN-US>DYNAMIC_CAST</span>运行速度要比<span lang=EN-US>dynamic_cast</span>慢一倍左右。如果在<span lang=EN-US>Release</span>编译配置选项下将<span lang=EN-US>UObject::IsKindOf</span>方法改成如下<span lang=EN-US>inline</span>函数，我们将得到更让人兴奋的结果（<span lang=EN-US>DYNAMIC_CAST</span>运行时间只有<span lang=EN-US>dynamic_cast</span>的<span lang=EN-US>1/5</span>）。<span lang=EN-US><o:p></o:p></span></span></font></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><font face=宋体><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black">inline bool UObject::IsKindOf(TypeInfo&amp; cls)<br>{<br>for(TypeInfo* p=&amp;(this-&gt;GetTypeInfo());p!=NULL;p=p-&gt;pBaseClass)<br>if(p==&amp;cls) return true;<br>return false;<br>} <br></span><span style="FONT-SIZE: 9pt; COLOR: black">　　输出：<span lang=EN-US><o:p></o:p></span></span></font></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><font face=宋体><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black">start my DYNAMIC_CAST at: 1021512041<br>end my DYNAMIC_CAST at: 1021512044<br>start c++ dynamic_cast at: 1021512044<br>end c++ dynamic_cast at: 1021512059 <br></span><span style="FONT-SIZE: 9pt; COLOR: black">　　结论：<span lang=EN-US><o:p></o:p></span></span></font></p>
<p style="WORD-BREAK: break-all; LINE-HEIGHT: 12pt"><span style="FONT-SIZE: 9pt; COLOR: black"><font face=宋体>　　由本文的实践可以得出结论，自己动手编码实现<span lang=EN-US>RTTI</span>是简单可行的。这样的实现可以在编译器优秀的代码优化中表现出比<span lang=EN-US>dynamic_cast</span>更好的性能，而且没有带来过多的存储开销。本文的<span lang=EN-US>RTTI</span>以性能为主要设计目标，在实现上一定程度上受到了<span lang=EN-US>MFC</span>的影响。适于嵌入式环境。</font></span></p>
<img src ="http://www.cppblog.com/deane/aggbug/49209.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2008-05-08 14:01 <a href="http://www.cppblog.com/deane/articles/49209.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>