﻿<?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++博客-mysileng-随笔分类-c++</title><link>http://www.cppblog.com/mysileng/category/20052.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 16 Jan 2015 15:44:01 GMT</lastBuildDate><pubDate>Fri, 16 Jan 2015 15:44:01 GMT</pubDate><ttl>60</ttl><item><title>C/C++ Volatile关键词深度剖析</title><link>http://www.cppblog.com/mysileng/archive/2015/01/13/209505.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Tue, 13 Jan 2015 12:35:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2015/01/13/209505.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/209505.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2015/01/13/209505.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/209505.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/209505.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1&nbsp;&nbsp;&nbsp;&nbsp;背景&nbsp;&nbsp;&nbsp;&nbsp;12&nbsp;&nbsp;&nbsp;&nbsp;Volatile：易变的&nbsp;&nbsp;&nbsp;&nbsp;12.1&nbsp;&nbsp;&nbsp;&nbsp;小结&nbsp;&nbsp;&nbsp;&nbsp;23&nbsp;&nbsp;&nbsp;&nbsp;Volatil...&nbsp;&nbsp;<a href='http://www.cppblog.com/mysileng/archive/2015/01/13/209505.html'>阅读全文</a><img src ="http://www.cppblog.com/mysileng/aggbug/209505.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2015-01-13 20:35 <a href="http://www.cppblog.com/mysileng/archive/2015/01/13/209505.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>函数调用约定：_stdcall，_cdecl，_fastcall  (转)</title><link>http://www.cppblog.com/mysileng/archive/2012/12/27/196703.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Thu, 27 Dec 2012 03:41:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/12/27/196703.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/196703.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/12/27/196703.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/196703.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/196703.html</trackback:ping><description><![CDATA[<div>(1) _stdcall调用</div><div>　　_stdcall 是Standard Call的缩写，是C++的标准调用方式，也是Pascal程序的缺省调用方式，参数采用<span style="color: red;">从右到左</span>的压栈方式，如果是调用类成员的话，最后一个入栈的是this指针。这些堆栈中的参数由<span style="color: red;">被调函数自身</span>在返回前清空，</div><div></div><div>使用的指令是 retn X，X表示参数占用的字节数，CPU在ret之后自动弹出X个字节的堆栈空间。称为自动清栈。</div><div>　　WIN32 Api都采用_stdcall调用方式，这样的宏定义说明了问题：</div><div>　　#define WINAPI _stdcall</div><div>　　按C编译方式，_stdcall调用约定在输出函数名前面加下划线，后面加&#8220;@&#8221;符号和参数的字节数，形如_functionname@number。</div><div>(2) _cdecl调用</div><div>　　_cdecl是C Declaration[声明]的缩写，表示C/C++语言默认的函数调用方法，也是C/C++的缺省调用方式，所有参数<span style="color: red;">从右到左</span>依次入栈，这些参数由<span style="color: red;">调用者清除</span>，称为手动清栈。_cedcl约定的函数只能被C/C++调用，每一个调用它的</div><div></div><div>函数都包含清空堆栈的代码，所以产生的可执行文件大小会比调用_stdcall函数的大。被调用函数无需要求调用者传递多少参数，调用者传递过多或者过少的参数，甚至完全不同的参数都不会产生编译阶段的错误。</div><div></div><div>　　由于_cdecl调用方式的参数内存栈由调用者维护，所以变长参数的函数能（也只能）使用这种调用约定。</div><div></div><div>　　由于Visual C++默认采用_cdecl 调用方式，所以VC中中调用DLL时，用户应使用_stdcall调用约定。</div><div>　　按C编译方式，_cdecl调用约定仅在输出函数名前面加下划线，形如_functionname。</div><div></div><div>(3) _fastcall调用</div><div>　　_fastcall 是编译器指定的快速调用方式。由于大多数的函数参数个数很少，使用堆栈传递比较费时。因此_fastcall通常规定将前两个（或若干个）参数由寄存器传递，其余参数还是通过堆栈传递。不同编译器编译的程序规定的寄</div><div></div><div>存器不同。返回方式和_stdcall相当。</div><div>　　_fastcall调用较快，它通过CPU内部寄存器传递参数。</div><div>&nbsp;</div><div>(4)PASCAL调用</div><div>PASCAL 是Pascal语言的函数调用方式，也可以在C/C++中使用，参数压栈顺序与_cdecl和_stdcall相反。</div><div>&nbsp;</div><div>归纳如下:</div><div>调用约定 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;入栈参数清理 &nbsp; &nbsp; &nbsp; &nbsp; 参数入栈顺序</div><div>----------- &nbsp; &nbsp; &nbsp; &nbsp;-------------- &nbsp; &nbsp; &nbsp; &nbsp; ----------------</div><div>cdecl &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 调用者处理 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;右-&gt;左</div><div>stdcall &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 函数自己处理 &nbsp; &nbsp; &nbsp; &nbsp;右-&gt;左</div><div>fastcall &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;函数自己处理 &nbsp; &nbsp; &nbsp; &nbsp;依赖于编译器</div><div>pascal &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 函数自己处理 &nbsp; &nbsp; &nbsp; &nbsp;左-&gt;右</div><div></div><img src ="http://www.cppblog.com/mysileng/aggbug/196703.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-12-27 11:41 <a href="http://www.cppblog.com/mysileng/archive/2012/12/27/196703.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>左值和右值（转）</title><link>http://www.cppblog.com/mysileng/archive/2012/12/16/196350.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Sun, 16 Dec 2012 07:08:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/12/16/196350.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/196350.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/12/16/196350.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/196350.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/196350.html</trackback:ping><description><![CDATA[<h1></h1><h1><span style="font-family: 宋体; ">左值右值</span></h1><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">左值（</span><span style="font-size: 12pt; ">lvalue</span><span style="font-size: 12pt; font-family: 宋体; ">）和右值（</span><span style="font-size: 12pt; ">rvalue</span><span style="font-size: 12pt; font-family: 宋体; ">）是编程中两个非常基本的概念，但是也非常容易让人误解，看了很多文章，自我感觉真正将这个问题讲的很透彻的文章还没有看见，所以自告奋勇来尝试一下。如果左值右值的概念不是非常清楚的话，它们迟早会像拦路虎一样跳出来，让你烦心不已，就像玩电脑游戏的时候每隔一段时间总有那么几个地雷考验你的耐性，如果一次把所有地雷扫尽就好了。：）</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">左值（</span><span style="font-size: 12pt; ">lvalue</span><span style="font-size: 12pt; font-family: 宋体; ">）和右值（</span><span style="font-size: 12pt; ">rvalue</span><span style="font-size: 12pt; font-family: 宋体; ">）最先来源于编译理论</span><span style="font-size: 12pt; font-family: 宋体; ">（感谢南大小百合的programs）。在</span><span style="font-size: 12pt; ">C</span><span style="font-size: 12pt; font-family: 宋体; ">语言中表示位于赋值运算符两侧的两个值，左边的就叫左值，右边的就叫右值。比如：</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; ">int ii = 5;//ii</span><span style="font-size: 12pt; font-family: 宋体; ">是左值，</span><span style="font-size: 12pt; ">5</span><span style="font-size: 12pt; font-family: 宋体; ">是右值</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; ">int jj = ii;//jj</span><span style="font-size: 12pt; font-family: 宋体; ">是左值，</span><span style="font-size: 12pt; ">ii</span><span style="font-size: 12pt; font-family: 宋体; ">是右值</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">上面表明，左值肯定可以作为右值使用，但反之则不然。左值和右值的最早区别就在于能否改变。左值是可以变的，右值不能变。【注</span><span style="font-size: 12pt; ">1</span><span style="font-size: 12pt; font-family: 宋体; ">】</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: blue; font-family: 宋体; ">注</span><span style="font-size: 12pt; color: blue; ">1</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">：这一点在</span><span style="font-size: 12pt; color: blue; ">C++</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">中已经</span><span style="font-size: 12pt; color: blue; "><a href="http://kscc.3nopage.com/documents/20011209.htm" target="_blank" style="color: #336699; text-decoration: none; "><span style="color: blue; font-family: 宋体; ">猪羊变色</span></a></span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">，不再成立。拱猪游戏还是挺好玩的，我还真抓过好几次全红心，不过真的好险。：）</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">在很多文章中提到，在</span><span style="font-size: 12pt; ">C++</span><span style="font-size: 12pt; font-family: 宋体; ">中，左值更多的指的是可以定位，即有地址的值，而右值没有地址。【注</span><span style="font-size: 12pt; ">2</span><span style="font-size: 12pt; font-family: 宋体; ">】</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: blue; font-family: 宋体; ">注</span><span style="font-size: 12pt; color: blue; ">2</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">：这一点仍然不准确，我在程序中生成一个临时右值</span><span style="font-size: 12pt; color: blue; ">std::vector()</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">，你能够说它没有地址吗？难道它是没有肉体的鬼魂或幽灵？它是有地址的，而且它也是绝对的右值。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">在现代</span><span style="font-size: 12pt; ">C++</span><span style="font-size: 12pt; font-family: 宋体; ">中，现在左值和右值基本上已经失去它们原本所具有的意义，对于左值表达式，通过具体名字和引用（</span><span style="font-size: 12pt; ">pointer or reference</span><span style="font-size: 12pt; font-family: 宋体; ">）来指定一个对象。非左值就是右值。我来下一个定义：</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: blue; font-family: 宋体; ">左值表示程序中必须有一个特定的名字引用到这个值。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: blue; font-family: 宋体; ">右值表示程序中没有一个特定的名字引用到这个值。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: blue; font-family: 宋体; ">跟它们是否可以改变，是否在栈或堆（</span><span style="font-size: 12pt; color: blue; ">stack or heap</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">）中有地址毫无关系。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 32.15pt; "><strong><span style="font-size: 16pt; color: #993300; ">1</span></strong><strong><span style="font-size: 16pt; color: #993300; font-family: 宋体; ">．左值</span></strong><strong></strong></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">在下面的代码中：</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">int ii = 5;</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">int const jj = ii;</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">int a[5];</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">a[0] = 100;</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">*(a+3) = 200;</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">int const&amp; max( int const&amp; a, int const&amp; b ) //call by reference</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">{</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return a &gt; b ? a : b;</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">}</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">int&amp; fun(int&amp; a) //call by reference</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">{</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a += 5;</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">&nbsp;&nbsp;&nbsp;return a;</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">}</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; ">ii</span><span style="font-size: 12pt; font-family: 宋体; ">，</span><span style="font-size: 12pt; ">jj</span><span style="font-size: 12pt; font-family: 宋体; ">，</span><span style="font-size: 12pt; ">a[0]</span><span style="font-size: 12pt; font-family: 宋体; ">，</span><span style="font-size: 12pt; ">*(a+3)</span><span style="font-size: 12pt; font-family: 宋体; ">，还有函数</span><span style="font-size: 12pt; ">max</span><span style="font-size: 12pt; font-family: 宋体; ">的返回值比如</span><span style="font-size: 12pt; ">max(ii, jj)</span><span style="font-size: 12pt; font-family: 宋体; ">，【注</span><span style="font-size: 12pt; ">3</span><span style="font-size: 12pt; font-family: 宋体; ">】函数</span><span style="font-size: 12pt; ">fun</span><span style="font-size: 12pt; font-family: 宋体; ">的返回值</span><span style="font-size: 12pt; ">fun(ii)</span><span style="font-size: 12pt; font-family: 宋体; ">都是左值。，它们都是有特定的引用名字的值。</span><span style="font-size: 12pt; ">ii</span><span style="font-size: 12pt; font-family: 宋体; ">，</span><span style="font-size: 12pt; ">jj</span><span style="font-size: 12pt; font-family: 宋体; ">，</span><span style="font-size: 12pt; ">a[0]</span><span style="font-size: 12pt; font-family: 宋体; ">，</span><span style="font-size: 12pt; ">*(a+3)</span><span style="font-size: 12pt; font-family: 宋体; ">，</span><span style="font-size: 12pt; ">max(ii, jj)</span><span style="font-size: 12pt; font-family: 宋体; ">，</span><span style="font-size: 12pt; ">fun(ii)</span><span style="font-size: 12pt; font-family: 宋体; ">分别就是它们的名字。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: blue; font-family: 宋体; ">注</span><span style="font-size: 12pt; color: blue; ">3</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">：在这里有一个不太容易分清楚的盲点。那就是有人会问</span><span style="font-size: 12pt; color: blue; ">max(8, 9)</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">到达是左值还是右值，</span><span style="font-size: 12pt; color: blue; ">C++</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">标准规定常量引用（</span><span style="font-size: 12pt; color: blue; ">reference to const</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">）可以引用到右值，所以</span><span style="font-size: 12pt; color: blue; ">max(8, 9)</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">似乎应该是右值，不过不管它是左值，还是右值，我们都不能试图去改变它。为了与前面的概念一致，我认为它是左值，不可改变的常量左值。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">左值有不能改变的，即被</span><span style="font-size: 12pt; ">const</span><span style="font-size: 12pt; font-family: 宋体; ">所修饰的左值，比如上面的</span><span style="font-size: 12pt; ">jj</span><span style="font-size: 12pt; font-family: 宋体; ">，</span><span style="font-size: 12pt; ">max(ii, jj)</span><span style="font-size: 12pt; font-family: 宋体; ">都是被常量（</span><span style="font-size: 12pt; ">const</span><span style="font-size: 12pt; font-family: 宋体; ">）魔咒所困住的左值。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">没有被</span><span style="font-size: 12pt; ">const</span><span style="font-size: 12pt; font-family: 宋体; ">困住的左值当然是可以改变的，比如下面的代码都是成立的：</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; ">ii = 600;</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; ">a[0] = 700;</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: blue; ">fun(ii) = 800; //OK!</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: red; font-family: 宋体; ">我们的眼睛没有问题，</span><span style="font-size: 12pt; color: red; ">fun(ii) = 800;</span><span style="font-size: 12pt; color: red; font-family: 宋体; ">完全正确，因为它是可以改变的左值。</span><span style="font-size: 12pt; font-family: 宋体; ">所以我们看</span><span style="font-size: 12pt; ">STL</span><span style="font-size: 12pt; font-family: 宋体; ">的源码，就会理解</span><span style="font-size: 12pt; ">std::vector</span><span style="font-size: 12pt; font-family: 宋体; ">中的重载</span><span style="font-size: 12pt; ">operator[]</span><span style="font-size: 12pt; font-family: 宋体; ">运算符的返回值为什么要写成引用，因为</span><span style="font-size: 12pt; ">operator[]</span><span style="font-size: 12pt; font-family: 宋体; ">必须返回左值。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; ">&nbsp;</p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 32.15pt; "><strong><span style="font-size: 16pt; color: #993300; ">2</span></strong><strong><span style="font-size: 16pt; color: #993300; font-family: 宋体; ">．右值</span></strong></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">没有特定名字的值是右值。先看下面的代码：</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">std::list();</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">std::string(&#8220;It is a rvalue!&#8221;);</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">int fun1() //call by value</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">{</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-size: 12pt; color: #993300; font-family: 宋体; ">&#8230;</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">}</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">int* fun2() //call by reference</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">{</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-size: 12pt; color: #993300; font-family: 宋体; ">&#8230;</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">}</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">其中</span><span style="font-size: 12pt; ">std::list()</span><span style="font-size: 12pt; font-family: 宋体; ">，</span><span style="font-size: 12pt; ">std::string(&#8220;It is a rvalue!&#8221;)</span><span style="font-size: 12pt; font-family: 宋体; ">，函数</span><span style="font-size: 12pt; ">fun1</span><span style="font-size: 12pt; font-family: 宋体; ">的返回值</span><span style="font-size: 12pt; ">fun1()</span><span style="font-size: 12pt; font-family: 宋体; ">，函数</span><span style="font-size: 12pt; ">fun2</span><span style="font-size: 12pt; font-family: 宋体; ">的返回值</span><span style="font-size: 12pt; ">fun2()</span><span style="font-size: 12pt; font-family: 宋体; ">都是右值，它们的值都没有特定的名字去引用。也许有人会奇怪，</span><span style="font-size: 12pt; ">fun2()</span><span style="font-size: 12pt; font-family: 宋体; ">也是右值？最前面的</span><span style="font-size: 12pt; ">max(a,b)</span><span style="font-size: 12pt; font-family: 宋体; ">不是左值吗？</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">请看清楚，函数</span><span style="font-size: 12pt; ">fun2</span><span style="font-size: 12pt; font-family: 宋体; ">的返回值是</span><span style="font-size: 12pt; ">pointer</span><span style="font-size: 12pt; font-family: 宋体; ">，</span><span style="font-size: 12pt; ">pointer</span><span style="font-size: 12pt; font-family: 宋体; ">也是</span><span style="font-size: 12pt; ">call by value</span><span style="font-size: 12pt; font-family: 宋体; ">，而函数</span><span style="font-size: 12pt; ">max</span><span style="font-size: 12pt; font-family: 宋体; ">的返回值是</span><span style="font-size: 12pt; ">reference</span><span style="font-size: 12pt; font-family: 宋体; ">，</span><span style="font-size: 12pt; ">reference</span><span style="font-size: 12pt; font-family: 宋体; ">是</span><span style="font-size: 12pt; ">call by reference</span><span style="font-size: 12pt; font-family: 宋体; ">。所以说</span><span style="font-size: 12pt; ">C++</span><span style="font-size: 12pt; font-family: 宋体; ">中引入</span><span style="font-size: 12pt; ">reference</span><span style="font-size: 12pt; font-family: 宋体; ">不仅仅是为了方便，它也是一种必须。【注</span><span style="font-size: 12pt; ">4</span><span style="font-size: 12pt; font-family: 宋体; ">】</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: blue; font-family: 宋体; ">注</span><span style="font-size: 12pt; color: blue; ">4</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">：</span><span style="font-size: 12pt; color: blue; ">Scott Meyer</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">写的《</span><span style="font-size: 12pt; color: blue; ">More Effective C++</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">》的条款</span><span style="font-size: 12pt; color: blue; ">1</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">专门讲了</span><span style="font-size: 12pt; color: blue; ">pointer</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">和</span><span style="font-size: 12pt; color: blue; ">reference</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">的区别，写的很好，辨别的非常清楚。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: blue; ">fun2()</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">是右值，但</span><span style="font-size: 12pt; color: blue; ">&nbsp;*fun2()</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">却是左值，就跟经常看到的</span><span style="font-size: 12pt; color: blue; ">*p</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">一样，所以看</span><span style="font-size: 12pt; color: blue; ">C++</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">库代码的时候，会发现重载</span><span style="font-size: 12pt; color: blue; ">operator*</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">的函数返回值是</span><span style="font-size: 12pt; color: blue; ">reference</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">当然我还遗漏了一种右值，那就是字面上的（</span><span style="font-size: 12pt; ">literal</span><span style="font-size: 12pt; font-family: 宋体; ">）值，比如</span><span style="font-size: 12pt; ">5</span><span style="font-size: 12pt; font-family: 宋体; ">，</span><span style="font-size: 12pt; ">8.23</span><span style="font-size: 12pt; font-family: 宋体; ">，</span><span style="font-size: 12pt; ">&#8217;a&#8217;</span><span style="font-size: 12pt; font-family: 宋体; ">等等理所当然的都是右值。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">右值最初出现的时候，一个最大的特征就是不可改变。但就跟我们的道德标准一样，时代不同了，标准也变化了，以前的三纲五常早已经被扔到历史的垃圾堆里面了。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; ">C++</span><span style="font-size: 12pt; font-family: 宋体; ">中有可以改变的右值，而且这个特性还非常有用。那就是用户自定义的类（</span><span style="font-size: 12pt; ">class</span><span style="font-size: 12pt; font-family: 宋体; ">）的构造函数生成的临时对象。比如：</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; ">std::vector(9)</span><span style="font-size: 12pt; font-family: 宋体; ">，</span><span style="font-size: 12pt; ">std::deque()</span><span style="font-size: 12pt; font-family: 宋体; ">，&#8230;&#8230;都是可以改变的右值。在</span><span style="font-size: 12pt; ">Herb Sutter</span><span style="font-size: 12pt; font-family: 宋体; ">的《</span><span style="font-size: 12pt; ">More Exceptional C++</span><span style="font-size: 12pt; font-family: 宋体; ">》中的条款</span><span style="font-size: 12pt; ">7</span><span style="font-size: 12pt; font-family: 宋体; ">的</span><span style="font-size: 12pt; ">page51</span><span style="font-size: 12pt; font-family: 宋体; ">页有这样几行代码：</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">// Example 7-2(b): The right way to shrink-to-fit a vector.</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">vector&lt;Customer&gt; c( 10000 );</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">// ...now c.capacity() &gt;= 10000...</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">// erase all but the first 10 elements</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">c.erase( c.begin()+10, c.end() );</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">// the following line does shrink c's</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">// internal buffer to fit (or close)</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">vector&lt;Customer&gt;( c ).swap( c );</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">// ...now c.capacity() == c.size(), or</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: #993300; ">// perhaps a little more than c.size()</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">认真看几遍，你会发现但</span><span style="font-size: 12pt; ">vector</span><span style="font-size: 12pt; font-family: 宋体; ">的大小增大到一定程度，你又用不着这么多空间的时候，你会想办法把它收缩到最合适的大小，但利用别的办法比如调用成员函数</span><span style="font-size: 12pt; ">reserve()</span><span style="font-size: 12pt; font-family: 宋体; ">都无法办到，这个时候就必须利用右值可以改变这个性质了。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: blue; ">vector&lt;Customer&gt;( c ).swap( c );</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">这行代码就是点睛之处。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">首先使用复制构造函数生成临时右值</span><span style="font-size: 12pt; ">vector&lt;Customer&gt;( c )</span><span style="font-size: 12pt; font-family: 宋体; ">，这个右值正好是合适大小，然后和</span><span style="font-size: 12pt; ">c</span><span style="font-size: 12pt; font-family: 宋体; ">交换【注</span><span style="font-size: 12pt; ">5</span><span style="font-size: 12pt; font-family: 宋体; ">】，</span><span style="font-size: 12pt; ">c</span><span style="font-size: 12pt; font-family: 宋体; ">就变成合适大小了，最后在整个表达式结束的时候，这个临时右值析构归还内存空间。真是绅士一般的优雅！</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; color: blue; font-family: 宋体; ">注</span><span style="font-size: 12pt; color: blue; ">5</span><span style="font-size: 12pt; color: blue; font-family: 宋体; ">：这个时候这个临时右值就发生了改变。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">如果还不理解，可以看看书，或者直接看库的源代码。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">至于为什么会这样？我思考了一下，我想是这样的，我们看类（</span><span style="font-size: 12pt; ">class</span><span style="font-size: 12pt; font-family: 宋体; ">）的数据布置结构，会发现它的每一个数据成员都是有名字的，我想编译器在编译的过程中，都会生成一个外部不所知的对这个临时对象右值的名字引用，但需要改变这个临时对象的时候，这个名字就用上了。比如：</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; ">class Point</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; ">{</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; ">public: //</span><span style="font-size: 12pt; font-family: 宋体; ">纯粹为了方便，我把数据成员公开，现实中尽量不要这样用</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int x, y ,z;</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-size: 12pt; font-family: 宋体; ">&#8230;&#8230;</span><span style="font-size: 12pt; ">//</span><span style="font-size: 12pt; font-family: 宋体; ">其他各种成员函数</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; ">};</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">我们现在就可以改变右值，用到了匿名的引用名字。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; ">Point().x = 6;//</span><span style="font-size: 12pt; font-family: 宋体; ">改变了右值</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; ">Point().y = 6;//</span><span style="font-size: 12pt; font-family: 宋体; ">同意改变了右值，不过注意，这个右值跟上面的不是同一个。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 32.15pt; "><strong><span style="font-size: 16pt; color: #993300; font-family: 宋体; ">总结</span></strong><strong></strong></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">左值和右值的真正区别我想就是这些了，左值表示有特定的名字引用，而右值没有特定的名字引用。当然我仍然会有疏忽，希望大家能够提醒我，指正我的不足。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">前两天看</span><span style="font-size: 12pt; ">Herb Sutter</span><span style="font-size: 12pt; font-family: 宋体; ">从邮件中寄来的新文章（我订阅他的新文章邮件通知），一篇是讲</span><span style="font-size: 12pt; ">Tuple</span><span style="font-size: 12pt; font-family: 宋体; ">数据结构的，没有什么新意，以前好像看过，还有一篇名字是：（</span><span style="font-size: 12pt; ">Mostly</span><span style="font-size: 12pt; font-family: 宋体; ">）</span><span style="font-size: 12pt; ">Private,</span><span style="font-size: 12pt; font-family: 宋体; ">地址为</span><span style="font-size: 12pt; "><a href="http://www.cuj.com/documents/s=8273/cujcexp2107sutter/" style="color: #336699; text-decoration: none; ">http://www.cuj.com/documents/s=8273/cujcexp2107sutter/</a>&nbsp;</span><span style="font-size: 12pt; font-family: 宋体; ">内容本身并不深，但看完文章，发现随处可见</span><span style="font-size: 12pt; ">C++</span><span style="font-size: 12pt; font-family: 宋体; ">的波诡云谲，又会对什么叫袖里乾坤，滴水藏海多一份感性认识。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">在下一篇文章我想从不同于一般的角度，从自己的经历谈谈在校毕业生在</span><span style="font-size: 12pt; ">IT</span><span style="font-size: 12pt; font-family: 宋体; ">行业怎样找工作，我想会让所有读者都有一些思考，不仅仅是求职者。题目我已经想好了，就叫《扮虎吃猪》，不过现在我有一些别的事情要忙，所以可能会让大家等几天。</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">转载请注明来源，谢谢！</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">吴桐写于</span><span style="font-size: 12pt; ">2003.6.20</span></p><p style="color: #333333; font-family: Arial; background-color: #ffffff; text-indent: 24pt; "><span style="font-size: 12pt; font-family: 宋体; ">最近修改</span><span style="font-size: 12pt; ">2003.6.21<br /><br />转自：</span>
<a href="http://blog.csdn.net/csdnji/article/details/169200">http://blog.csdn.net/csdnji/article/details/169200</a>
</p><div style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 16px; background-color: #ffffff; color: red; "><a href="http://www.cppblog.com/cc/archive/2008/06/07/7619.html"></a></div><img src ="http://www.cppblog.com/mysileng/aggbug/196350.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-12-16 15:08 <a href="http://www.cppblog.com/mysileng/archive/2012/12/16/196350.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>拷贝构造函数和赋值操作函数的参数为什么是const和&amp;?! </title><link>http://www.cppblog.com/mysileng/archive/2012/12/16/196349.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Sun, 16 Dec 2012 06:52:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/12/16/196349.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/196349.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/12/16/196349.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/196349.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/196349.html</trackback:ping><description><![CDATA[<font face="song, Verdana"><span style="line-height: 22.366666793823242px;">习惯了这样写，但有时候会反问下自己为什么要const和&amp;?<br />1.为什么要&amp;?<br /></span></font><span style="font-family: song, Verdana; line-height: 22.383333206176758px; ">答：&amp;是必须的。<br />如果它不是引用，在传递参数时，会产生一个局部变量做参数，而这个局部变量的构造又要调copy构造函数一次,....那就子子孙孙无穷匮了....<br />但如果是引用，在传递参数时候，局部变量只需要绑定原变量，而不需要再一次调用copy构造函数。<br /><br />2.为什么要const?<br />答：const不是必须的。<br /></span><pre id="best-content-284255532" accuse="aContent" class="best-text mb-10" style="margin-top: 0px; margin-bottom: 10px; padding: 0px; font-family: arial, 'courier new', courier, 宋体, monospace; white-space: pre-wrap; word-wrap: break-word; line-height: 24px; background-color: #fffcf6; ">构造函数是用引用方式传递复制对象，引用方式传递的是地址，因此在构造函数内对该引用的修改会影响源对象。而你在用对象a1构造a2时，自然不希望复制构造函数会改变a1的内容，因此要防止复制构造函数内部修改该引用，所以一般用const声明。加不加const还是需要看copy构造的行为，比如std::auto_ptr的构造函数，就没有const,因为它要获得原有对象指针的拥有权。</pre><img src ="http://www.cppblog.com/mysileng/aggbug/196349.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-12-16 14:52 <a href="http://www.cppblog.com/mysileng/archive/2012/12/16/196349.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++模板元编程 入门简介 (转)</title><link>http://www.cppblog.com/mysileng/archive/2012/12/01/195869.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Sat, 01 Dec 2012 12:01:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/12/01/195869.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/195869.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/12/01/195869.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/195869.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/195869.html</trackback:ping><description><![CDATA[<div style="box-sizing: border-box; font-size: 14.7px; font-weight: bold; margin-bottom: 10px; color: #4b4b4b; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;"><h1><a id="cb_post_title_url" href="http://www.cnblogs.com/salomon/archive/2012/06/04/2534787.html" style="box-sizing: border-box; text-decoration: initial; color: #3468a4;">C++模板元编程 入门简介</a></h1></div><div id="cnblogs_post_body" style="box-sizing: border-box; color: #4b4b4b; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 20px; background-color: #ffffff; word-break: normal !important;"><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">最近一直在看STL和Boost，源码里边好多涉及到模板元编程技术，简单了解一下，备忘（Boost Python中的涉及模板元的部分重点关注一下）。</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;"><strong style="box-sizing: border-box;">范例引入</strong></p><div style="box-sizing: border-box; font-size: 12px; margin: 5px 0px;"><div style="box-sizing: border-box; margin-top: 5px;"><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;主模板</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">template</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;N</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;Fib<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">enum</span><span style="color: #000000; ">&nbsp;{&nbsp;Result&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Fib</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">N</span><span style="color: #000000; ">-</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">::Result&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;Fib</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">N</span><span style="color: #000000; ">-</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">::Result&nbsp;};<br />};<br /><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;完全特化版</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">template&nbsp;</span><span style="color: #000000; ">&lt;&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;Fib</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">enum</span><span style="color: #000000; ">&nbsp;{&nbsp;Result&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;};<br />};<br /><br /><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;完全特化版</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">template&nbsp;</span><span style="color: #000000; ">&lt;&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;Fib</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">enum</span><span style="color: #000000; ">&nbsp;{&nbsp;Result&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">&nbsp;};<br />};<br /><br /></span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;main()<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;i&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Fib</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">10</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">::Result;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;std::cout&nbsp;&lt;&lt;&nbsp;i&nbsp;&lt;&lt;&nbsp;std::endl;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">}</span></div></div></div><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;"><strong style="box-sizing: border-box;">主要思想</strong></p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">利用模板特化机制实现编译期条件选择结构，利用递归模板实现编译期循环结构，模板元程序则由编译器在编译期解释执行。</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;"><strong style="box-sizing: border-box;">优劣及适用情况</strong></p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">通过将计算从运行期转移至编译期，在结果程序启动之前做尽可能多的工作，最终获得速度更快的程序。也就是说模板元编程的优势在于：</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">　　1.以编译耗时为代价换来卓越的运行期性能（一般用于为性能要求严格的数值计算换取更高的性能）。通常来说，一个有意义的程序的运行次数（或服役时间）总是远远超过编译次数（或编译时间）。</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">　　2.提供编译期类型计算，通常这才是模板元编程大放异彩的地方。</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">模板元编程技术并非都是优点：</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">　　1.代码可读性差，以类模板的方式描述算法也许有点抽象。</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">　　2.调试困难，元程序执行于编译期，没有用于单步跟踪元程序执行的调试器（用于设置断点、察看数据等）。程序员可做的只能是等待编译过程失败，然后人工破译编译器倾泻到屏幕上的错误信息。</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">　　3.编译时间长，通常带有模板元程序的程序生成的代码尺寸要比普通程序的大，</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">　　4.可移植性较差，对于模板元编程使用的高级模板特性，不同的编译器的支持度不同。</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">总结：</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">模板元编程技术不适用普通程序员的日常应用，它常常会做为类库开发的提供技术支持，为常规模板代码的内核的关键算法实现更好的性能或者编译期类型计算。模板元程序几乎总是应该与常规代码结合使用被封装在一个程序库的内部。对于库的用户来说，它应该是透明的。</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;"><strong style="box-sizing: border-box;">工程应用实例</strong></p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">1. Blitz++：由于模板元编程最先是因为数值计算而被发现的，因此早期的研究工作主要集中于数值计算方面，Blitz++库利用模板将运行期计算转移至编译期的库，主要提供了对向量、矩阵等进行处理的线性代数计算。</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">2.Loki：将模板元编程在类型计算方面的威力应用于设计模式领域，利用元编程（以及其他一些重要的设计技术）实现了一些常见的设计模式之泛型版本。Loki库中的Abstract Factory泛型模式即借助于这种机制实现在不损失类型安全性的前提下降低对类型的静态依赖性。</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">3.Boost：元编程库目前主要包含MPL、Type Traits和Static Assert等库。 Static Assert和Type Traits用作MPL的基础。Boost Type Traits库包含一系列traits类，用于萃取C++类型特征。另外还包含了一些转换traits（例如移除一个类型的const修饰符等）。Boost Static Assert库用于编译期断言，如果评估的表达式编译时计算结果为true，则代码可以通过编译，否则编译报错。</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;"><strong style="box-sizing: border-box;">技术细节</strong></p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">模板元编程使用静态C++语言成分，编程风格类似于函数式编程，在模板元编程中，主要操作整型（包括布尔类型、字符类型、整数类型）常量和类型，不可以使用变量、赋值语句和迭代结构等。被操纵的实体也称为元数据（Metadata），所有元数据均可作为模板参数。</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">由于在模板元编程中不可以使用变量，我们只能使用typedef名字和整型常量。它们分别采用一个类型和整数值进行初始化，之后不能再赋予新的类型或数值。如果需要新的类型或数值，必须引入新的typedef名字或常量。</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;"><strong style="box-sizing: border-box;">其它范例</strong></p><div style="background-color: #eeeeee; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><span style="color: #000000; "><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;仅声明</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;Nil;<br /><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;主模板</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">template&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;T</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;IsPointer<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">enum</span><span style="color: #000000; ">&nbsp;{&nbsp;Result&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">&nbsp;};<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;Nil&nbsp;ValueType;<br />};<br /><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;局部特化</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">template&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;T</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;IsPointer</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">T</span><span style="color: #000000; ">*&gt;</span><span style="color: #000000; "><br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">enum</span><span style="color: #000000; ">&nbsp;{&nbsp;Result&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">&nbsp;};<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;T&nbsp;ValueType;<br />};<br /><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;示例</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;main()<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;IsPointer</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">*&gt;</span><span style="color: #000000; ">::Result&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;endl;<br />&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;IsPointer</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">::Result&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;endl;<br />&nbsp;&nbsp;&nbsp;&nbsp;IsPointer</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">*&gt;</span><span style="color: #000000; ">::ValueType&nbsp;i&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">IsPointer&lt;int&gt;::ValueType&nbsp;j&nbsp;=&nbsp;1;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;错误：使用未定义的类型Nil</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">}</span></div><div style="box-sizing: border-box; font-size: 12px; margin: 5px 0px;"><div id="cnblogs_code_open_6238aa8c-25e9-4b29-a766-03636da91035" style="box-sizing: border-box;"><div style="box-sizing: border-box; margin-top: 5px;"><span style="box-sizing: border-box; padding-right: 5px; line-height: 1.5;"></span></div></div></div><div style="background-color: #eeeeee; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><span style="color: #000000; "><br /><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; ">主模板</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">template</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;StaticAssert;<br /><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;完全特化</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">template</span><span style="color: #000000; ">&lt;&gt;</span><span style="color: #000000; ">&nbsp;<br /></span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;StaticAssert</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />{};<br /><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;辅助宏</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;STATIC_ASSERT(exp)\</span><span style="color: #000000; "><br />{&nbsp;StaticAssert</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">((exp)&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">)</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;StaticAssertFailed;&nbsp;}<br /><br /></span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;main()<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;STATIC_ASSERT(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">);<br />}</span></div>&nbsp;<p>&nbsp;</p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;"><strong style="box-sizing: border-box;">References：</strong></p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;"><strong style="box-sizing: border-box;"><a href="http://club.topsage.com/thread-421469-1-1.html" style="box-sizing: border-box; color: #3468a4;">http://club.topsage.com/thread-421469-1-1.html</a></strong></p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;"><strong style="box-sizing: border-box;"><a href="http://wenku.baidu.com/view/c769720df78a6529647d539d.html" style="box-sizing: border-box; color: #3468a4;">http://wenku.baidu.com/view/c769720df78a6529647d539d.html</a></strong></p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">Blitz++：&nbsp;<a href="http://www.oonumerics.org/blitz" target="_blank" style="box-sizing: border-box; color: #3468a4;">http://www.oonumerics.org/blitz</a>&nbsp;.<br style="box-sizing: border-box;" />Loki ：<a href="http://sourceforge.net/projects/loki-lib" target="_blank" style="box-sizing: border-box; color: #3468a4;">http://sourceforge.net/projects/loki-lib</a></p><p style="box-sizing: border-box; margin-top: 10px; margin-bottom: 10px;">Boost：<a href="http://www.boost.org/" style="box-sizing: border-box; color: #3468a4;">http://www.boost.org/</a></p></div><img src ="http://www.cppblog.com/mysileng/aggbug/195869.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-12-01 20:01 <a href="http://www.cppblog.com/mysileng/archive/2012/12/01/195869.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Traits 技术 --- 模板元编程 (转) </title><link>http://www.cppblog.com/mysileng/archive/2012/12/01/195868.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Sat, 01 Dec 2012 11:57:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/12/01/195868.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/195868.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/12/01/195868.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/195868.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/195868.html</trackback:ping><description><![CDATA[<div>&nbsp; &nbsp; &nbsp; 什么是traits，为什么人们把它认为是C++ Generic Programming的重要技术？</div><div><br />&nbsp; &nbsp; &nbsp; 简洁地说，traits如此重要，是因为此项技术允许系统在编译时根据类型作一些决断，就好像在运行时根据值来做出决断一样。更进一步，此技术遵循&#8220;另增一个间接层&#8221;的谚语，解决了不少软件工程问题，traits使您能根据其产生的背景(context) 来做出抉择。这样最终的代码就变得清晰易读，容易维护。如果你正确运用了traits技术，你就能在不付出任何性能和安全代价的同时得到这些好处，或者能够契合其他解决方案上的需求。<br /></div><div>&nbsp; &nbsp; &nbsp; 先举个浅显易懂的例子来说明traits的用法：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; ">首先假如有以下一个泛型的迭代器类，其中类型参数&nbsp;T&nbsp;为迭代器所指向的类型：</span><span style="color: #008000; "><br /></span>template&nbsp;&lt;typename&nbsp;T&gt;<br /><span style="color: #0000FF; ">class</span>&nbsp;myIterator<br />{<br /><img src="http://www.cppblog.com/Images/dot.gif"  alt="" /><br />};</div></div><div>&nbsp; &nbsp; &nbsp; &nbsp;那么当使用myIterator时，怎样才能知道它所指向元素的类型呢？一种解决方案是为这个类加入一个内嵌类型：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->template&nbsp;&lt;typename&nbsp;T&gt;<br /><span style="color: #0000FF; ">class</span>&nbsp;myIterator<br />{<br />typedef&nbsp;T&nbsp;value_type;<br /><img src="http://www.cppblog.com/Images/dot.gif"  alt="" /><br />};</div></div><div>&nbsp; &nbsp; &nbsp; 当使用myIterator时，可以通过myIterator::value_type来获得相应的myIterator所指向的类型。下面举例使用：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->template&nbsp;&lt;typename&nbsp;T&gt;<br />typename&nbsp;myIterator&lt;T&gt;::value_type&nbsp;func(myIterator&lt;T&gt;&nbsp;i)<br />{<br /><img src="http://www.cppblog.com/Images/dot.gif"  alt="" /><br />}</div></div><div>&nbsp; &nbsp; &nbsp; 这里定义了一个函数func，返回值类型为参数i所指的类型，也就是模板参数T，那么为什么不直接使用模板参数T，而要绕着圈去使用那个value_type呢？所以我们返回来，当修改func函数时，它能够适应所有类型的迭代器，不是更好吗？如下所示：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->template&nbsp;&lt;typename&nbsp;I&gt;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">这里的I可以是任意类型的迭代器</span><span style="color: #008000; "><br /></span>typename&nbsp;I::value_type&nbsp;func(I&nbsp;i)<br />{<br /><img src="http://www.cppblog.com/Images/dot.gif"  alt="" /><span style="color: #008000; "><br /></span>}</div></div><div>&nbsp; &nbsp; &nbsp; 现在，任意定义了value_type内嵌类型的迭代器都可以做为func的参数了，并且func的返回值的类型将与相应迭代器所指的元素的类型一致。至此一切问题似乎都已解决，并且似乎并没有使用任何特殊的技术。<br />&nbsp; &nbsp; &nbsp; 然而当考虑到以下情况时，新的问题便显现出来了：原生指针也完全可以做为迭代器来使用，然而显然没有办法为原生指针添加一个value_type的内嵌类型，如此一来func()函数就不能适用原生指针了，这不能不说是一大缺憾。那么有什么办法可以解决这个问题呢？此时不禁想到了用Traits萃取类型信息。可以不直接使用myIterator的value_type，而是<span style="color: red;">通过Traits类来把这个信息提取出来：</span>(不同的类型，可以有不同的提取方式)</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->template&nbsp;&lt;typename&nbsp;T&gt;<br /><span style="color: #0000FF; ">class</span>&nbsp;Traits<br />{<br />typedef&nbsp;typename&nbsp;T::value_type&nbsp;value_type;<br />};</div></div><div>&nbsp; &nbsp; &nbsp; 这样以后就可以通过Traits&lt;myIterator&gt;::value_type来提取出myIterator中的value_type，于是func函数改写成：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->template&nbsp;&lt;typename&nbsp;I&gt;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">这里的I可以是任意类型的迭代器</span><span style="color: #008000; "><br /></span>typename&nbsp;Traits&lt;I&gt;::value_type&nbsp;Foo(I&nbsp;i)<br />{<br /><img src="http://www.cppblog.com/Images/dot.gif"  alt="" /><br />}</div></div><div>&nbsp; &nbsp; &nbsp; 然而，即使这样，那个原生指针的问题仍然没有解决，因为Trait类还是没办法获得原生指针的相关信息。于是不妨将<span style="color: red;">Traits偏特化(partial specialization)：(通过特化、重载特化等手段产出不同的提取方式)</span></div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->template&nbsp;&lt;typename&nbsp;T&gt;<br /><span style="color: #0000FF; ">class</span>&nbsp;Traits&lt;T*&gt;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">注意&nbsp;这里针对原生指针进行了偏特化</span><span style="color: #008000; "><br /></span>{<br />typedef&nbsp;typename&nbsp;T&nbsp;value_type;<br />};</div></div><div>&nbsp; &nbsp; &nbsp;通过上面这个Traits的偏特化版本，一个T*类型的指针所指向的元素的类型为T。如此一来，我们func函数就完全可以适用于原生指针了。比如：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">int</span>&nbsp;*&nbsp;p;<br /><img src="http://www.cppblog.com/Images/dot.gif"  alt="" />.<br /><span style="color: #0000FF; ">int</span>&nbsp;i&nbsp;=&nbsp;func(p);</div></div><div>&nbsp; &nbsp; &nbsp;Traits会自动推导出p所指元素的类型为int，从而func正确返回。</div><div>-----------------------------------------------------------------------------------------------------------------------------------------------------------</div><div>&nbsp; &nbsp; &nbsp;现在再看一个更加一般的例子&#8212;&#8212;smart pointers。(智能指针)</div><div>&nbsp; &nbsp; &nbsp;假设你正在设计一个SmartPtr模板类，对于一个smart pointer 来说，它的最大的用处是可以自动管理内存问题，同时在其他方面又像一个常规指针。但是有些C++的Smart&nbsp;pointer实现技术却非常令人难以理解。这一残酷的事实带来了一个重要实践经验：你最好尽一切可能一劳永逸，写出一个出色的、具有工业强度的 smart pointer来满足你所有的需求。此外，你通常不能修改一个类来适应你的smart pointer，所以你的SmartPtr一定要足够灵活。</div><div>&nbsp; &nbsp; &nbsp;有不少类层次使用引用计数(reference counting)以及相应的函数管理对象的生存期。然而，并没有reference counting的标准实现方法，每一个C++库的供应商在实现的语法和/或语义上都有所不同。例如，在你的应用程序中有这样两个interfaces：<br />第一种智能指针--大部分的类实现了RefCounted接口:</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">class</span>&nbsp;RefCounted<br />{<br /><span style="color: #0000FF; ">public</span>:<br /><span style="color: #0000FF; ">virtual</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;IncRef()&nbsp;=&nbsp;0;<br /><span style="color: #0000FF; ">virtual</span>&nbsp;<span style="color: #0000FF; ">bool</span>&nbsp;DecRef()&nbsp;=&nbsp;0;<br /><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;if&nbsp;you&nbsp;DecRef()&nbsp;to&nbsp;zero&nbsp;references,&nbsp;the&nbsp;object&nbsp;is&nbsp;destroyed<br /></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;automatically&nbsp;and&nbsp;DecRef()&nbsp;returns&nbsp;true</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">virtual</span>&nbsp;~RefCounted()&nbsp;{}<br />};</div></div><div>第二种智能指针--第三方提供的Widget类使用不同的接口：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">class</span>&nbsp;Widget<br />{<br /><span style="color: #0000FF; ">public</span>:<br /><span style="color: #0000FF; ">void</span>&nbsp;AddReference();<br /><span style="color: #0000FF; ">int</span>&nbsp;RemoveReference();&nbsp;<br /><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;returns&nbsp;the&nbsp;remaining&nbsp;number&nbsp;of&nbsp;references;&nbsp;it's&nbsp;the&nbsp;client's<br /></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;responsibility&nbsp;to&nbsp;destroy&nbsp;the&nbsp;object</span><span style="color: #008000; "><br /></span><img src="http://www.cppblog.com/Images/dot.gif"  alt="" /><br />};</div></div><div>&nbsp; &nbsp; &nbsp; 不过你并不想维护两个smart pointer类，你想让两种类共享一个SmartPtr。一个基于traits的解决方案把两种不同的接口用语法和语义上统一的接口包装起来，建立针对普通类的通用模板，而针对Widget建立一个特殊化版本，如下：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->template&nbsp;&lt;<span style="color: #0000FF; ">class</span>&nbsp;T&gt;<br /><span style="color: #0000FF; ">class</span>&nbsp;RefCountingTraits<br />{<br /><span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;Refer(T*&nbsp;p)<br />{<br />p-&gt;IncRef();&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;assume&nbsp;RefCounted&nbsp;interface</span><span style="color: #008000; "><br /></span>}<br /><span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;Unrefer(T*&nbsp;p)<br />{<br />p-&gt;DecRef();&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">assume&nbsp;RefCounted&nbsp;interface</span><span style="color: #008000; "><br /></span>}<br />};<br />template&lt;&gt;<br /><span style="color: #0000FF; ">class</span>&nbsp;RefCountingTraits&lt;Widget&gt;<br />{<br /><span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;Refer(Widget*&nbsp;p)<br />{<br />p-&gt;AddReference();&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">use&nbsp;Widget&nbsp;interface</span><span style="color: #008000; "><br /></span>}<br /><span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;Unrefer(Widget*&nbsp;p)<br />{<br /><span style="color: #008000; ">//</span><span style="color: #008000; ">use&nbsp;Widget&nbsp;interface</span><span style="color: #008000; "><br /></span>If&nbsp;(p-&gt;RemoveReference()&nbsp;==&nbsp;0)<br />delete&nbsp;p;<br />}<br />};</div></div><div>&nbsp; &nbsp; &nbsp; 在SmartPtr里，我们像这样使用RefCountingTraits:</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->template&nbsp;&lt;<span style="color: #0000FF; ">class</span>&nbsp;T&gt;<br /><span style="color: #0000FF; ">class</span>&nbsp;SmartPtr<br />{<br /><span style="color: #0000FF; ">private</span>:<br />typedef&nbsp;RefCountingTraits&lt;T&gt;&nbsp;RCTraits;<br />T*&nbsp;pointee_;<br /><span style="color: #0000FF; ">public</span>:<br /><img src="http://www.cppblog.com/Images/dot.gif"  alt="" /><br />~SmartPtr()<br />{<br />RCTraits::Unrefer(pointee_);<br />}<br />};</div></div><div>&nbsp; &nbsp; &nbsp; 当然在上面的例子里，你可能会争论说你可以直接特殊化Widget类的SmartPtr的构造与析构函数。你可以使用把模板特殊化技术用在 SmartPtr本身，而不是用在traits上头，这样还可以消除额外的类。尽管对这个问题来说这种想法没错，但还是由一些你需要注意的缺陷：</div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><div><ol><li>这么干缺乏可扩展性。如果给SmartPtr再增加一个模板参数，你不能特殊化这样一个SmartPtr&lt;T. U&gt;，其中模板参数T是Widget，而U可以为其他任何类型。</li><li>最终代码不那么清晰。Trait有一个名字，而且把相关的东西很好的组织起来，因此使用traits的代码更加容易理解。相比之下，用直接特殊化SmartPtr成员函数的代码，看上去更招黑客的喜欢。</li></ol></div></blockquote><div>&nbsp; &nbsp; &nbsp; 用继承机制的解决方案，就算本身完美无瑕，也至少存在上述的缺陷。解决这样一个变体问题，使用继承实在是太笨重了。此外，通常用以取代继承方案的另一种经典机制&#8212;&#8212;containment，用在这里也显得画蛇添足，繁琐不堪。相反，traits方案干净利落，简明有效，物合其用，恰到好处。</div><div>&nbsp; &nbsp; &nbsp; Traits的一个重要的应用是&#8220;interface glue&#8221;（接口胶合剂），通用的、可适应性极强的适配子。如果不同的类对于一个给定的概念有着不同的实现，traits可以把这些实现再组织统一成一个公共的接口。对于一个给定类型提供多种TRAITS：现在，我们假设所有的人都很喜欢你的SmartPtr模板类，直到有一天，在你的多线程应用程序里开始现了神秘的bug。你发现罪魁祸首是Widget，它的引用计数函数并不是线程安全的。现在你不得不亲自实现Widget:: AddReference和Widget::RemoveReference，最合理的位置应该是在RefCountingTraits中，打上个补丁吧：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Example&nbsp;7:&nbsp;Patching&nbsp;Widget's&nbsp;traits&nbsp;for&nbsp;thread&nbsp;safety</span><span style="color: #008000; "><br /></span>template&nbsp;&lt;&gt;<br /><span style="color: #0000FF; ">class</span>&nbsp;RefCountingTraits&lt;Widget&gt;<br />{<br /><span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;Refer(Widget*&nbsp;p)<br />{<br />Sentry&nbsp;s(lock_);&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;serialize&nbsp;access</span><span style="color: #008000; "><br /></span>p-&gt;AddReference();<br />}<br /><span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;Unrefer(Widget*&nbsp;p)<br />{<br />Sentry&nbsp;s(lock_);&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;serialize&nbsp;access</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">if</span>&nbsp;(p-&gt;RemoveReference()&nbsp;==&nbsp;0)<br />delete&nbsp;p;<br />}<br /><span style="color: #0000FF; ">private</span>:<br /><span style="color: #0000FF; ">static</span>&nbsp;Lock&nbsp;lock_;<br />};</div></div><div>&nbsp; &nbsp; &nbsp; &nbsp;不幸的是，虽然你重新编译、测试之后正确运行，但是程序慢得像蜗牛。仔细分析之后发现，你刚才的所作所为往程序里塞了一个糟糕的瓶颈。实际上只有少数几个Widget是需要能够被好几个线程访问的，余下的绝大多数Widget都是只被一个线程访问的。你要做的是告诉编译器按你的需求分别使用多线程traits和单线程traits这两个不同版本。你的代码主要使用单线程traits。</div><div>&nbsp; &nbsp; &nbsp; &nbsp;如何告诉编译器使用哪个traits？一种方法是把traits作为另一个模板参数传给SmartPtr。缺省情况下传递老式的traits模板，而用特定的类型实例化特定的模板。</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->template&nbsp;&lt;<span style="color: #0000FF; ">class</span>&nbsp;T,&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;RCTraits&nbsp;=&nbsp;RefCountingTraits&lt;T&gt;&nbsp;&gt;<br /><span style="color: #0000FF; ">class</span>&nbsp;SmartPtr<br />{<br /><img src="http://www.cppblog.com/Images/dot.gif"  alt="" /><br />};</div></div><div>&nbsp; &nbsp; &nbsp; 你对单线程版的RefCountingTraits&lt;Widget&gt;不做改动，而把多线程版放在一个单独的类中：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">class</span>&nbsp;MtRefCountingTraits<br />{<br /><span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;Refer(Widget*&nbsp;p)<br />{<br />Sentry&nbsp;s(lock_);&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;serialize&nbsp;access</span><span style="color: #008000; "><br /></span>p-&gt;AddReference();<br />}<br /><span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;Unrefer(Widget*&nbsp;p)<br />{<br />Sentry&nbsp;s(lock_);&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;serialize&nbsp;access</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">if</span>&nbsp;(p-&gt;RemoveReference()&nbsp;==&nbsp;0)<br />delete&nbsp;p;<br />}<br /><span style="color: #0000FF; ">private</span>:<br /><span style="color: #0000FF; ">static</span>&nbsp;Lock&nbsp;lock_;<br />};</div></div><div>&nbsp; &nbsp; &nbsp; &nbsp;现在你可将SmartPtr&lt;Widget&gt;用于单线程目的，将SmartPtr&lt;Widget,MtRefCountingTraits&gt;用于多线程目的。</div><div>&nbsp; &nbsp; &nbsp; &nbsp;最后，以SGI STL中的__type_traits结束本篇讨论，在SGI 实现版的STL中，为了获取高效率，提供了__type_traits，用来提取类的信息，比如类是否拥有trival的构造、析构、拷贝、赋值操作，然后跟据具体的信息，就可提供最有效率的操作。以下摘录cygwin的gcc3.3源码，有改动，在&lt;type_traits.h&gt;中。</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">struct</span>&nbsp;__true_type&nbsp;{};<br /><span style="color: #0000FF; ">struct</span>&nbsp;__false_type&nbsp;{};<br />template&nbsp;&lt;<span style="color: #0000FF; ">class</span>&nbsp;_Tp&gt;<br /><span style="color: #0000FF; ">struct</span>&nbsp;__type_traits<br />{<br />typedef&nbsp;__true_type&nbsp;&nbsp;&nbsp;&nbsp;this_dummy_member_must_be_first;<br />typedef&nbsp;__false_type&nbsp;&nbsp;&nbsp;&nbsp;has_trivial_default_constructor;<br />typedef&nbsp;__false_type&nbsp;&nbsp;&nbsp;&nbsp;has_trivial_copy_constructor;<br />typedef&nbsp;__false_type&nbsp;&nbsp;&nbsp;&nbsp;has_trivial_assignment_operator;<br />typedef&nbsp;__false_type&nbsp;&nbsp;&nbsp;&nbsp;has_trivial_destructor;<br />typedef&nbsp;__false_type&nbsp;&nbsp;&nbsp;&nbsp;is_POD_type;<br />};</div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; 对于普通类来讲，为了安全起见，都认为它们拥有non-trival的构造、析构、拷贝、赋值函数，POD是指plain old data。接下来对C++的原生类型（bool，int， double之类）定义了显式的特化实现，以double为例：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->template&lt;&gt;&nbsp;<br /><span style="color: #0000FF; ">struct</span>&nbsp;__type_traits&lt;<span style="color: #0000FF; ">long</span>&nbsp;<span style="color: #0000FF; ">double</span>&gt;&nbsp;{<br />typedef&nbsp;__true_type&nbsp;&nbsp;&nbsp;&nbsp;has_trivial_default_constructor;<br />typedef&nbsp;__true_type&nbsp;&nbsp;&nbsp;&nbsp;has_trivial_copy_constructor;<br />typedef&nbsp;__true_type&nbsp;&nbsp;&nbsp;&nbsp;has_trivial_assignment_operator;<br />typedef&nbsp;__true_type&nbsp;&nbsp;&nbsp;&nbsp;has_trivial_destructor;<br />typedef&nbsp;__true_type&nbsp;&nbsp;&nbsp;&nbsp;is_POD_type;<br />};</div></div><div>还有，对所有的原生指针来讲，它们的构造、析构等操作也是trival的，因此有：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->template&nbsp;&lt;<span style="color: #0000FF; ">class</span>&nbsp;_Tp&gt;<br /><span style="color: #0000FF; ">struct</span>&nbsp;__type_traits&lt;_Tp*&gt;&nbsp;{<br />typedef&nbsp;__true_type&nbsp;&nbsp;&nbsp;&nbsp;has_trivial_default_constructor;<br />typedef&nbsp;__true_type&nbsp;&nbsp;&nbsp;&nbsp;has_trivial_copy_constructor;<br />typedef&nbsp;__true_type&nbsp;&nbsp;&nbsp;&nbsp;has_trivial_assignment_operator;<br />typedef&nbsp;__true_type&nbsp;&nbsp;&nbsp;&nbsp;has_trivial_destructor;<br />typedef&nbsp;__true_type&nbsp;&nbsp;&nbsp;&nbsp;is_POD_type;<br />};</div></div><div>&nbsp; &nbsp; &nbsp; 简化&lt;stl_algobase.h&gt;中copy的部分代码来说明对__type_traits的应用。</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->template&lt;typename&nbsp;_Tp&gt;<br />inline&nbsp;_Tp*&nbsp;__copy_trivial(<span style="color: #0000FF; ">const</span>&nbsp;_Tp*&nbsp;__first,&nbsp;<span style="color: #0000FF; ">const</span>&nbsp;_Tp*&nbsp;__last,&nbsp;_Tp*&nbsp;__result)<br />{<br />memmove(__result,&nbsp;__first,&nbsp;<span style="color: #0000FF; ">sizeof</span>(_Tp)&nbsp;*&nbsp;(__last&nbsp;-&nbsp;__first));<br /><span style="color: #0000FF; ">return</span>&nbsp;__result&nbsp;+&nbsp;(__last&nbsp;-&nbsp;__first);<br />}<br />template&lt;typename&nbsp;_Tp&gt;<br />inline&nbsp;_Tp*&nbsp;__copy_aux&nbsp;(_Tp*&nbsp;__first,&nbsp;_Tp*&nbsp;__last,&nbsp;_Tp*&nbsp;__result,&nbsp;__true_type)<br />{&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;__copy_trivial(__first,&nbsp;__last,&nbsp;__result);&nbsp;}&nbsp;<br />template&lt;typename&nbsp;_Tp&gt;<br />inline&nbsp;_Tp*&nbsp;__copy_aux&nbsp;(_Tp*&nbsp;__first,&nbsp;_Tp*&nbsp;__last,&nbsp;_Tp*&nbsp;__result,&nbsp;__false_type)<br />{&nbsp;另外处理；}<br />template&lt;typename&nbsp;_InputIter,&nbsp;typename&nbsp;_OutputIter&gt;&nbsp;inline&nbsp;<br />_OutputIter&nbsp;copy&nbsp;(_InputIter&nbsp;__first,&nbsp;_InputIter&nbsp;__last,&nbsp;_OutputIter&nbsp;__result)<br />{<br />typedef&nbsp;typename&nbsp;iterator_traits&lt;_InputIter&gt;::value_type&nbsp;_ValueType;<br />typedef&nbsp;typename&nbsp;__type_traits&lt;_ValueType&gt;::has_trivial_assignment_operator&nbsp;_Trivial;<br /><span style="color: #0000FF; ">return</span>&nbsp;__copy_aux(__first,&nbsp;__last,&nbsp;__result,&nbsp;_Trivial());<br />}</div></div><div>&nbsp; &nbsp; &nbsp; Copy 函数利用__type_traits判断当前的value_type是否有trival的赋值操作，如果是，则产生类__true_type的实例，编译 时选择__copy_trivial函数进行memmove，效率最高。如果是non-trival的赋值操作，则另作处理，效率自然低些。__true_type和__false_type之所以是类，就因为C++的函数重载是根据类型信息来的，不能依据参数值来判别。使用SGI STL时，可以为自己的类定义__type_traits显式特化版本，以求达到高效率。</div><div></div><div>本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/budTang/archive/2008/05/06/2397013.aspx</div><img src ="http://www.cppblog.com/mysileng/aggbug/195868.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-12-01 19:57 <a href="http://www.cppblog.com/mysileng/archive/2012/12/01/195868.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++类型转换总结(转)</title><link>http://www.cppblog.com/mysileng/archive/2012/11/25/195650.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Sun, 25 Nov 2012 09:33:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/11/25/195650.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/195650.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/11/25/195650.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/195650.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/195650.html</trackback:ping><description><![CDATA[<p style="line-height: 19px; font-size: 13px; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; text-align: left; background-color: #ffffff; ">C风格的强制类型转换(Type Cast)很简单，不管什么类型的转换统统是：<br />TYPE b = (TYPE)a。<br />C++风格的类型转换提供了4种类型转换操作符来应对不同场合的应用。</p><p style="line-height: 19px; font-size: 13px; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; text-align: left; background-color: #ffffff; ">const_cast，字面上理解就是去const属性。<br />static_cast，命名上理解是静态类型转换。如int转换成char。<br />dynamic_cast，命名上理解是动态类型转换。如子类和父类之间的多态类型转换。<br />reinterpret_cast，仅仅重新解释类型，但没有进行二进制的转换。<br />4种类型转换的格式，如：TYPE B = static_cast(TYPE)(a)。</p><p style="line-height: 19px; font-size: 13px; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; text-align: left; background-color: #ffffff; "><strong><span style="font-size: medium; ">const_cast</span></strong><br />去掉类型的const或volatile属性。</p><div style="font-size: 12px; margin-top: 5px; margin-right: 0px; margin-bottom: 5px; margin-left: 0px; line-height: 19px; text-align: left; "><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: 'Courier New'; "><span style="line-height: 1.5; color: #008080; ">1</span> <span style="line-height: 1.5; color: #0000ff; ">struct</span><span style="line-height: 1.5; "> SA {<br /></span><span style="line-height: 1.5; color: #008080; ">2</span>  <span style="line-height: 1.5; color: #0000ff; ">int</span><span style="line-height: 1.5; "> i;<br /></span><span style="line-height: 1.5; color: #008080; ">3</span> <span style="line-height: 1.5; ">};<br /></span><span style="line-height: 1.5; color: #008080; ">4</span> <span style="line-height: 1.5; color: #0000ff; ">const</span><span style="line-height: 1.5; "> SA ra;<br /></span><span style="line-height: 1.5; color: #008080; ">5</span> <span style="line-height: 1.5; color: #008000; ">//</span><span style="line-height: 1.5; color: #008000; ">ra.i = 10; </span><span style="line-height: 1.5; color: #008000; ">//</span><span style="line-height: 1.5; color: #008000; ">直接修改const类型，编译错误</span><span style="line-height: 1.5; color: #008000; "><br /></span><span style="line-height: 1.5; color: #008080; ">6</span> <span style="line-height: 1.5; ">SA </span><span style="line-height: 1.5; ">&amp;</span><span style="line-height: 1.5; ">rb </span><span style="line-height: 1.5; ">=</span><span style="line-height: 1.5; "> const_cast</span><span style="line-height: 1.5; ">&lt;</span><span style="line-height: 1.5; ">SA</span><span style="line-height: 1.5; ">&amp;&gt;</span><span style="line-height: 1.5; ">(ra);<br /></span><span style="line-height: 1.5; color: #008080; ">7</span> <span style="line-height: 1.5; ">rb.i </span><span style="line-height: 1.5; ">=</span> <span style="line-height: 1.5; color: #800080; ">10</span><span style="line-height: 1.5; ">;</span></pre></div><p style="line-height: 19px; font-size: 13px; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; text-align: left; background-color: #ffffff; "><span style="font-size: medium; "><strong>static_cast</strong></span></p><p style="line-height: 19px; font-size: 13px; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; text-align: left; background-color: #ffffff; ">类似于C风格的强制转换。无条件转换，静态类型转换。用于：<br />1. 基类和子类之间转换：其中子类指针转换成父类指针是安全的；但父类指针转换成子类指针是不安全的。(基类和子类之间的动态类型转换建议用dynamic_cast)<br />2. 基本数据类型转换。enum, struct, int, char, float等。static_cast不能进行无关类型（如非基类和子类）指针之间的转换。<br />3. 把空指针转换成目标类型的空指针。<br />4. 把任何类型的表达式转换成void类型。<br />5. static_cast不能去掉类型的const、volitale属性(用const_cast)。</p><div style="font-size: 12px; margin-top: 5px; margin-right: 0px; margin-bottom: 5px; margin-left: 0px; line-height: 19px; text-align: left; "><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: 'Courier New'; "><span style="line-height: 1.5; color: #008080; ">1</span> <span style="line-height: 1.5; color: #0000ff; ">int</span><span style="line-height: 1.5; "> n </span><span style="line-height: 1.5; ">=</span> <span style="line-height: 1.5; color: #800080; ">6</span><span style="line-height: 1.5; ">;<br /></span><span style="line-height: 1.5; color: #008080; ">2</span> <span style="line-height: 1.5; color: #0000ff; ">double</span><span style="line-height: 1.5; "> d </span><span style="line-height: 1.5; ">=</span><span style="line-height: 1.5; "> static_cast</span><span style="line-height: 1.5; ">&lt;</span><span style="line-height: 1.5; color: #0000ff; ">double</span><span style="line-height: 1.5; ">&gt;</span><span style="line-height: 1.5; ">(n); </span><span style="line-height: 1.5; color: #008000; ">//</span><span style="line-height: 1.5; color: #008000; "> 基本类型转换</span><span style="line-height: 1.5; color: #008000; "><br /></span><span style="line-height: 1.5; color: #008080; ">3</span> <span style="line-height: 1.5; color: #0000ff; ">int</span> <span style="line-height: 1.5; ">*</span><span style="line-height: 1.5; ">pn </span><span style="line-height: 1.5; ">=</span> <span style="line-height: 1.5; ">&amp;</span><span style="line-height: 1.5; ">n;<br /></span><span style="line-height: 1.5; color: #008080; ">4</span> <span style="line-height: 1.5; color: #0000ff; ">double</span> <span style="line-height: 1.5; ">*</span><span style="line-height: 1.5; ">d </span><span style="line-height: 1.5; ">=</span><span style="line-height: 1.5; "> static_cast</span><span style="line-height: 1.5; ">&lt;</span><span style="line-height: 1.5; color: #0000ff; ">double</span> <span style="line-height: 1.5; ">*&gt;</span><span style="line-height: 1.5; ">(</span><span style="line-height: 1.5; ">&amp;</span><span style="line-height: 1.5; ">n) </span><span style="line-height: 1.5; color: #008000; ">//</span><span style="line-height: 1.5; color: #008000; ">无关类型指针转换，编译错误</span><span style="line-height: 1.5; color: #008000; "><br /></span><span style="line-height: 1.5; color: #008080; ">5</span> <span style="line-height: 1.5; color: #0000ff; ">void</span> <span style="line-height: 1.5; ">*</span><span style="line-height: 1.5; ">p </span><span style="line-height: 1.5; ">=</span><span style="line-height: 1.5; "> static_cast</span><span style="line-height: 1.5; ">&lt;</span><span style="line-height: 1.5; color: #0000ff; ">void</span> <span style="line-height: 1.5; ">*&gt;</span><span style="line-height: 1.5; ">(pn); </span><span style="line-height: 1.5; color: #008000; ">//</span><span style="line-height: 1.5; color: #008000; ">任意类型转换成void类型</span></pre></div><p style="line-height: 19px; font-size: 13px; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; text-align: left; background-color: #ffffff; "><strong><span style="font-size: medium; ">dynamic_cast</span></strong><br />有条件转换，动态类型转换，运行时类型安全检查(转换失败返回NULL)：<br />1. 安全的基类和子类之间转换。<br />2. 必须要有虚函数。<br />3. 相同基类不同子类之间的交叉转换。但结果是NULL。</p><div style="font-size: 12px; margin-top: 5px; margin-right: 0px; margin-bottom: 5px; margin-left: 0px; line-height: 19px; text-align: left; "><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: 'Courier New'; "><span style="line-height: 1.5; color: #008080; "> 1</span> <span style="line-height: 1.5; color: #0000ff; ">class</span><span style="line-height: 1.5; "> BaseClass {<br /></span><span style="line-height: 1.5; color: #008080; "> 2</span> <span style="line-height: 1.5; color: #0000ff; ">public</span><span style="line-height: 1.5; ">:<br /></span><span style="line-height: 1.5; color: #008080; "> 3</span> <span style="line-height: 1.5; color: #0000ff; ">int</span><span style="line-height: 1.5; "> m_iNum;<br /></span><span style="line-height: 1.5; color: #008080; "> 4</span> <span style="line-height: 1.5; color: #0000ff; ">virtual</span> <span style="line-height: 1.5; color: #0000ff; ">void</span><span style="line-height: 1.5; "> foo(){}; </span><span style="line-height: 1.5; color: #008000; ">//</span><span style="line-height: 1.5; color: #008000; ">基类必须有虚函数。保持多台特性才能使用dynamic_cast</span><span style="line-height: 1.5; color: #008000; "><br /></span><span style="line-height: 1.5; color: #008080; "> 5</span> <span style="line-height: 1.5; ">};<br /></span><span style="line-height: 1.5; color: #008080; "> 6</span> <span style="line-height: 1.5; "><br /></span><span style="line-height: 1.5; color: #008080; "> 7</span> <span style="line-height: 1.5; color: #0000ff; ">class</span><span style="line-height: 1.5; "> DerivedClass: </span><span style="line-height: 1.5; color: #0000ff; ">public</span><span style="line-height: 1.5; "> BaseClass {<br /></span><span style="line-height: 1.5; color: #008080; "> 8</span> <span style="line-height: 1.5; color: #0000ff; ">public</span><span style="line-height: 1.5; ">:<br /></span><span style="line-height: 1.5; color: #008080; "> 9</span> <span style="line-height: 1.5; color: #0000ff; ">char</span> <span style="line-height: 1.5; ">*</span><span style="line-height: 1.5; ">m_szName[</span><span style="line-height: 1.5; color: #800080; ">100</span><span style="line-height: 1.5; ">];<br /></span><span style="line-height: 1.5; color: #008080; ">10</span> <span style="line-height: 1.5; color: #0000ff; ">void</span><span style="line-height: 1.5; "> bar(){};<br /></span><span style="line-height: 1.5; color: #008080; ">11</span> <span style="line-height: 1.5; ">};<br /></span><span style="line-height: 1.5; color: #008080; ">12</span> <span style="line-height: 1.5; "><br /></span><span style="line-height: 1.5; color: #008080; ">13</span> <span style="line-height: 1.5; ">BaseClass</span><span style="line-height: 1.5; ">*</span><span style="line-height: 1.5; "> pb </span><span style="line-height: 1.5; ">=</span> <span style="line-height: 1.5; color: #0000ff; ">new</span><span style="line-height: 1.5; "> DerivedClass();<br /></span><span style="line-height: 1.5; color: #008080; ">14</span> <span style="line-height: 1.5; ">DerivedClass </span><span style="line-height: 1.5; ">*</span><span style="line-height: 1.5; ">pd1 </span><span style="line-height: 1.5; ">=</span><span style="line-height: 1.5; "> static_cast</span><span style="line-height: 1.5; ">&lt;</span><span style="line-height: 1.5; ">DerivedClass </span><span style="line-height: 1.5; ">*&gt;</span><span style="line-height: 1.5; ">(pb); </span><span style="line-height: 1.5; color: #008000; ">//</span><span style="line-height: 1.5; color: #008000; ">子类-&gt;父类，静态类型转换，正确但不推荐</span><span style="line-height: 1.5; color: #008000; "><br /></span><span style="line-height: 1.5; color: #008080; ">15</span> <span style="line-height: 1.5; ">DerivedClass </span><span style="line-height: 1.5; ">*</span><span style="line-height: 1.5; ">pd2 </span><span style="line-height: 1.5; ">=</span><span style="line-height: 1.5; "> dynamic_cast</span><span style="line-height: 1.5; ">&lt;</span><span style="line-height: 1.5; ">DerivedClass </span><span style="line-height: 1.5; ">*&gt;</span><span style="line-height: 1.5; ">(pb); </span><span style="line-height: 1.5; color: #008000; ">//</span><span style="line-height: 1.5; color: #008000; ">子类-&gt;父类，动态类型转换，正确</span><span style="line-height: 1.5; color: #008000; "><br /></span><span style="line-height: 1.5; color: #008080; ">16</span> <span style="line-height: 1.5; "><br /></span><span style="line-height: 1.5; color: #008080; ">17</span> <span style="line-height: 1.5; ">BaseClass</span><span style="line-height: 1.5; ">*</span><span style="line-height: 1.5; "> pb2 </span><span style="line-height: 1.5; ">=</span> <span style="line-height: 1.5; color: #0000ff; ">new</span><span style="line-height: 1.5; "> BaseClass();<br /></span><span style="line-height: 1.5; color: #008080; ">18</span> <span style="line-height: 1.5; ">DerivedClass </span><span style="line-height: 1.5; ">*</span><span style="line-height: 1.5; ">pd21 </span><span style="line-height: 1.5; ">=</span><span style="line-height: 1.5; "> static_cast</span><span style="line-height: 1.5; ">&lt;</span><span style="line-height: 1.5; ">DerivedClass </span><span style="line-height: 1.5; ">*&gt;</span><span style="line-height: 1.5; ">(pb2); </span><span style="line-height: 1.5; color: #008000; ">//</span><span style="line-height: 1.5; color: #008000; ">父类-&gt;子类，静态类型转换，危险！访问子类m_szName成员越界</span><span style="line-height: 1.5; color: #008000; "><br /></span><span style="line-height: 1.5; color: #008080; ">19</span> <span style="line-height: 1.5; ">DerivedClass </span><span style="line-height: 1.5; ">*</span><span style="line-height: 1.5; ">pd22 </span><span style="line-height: 1.5; ">=</span><span style="line-height: 1.5; "> dynamic_cast</span><span style="line-height: 1.5; ">&lt;</span><span style="line-height: 1.5; ">DerivedClass </span><span style="line-height: 1.5; ">*&gt;</span><span style="line-height: 1.5; ">(pb2); </span><span style="line-height: 1.5; color: #008000; ">//</span><span style="line-height: 1.5; color: #008000; ">父类-&gt;子类，动态类型转换，安全的。结果是NULL</span></pre></div><p style="line-height: 19px; font-size: 13px; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; text-align: left; background-color: #ffffff; "><strong><span style="font-size: medium; ">reinterpret_cast</span></strong><br />仅仅重新解释类型，但没有进行二进制的转换：<br />1. 转换的类型必须是一个指针、引用、算术类型、函数指针或者成员指针。<br />2. 在比特位级别上进行转换。它可以把一个指针转换成一个整数，也可以把一个整数转换成一个指针（先把一个指针转换成一个整数，在把该整数转换成原类型的指针，还可以得到原先的指针值）。但不能将非32bit的实例转成指针。<br />3. 最普通的用途就是在函数指针类型之间进行转换。<br />4. 很难保证移植性。</p><div style="font-size: 12px; margin-top: 5px; margin-right: 0px; margin-bottom: 5px; margin-left: 0px; line-height: 19px; text-align: left; "><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: 'Courier New'; "><span style="line-height: 1.5; color: #008080; ">1</span> <span style="line-height: 1.5; color: #0000ff; ">int</span><span style="line-height: 1.5; "> doSomething(){</span><span style="line-height: 1.5; color: #0000ff; ">return</span> <span style="line-height: 1.5; color: #800080; ">0</span><span style="line-height: 1.5; ">;};<br /></span><span style="line-height: 1.5; color: #008080; ">2</span> <span style="line-height: 1.5; ">typedef </span><span style="line-height: 1.5; color: #0000ff; ">void</span><span style="line-height: 1.5; ">(</span><span style="line-height: 1.5; ">*</span><span style="line-height: 1.5; ">FuncPtr)(); </span><span style="line-height: 1.5; color: #008000; ">//</span><span style="line-height: 1.5; color: #008000; ">FuncPtr is 一个指向函数的指针，该函数没有参数，返回值类型为 void</span><span style="line-height: 1.5; color: #008000; "><br /></span><span style="line-height: 1.5; color: #008080; ">3</span> <span style="line-height: 1.5; ">FuncPtr funcPtrArray[</span><span style="line-height: 1.5; color: #800080; ">10</span><span style="line-height: 1.5; ">]; </span><span style="line-height: 1.5; color: #008000; ">//</span><span style="line-height: 1.5; color: #008000; ">10个FuncPtrs指针的数组 让我们假设你希望（因为某些莫名其妙的原因）把一个指向下面函数的指针存入funcPtrArray数组：</span><span style="line-height: 1.5; color: #008000; "><br /></span><span style="line-height: 1.5; color: #008080; ">4</span> <span style="line-height: 1.5; "><br /></span><span style="line-height: 1.5; color: #008080; ">5</span> <span style="line-height: 1.5; ">funcPtrArray[</span><span style="line-height: 1.5; color: #800080; ">0</span><span style="line-height: 1.5; ">] </span><span style="line-height: 1.5; ">=</span> <span style="line-height: 1.5; ">&amp;</span><span style="line-height: 1.5; ">doSomething;</span><span style="line-height: 1.5; color: #008000; ">//</span><span style="line-height: 1.5; color: #008000; "> 编译错误！类型不匹配，reinterpret_cast可以让编译器以你的方法去看待它们：funcPtrArray</span><span style="line-height: 1.5; color: #008000; "><br /></span><span style="line-height: 1.5; color: #008080; ">6</span> <span style="line-height: 1.5; ">funcPtrArray[</span><span style="line-height: 1.5; color: #800080; ">0</span><span style="line-height: 1.5; ">] </span><span style="line-height: 1.5; ">=</span><span style="line-height: 1.5; "> reinterpret_cast</span><span style="line-height: 1.5; ">&lt;</span><span style="line-height: 1.5; ">FuncPtr</span><span style="line-height: 1.5; ">&gt;</span><span style="line-height: 1.5; ">(</span><span style="line-height: 1.5; ">&amp;</span><span style="line-height: 1.5; ">doSomething); </span><span style="line-height: 1.5; color: #008000; ">//</span><span style="line-height: 1.5; color: #008000; ">不同函数指针类型之间进行转换</span></pre></div><p style="line-height: 19px; font-size: 13px; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; text-align: left; background-color: #ffffff; "><strong><span style="font-size: medium; ">总结</span></strong><br />去const属性用const_cast。<br />基本类型转换用static_cast。<br />多态类之间的类型转换用daynamic_cast。<br />不同类型的指针类型转换用reinterpret_cast。</p><img src ="http://www.cppblog.com/mysileng/aggbug/195650.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-11-25 17:33 <a href="http://www.cppblog.com/mysileng/archive/2012/11/25/195650.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++ cout 格式化输出(转)</title><link>http://www.cppblog.com/mysileng/archive/2012/11/19/195358.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Mon, 19 Nov 2012 06:42:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/11/19/195358.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/195358.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/11/19/195358.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/195358.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/195358.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="font-family: Arial; line-height: 26px; background-color: #ffffff; "><h2 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><a id="cb_post_title_url" href="http://www.cnblogs.com/walfud/articles/2047096.html" style="color: #ca0000; text-decoration: none; ">cout 格式化输出</a></h2><div id="cnblogs_post_body"><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><strong><br />将 cout 的 flag 保存到变量</strong>, 以便修改后的恢复</p><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #000000; ">    ostream::fmtflags old </span><span style="color: #000000; ">=</span><span style="color: #000000; "> cout.flag() ;        </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 无参将返回当前 flag 值</span><span style="color: #008000; ">
</span><span style="color: #000000; ">    cout.flag(old) ;                            </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 恢复到原先保存的值</span></div></pre><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a style="color: #ca0000; text-decoration: none; ">复制代码</a></span></div></div><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><strong><br />将 bool 值以 literals 输出</strong></p><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">numeric : </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #0000ff; ">true</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; "> or </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #0000ff; ">false</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;              </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 1 or 0</span><span style="color: #008000; ">
</span><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">literals : </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">boolalpha </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #0000ff; ">true</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; "> or </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #0000ff; ">false</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ; </span><span style="color: #008000; ">//</span><span style="color: #008000; "> true or false</span><span style="color: #008000; ">
</span><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">literals : </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">boolalpha </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">0</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;                     </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 0    原因: 0 在cout中不等价于 false</span></div></pre><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a style="color: #ca0000; text-decoration: none; ">复制代码</a></span></div></div><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">一旦我们使用 boolalpha 将改变 cout 对 bool 值的输出格式. 此后的 cout 都会将 bool 输出为 literals.</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><strong><br />将 bool 值以 numeric 输出</strong></p><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #000000; ">    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">numeric : </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">noboolalpha </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #0000ff; ">true</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; "> or </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #0000ff; ">false</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;</span><span style="color: #008000; ">//</span><span style="color: #008000; "> 1 or 0</span></div></pre></div><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">从此以后, cout 对 bool 值的输出将恢复 numeric 格式</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><strong><br />指定 Integral Values 的 Base</strong></p><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #000000; ">    </span><span style="color: #0000ff; ">const</span><span style="color: #000000; "> </span><span style="color: #0000ff; ">int</span><span style="color: #000000; "> ival </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #800080; ">17</span><span style="color: #000000; "> ;        </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 'ival' is constant, so value never change</span><span style="color: #008000; ">
</span><span style="color: #000000; ">    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">oct : </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">oct </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">ival </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;        </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 21 : 8 进制</span><span style="color: #008000; ">
</span><span style="color: #000000; ">    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">dec : </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">dec </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">ival </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;        </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 17 : 10 进制</span><span style="color: #008000; ">
</span><span style="color: #000000; ">    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">hex : </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">hex </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">ival </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;        </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 11 : 16 进制</span><span style="color: #008000; ">
</span><span style="color: #000000; ">    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">hex : </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">hex </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">17.01</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;        </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 17.01 : 不受影响</span></div></pre><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a style="color: #ca0000; text-decoration: none; ">复制代码</a></span></div></div>如 boolalpha 一样, oct, dec, hex 也是 persistent. 一旦改变, 将影响后续的输出格式.<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><strong><br />显示表明 Integer Values 的 Base</strong></p><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #000000; ">    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">showbase ;                            </span><span style="color: #008000; ">//</span><span style="color: #008000; "> Show base when printing integral values</span><span style="color: #008000; ">
</span><span style="color: #000000; ">    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">oct : </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">oct </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">ival </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;        </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 21 : 8 进制</span><span style="color: #008000; ">
</span><span style="color: #000000; ">    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">dec : </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">dec </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">ival </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;        </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 017 : 10 进制</span><span style="color: #008000; ">
</span><span style="color: #000000; ">    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">hex : </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">hex </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">ival </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;        </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 0x11 : 16 进制</span><span style="color: #008000; ">
</span><span style="color: #000000; ">    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">hex : </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">hex </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">17.01</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;        </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 17.01 : 不受影响</span><span style="color: #008000; ">
</span><span style="color: #000000; ">    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">noshowbase ;                            </span><span style="color: #008000; ">//</span><span style="color: #008000; "> Reset state of the stream</span></div></pre><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a style="color: #ca0000; text-decoration: none; ">复制代码</a></span></div></div><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">若想<strong>改变16进制字母的大小</strong>, 可以结合&nbsp;<strong>uppercase</strong>/<strong>nouppercase</strong></p><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #000000; ">    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">showbase </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">uppercase ;
    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">hex : </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">hex </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">15</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;            </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 0XF 大写形式</span><span style="color: #008000; ">
</span><span style="color: #000000; ">    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">nouppercase ;
    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">hex : </span><span style="color: #800000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">hex </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">15</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;            </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 0xf 小写形式</span></div></pre><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a style="color: #ca0000; text-decoration: none; ">复制代码</a></span></div></div><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><span></span>showbase 与 noshowbase 的作用周期也是 persistent</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><strong><br />对于 float/double 型, 有三种格式化控制</strong></p><blockquote>一:&nbsp;<strong>输出精度 precision</strong>&nbsp;: by default is 6<span>&nbsp;</span>pricision<br />&nbsp; &nbsp;控制了至多一共会输出多少个数字.&nbsp;<span>&nbsp;<br /></span>&nbsp; &nbsp;当要输出的数字多余指定的值时, 将发生 四舍五入(rounded);&nbsp;<span>&nbsp;<br /></span>&nbsp; &nbsp;当要输出的数字少于指定的值时, 则实际输出的数字个数将少于指定值.<br /><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #008000; ">//</span><span style="color: #008000; "> cout.pricision(4) ;                         </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 等价于 cout &lt;&lt;setprecision(4) ;</span><span style="color: #008000; ">
</span><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">setprecision(</span><span style="color: #800080; ">4</span><span style="color: #000000; ">) </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">12.345678</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;    </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 12.35  rounded!</span><span style="color: #008000; ">
</span><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">setprecision(</span><span style="color: #800080; ">10</span><span style="color: #000000; ">) </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">12.345678</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;   </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 12.345678 其实内部发生了 rounded, 而结果正好进位, 与原值相同</span><span style="color: #008000; ">
</span><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">cout.precision() </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;               </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 输出当前精度</span></div></pre><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a style="color: #ca0000; text-decoration: none; ">复制代码</a></span></div></div></blockquote><blockquote>&nbsp;二:&nbsp;<strong>表现形式 notation</strong>&nbsp;:<em>&nbsp;'very large and very small values are printed using&nbsp;<strong>scientific</strong>&nbsp;notation. other values use&nbsp;<strong>fixed</strong>&nbsp;decimal.'</em><span><br /></span>&nbsp; &nbsp;notation 控制了输出的形式 : 科学计数法(scientific) 和 定点小数(fixed)<br /><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #0000ff; ">float</span><span style="color: #000000; "> f </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #800080; ">101</span><span style="color: #000000; "> </span><span style="color: #000000; ">/</span><span style="color: #000000; "> </span><span style="color: #800080; ">6.0</span><span style="color: #000000; "> ;
cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #0000ff; ">fixed</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">f </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;           </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 16.83334 : 小数点后共6位</span><span style="color: #008000; ">
</span><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">scientific </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">f </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;      </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 1.683333e+001 : 小数点后共6位</span></div></pre><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a style="color: #ca0000; text-decoration: none; ">复制代码</a></span></div></div>&nbsp;<span>&nbsp;</span>恢复到初始状态<br /><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #000000; ">cout.unsetf(ostream::floatfield) ;  </span><span style="color: #008000; ">//</span><span style="color: #008000; "> Retrieve to default handling for notation</span><span style="color: #008000; ">
</span><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">f </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;                   </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 16.8333 : 所有数字共6位</span></div></pre><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a style="color: #ca0000; text-decoration: none; ">复制代码</a></span></div></div></blockquote><blockquote>&nbsp;三:&nbsp;<strong>输出十进制浮点</strong><em>&nbsp;'By default, when the fractional part of a floating-point value is 0, the decimal point is not displayed. The&nbsp;<strong>showpoint</strong>&nbsp;manipulator forces the decimal point ot be printed.'</em><br /><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">10.0</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;                </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 10</span><span style="color: #008000; ">
</span><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">showpoint </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">10.0</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;    </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 10.0000</span><span style="color: #008000; ">
</span><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">noshowpoint </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;         </span><span style="color: #008000; ">//</span><span style="color: #008000; "> Revert to default handling of decimal </span><span style="font-family: verdana, Arial, Helvetica, sans-serif; color: #333333; font-size: 14px; line-height: 15px; white-space: normal; ">&nbsp;</span></div></pre><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a style="color: #ca0000; text-decoration: none; ">复制代码</a></span></div></div></blockquote><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><strong><br />输出填充&nbsp;</strong>Padding the Output</p><blockquote><em><strong>&nbsp; &nbsp;setw&nbsp;</strong>to specify the&nbsp;<span style="color: #ff0000; ">minimum space</span>&nbsp;for the&nbsp;<span style="color: #ff0000; ">next&nbsp;</span>numeric or string value.<br /></em><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">setw(</span><span style="color: #800080; ">10</span><span style="color: #000000; ">) </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">12.3</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;     </span><span style="color: #008000; ">//</span><span style="color: #008000; "> ______12.3</span><span style="color: #008000; ">
</span><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">setw(</span><span style="color: #800080; ">10</span><span style="color: #000000; ">) </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">12</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">3</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;   </span><span style="color: #008000; ">//</span><span style="color: #008000; "> ________123</span><span style="color: #008000; ">
</span><span style="color: #000000; ">
cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">setw(</span><span style="color: #800080; ">3</span><span style="color: #000000; ">) </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">12.345</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;    </span><span style="color: #008000; ">//</span><span style="color: #008000; "> If the <strong>total output</strong> is more than 3, it can be extended</span></div></pre><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a style="color: #ca0000; text-decoration: none; ">复制代码</a></span></div></div>&nbsp;<img src="http://pic002.cnblogs.com/images/2011/282341/2011051520540965.png" alt="" style="border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; " /></blockquote><blockquote><em><strong>&nbsp; &nbsp;left&nbsp;</strong>to left-justify the output.<br /></em><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #000000; ">    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">left ;                                 </span><span style="color: #008000; ">//</span><span style="color: #008000; "> left-justify</span><span style="color: #008000; ">
</span><span style="color: #000000; ">    cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">setw(</span><span style="color: #800080; ">5</span><span style="color: #000000; ">) </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">12</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">setw(</span><span style="color: #800080; ">5</span><span style="color: #000000; ">) </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">34</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;   </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 12___34___</span></div></pre><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a style="color: #ca0000; text-decoration: none; ">复制代码</a></span></div></div>&nbsp;<img src="http://pic002.cnblogs.com/images/2011/282341/2011051520570056.png" alt="" style="border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; " /></blockquote><blockquote><em>&nbsp; &nbsp;right to right-justify the output. Output is right-justified bu default.<br /></em><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">right ;                                </span><span style="color: #008000; ">//</span><span style="color: #008000; "> By default</span><span style="color: #008000; ">
</span><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">setw(</span><span style="color: #800080; ">5</span><span style="color: #000000; ">) </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">12</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">setw(</span><span style="color: #800080; ">5</span><span style="color: #000000; ">) </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">34</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;   </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 12___34___</span></div></pre><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a style="color: #ca0000; text-decoration: none; ">复制代码</a></span></div></div>&nbsp;<img src="http://pic002.cnblogs.com/images/2011/282341/2011051520582023.png" alt="" style="border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; " /></blockquote><blockquote><em>&nbsp; &nbsp;<strong>internal</strong>&nbsp;controls placement of the sign on negative value.&nbsp;<strong>internal</strong>&nbsp;left-justifies the sign and right-justifies the value, padding any intervening space with blanks.(if&nbsp;<strong>setfill</strong>&nbsp;not set)&nbsp;<br /></em><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #0000ff; ">internal</span><span style="color: #000000; "> ;               </span><span style="color: #008000; ">//</span><span style="color: #008000; "> By default</span><span style="color: #008000; ">
</span><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">setw(</span><span style="color: #800080; ">5</span><span style="color: #000000; ">) </span><span style="color: #000000; ">&lt;&lt;-</span><span style="color: #800080; ">12</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;   </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 12___34___</span></div></pre><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a style="color: #ca0000; text-decoration: none; ">复制代码</a></span></div></div><img src="http://pic002.cnblogs.com/images/2011/282341/2011051521040668.png" alt="" style="border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; " />&nbsp;</blockquote><blockquote>&nbsp;&nbsp;<em>&nbsp;<strong>setfill</strong>&nbsp;lets us specify an alternative character to use when padding the output. By default, the value is a space.<br /></em><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">setfill(</span><span style="color: #800000; ">'</span><span style="color: #800000; ">*</span><span style="color: #800000; ">'</span><span style="color: #000000; ">) ;          </span><span style="color: #008000; ">//</span><span style="color: #008000; "> By default</span><span style="color: #008000; ">
</span><span style="color: #000000; ">cout </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">setw(</span><span style="color: #800080; ">5</span><span style="color: #000000; ">) </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #800080; ">12</span><span style="color: #000000; "> </span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">endl ;   </span><span style="color: #008000; ">//</span><span style="color: #008000; "> 12___34___</span></div></pre><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a style="color: #ca0000; text-decoration: none; ">复制代码</a></span></div></div></blockquote><blockquote><img src="http://pic002.cnblogs.com/images/2011/282341/2011051521054047.png" alt="" style="border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; " />&nbsp;</blockquote><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">Header Files</p><blockquote>&nbsp; &nbsp;Manipulators Defined in &lt;<span style="color: #0000ff; "><em>iomanip</em></span>&gt;<span style="color: #0000ff; "><em><br /></em></span><div class="cnblogs_code"><pre style="white-space: pre-wrap; word-wrap: break-word; "><div><span style="color: #000000; ">setfill(</span><span style="color: #0000ff; ">char</span><span style="color: #000000; "> ch)        Fill whitespace with </span><span style="color: #800000; ">'</span><span style="color: #800000; ">ch</span><span style="color: #800000; ">'</span><span style="color: #000000; ">
setprecision(</span><span style="color: #0000ff; ">int</span><span style="color: #000000; "> n)     Set floating</span><span style="color: #000000; ">-</span><span style="color: #000000; ">point precision to </span><span style="color: #800000; ">'</span><span style="color: #800000; ">n</span><span style="color: #800000; ">'</span><span style="color: #000000; ">
setw(</span><span style="color: #0000ff; ">int</span><span style="color: #000000; "> w)             Read or write value to </span><span style="color: #800000; ">'</span><span style="color: #800000; ">w</span><span style="color: #800000; ">'</span><span style="color: #000000; "> characters
setbase(</span><span style="color: #0000ff; ">int</span><span style="color: #000000; "> b)          Output integers </span><span style="color: #0000ff; ">in</span><span style="color: #000000; "> </span><span style="color: #0000ff; ">base</span><span style="color: #000000; "> </span><span style="color: #800000; ">'</span><span style="color: #800000; ">b</span><span style="color: #800000; ">'</span><span style="color: #000000; ">(only </span><span style="color: #800000; ">'</span><span style="color: #800000; ">b</span><span style="color: #800000; ">'</span><span style="color: #000000; "> </span><span style="color: #0000ff; ">is</span><span style="color: #000000; "> </span><span style="color: #800080; ">8</span><span style="color: #000000; ">/</span><span style="color: #800080; ">10</span><span style="color: #000000; ">/</span><span style="color: #800080; ">16</span><span style="color: #000000; "> could the function work)</span></div></pre><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a style="color: #ca0000; text-decoration: none; ">复制代码</a></span></div></div></blockquote></div></span><img src ="http://www.cppblog.com/mysileng/aggbug/195358.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-11-19 14:42 <a href="http://www.cppblog.com/mysileng/archive/2012/11/19/195358.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++内存泄露机制(转)</title><link>http://www.cppblog.com/mysileng/archive/2012/11/06/194769.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Tue, 06 Nov 2012 13:04:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/11/06/194769.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/194769.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/11/06/194769.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/194769.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/194769.html</trackback:ping><description><![CDATA[<span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">对于一个c/c++程序员来说，内存泄漏是一个常见的也是令人头疼的问题。已经有许多技术被研究出来以应对这个问题，比如 Smart Pointer，Garbage Collection等。Smart Pointer技术比较成熟，STL中已经包含支持Smart Pointer的class，但是它的使用似乎并不广泛，而且它也不能解决所有的问题；Garbage Collection技术在</span><a href="http://dev.yesky.com/devjava/" target="_blank" style="color: #ff9900; text-decoration: none; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">Java</a><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">中已经比较成熟，但是在c/c++领域的发展并不顺畅，虽然很早就有人思考在C++中也加入GC的支持。现实世界就是这样的，作为一个c/c++程序员，内存泄漏是你心中永远的痛。不过好在现在有许多工具能够帮助我们验证内存泄漏的存在，找出发生问题的代码。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　</span><strong style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">内存泄漏的定义</strong>&nbsp;<br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　 　一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的，大小任意的（内存块的大小可以在程序运行期决定），使用完后必须显示释放的内 存。应用程序一般使用malloc，realloc，new等函数从堆中分配到一块内存，使用完后，程序必须负责相应的调用free或delete释放该 内存块，否则，这块内存就不能被再次使用，我们就说这块内存泄漏了。以下这段小程序演示了堆内存发生泄漏的情形：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>void MyFunction(int nSize)<br />{<br />　char* p= new char[nSize];<br />　if( !GetStringFrom( p, nSize ) ){<br />　　MessageBox(&#8220;Error&#8221;);<br />　　return;<br />　}<br />　&#8230;//using the string pointed by p;<br />　delete p;<br />}</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　例一</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　当函数GetStringFrom()返回零的时候，指针p指向的内存就不会被释放。这是一种常见的发生内存泄漏的情形。程序在入口处分配内存，在出口处释放内存，但是c函数可以在任何地方退出，所以一旦有某个出口处没有释放应该释放的内存，就会发生内存泄漏。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　 　广义的说，内存泄漏不仅仅包含堆内存的泄漏，还包含系统资源的泄漏(resource leak)，比如核心态HANDLE，GDI Object，SOCKET， Interface等，从根本上说这些由操作系统分配的对象也消耗内存，如果这些对象发生泄漏最终也会导致内存的泄漏。而且，某些对象消耗的是核心态内 存，这些对象严重泄漏时会导致整个操作系统不稳定。所以相比之下，系统资源的泄漏比堆内存的泄漏更为严重。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　GDI Object的泄漏是一种常见的资源泄漏：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>void CMyView::OnPaint( CDC* pDC )<br />{<br />　CBitmap bmp;<br />　CBitmap* pOldBmp;<br />　bmp.LoadBitmap(IDB_MYBMP);<br />　pOldBmp = pDC-&gt;SelectObject( &amp;bmp );<br />　&#8230;<br />　if( Something() ){<br />　　return;<br />　}<br />　pDC-&gt;SelectObject( pOldBmp );<br />　return;<br />}</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　例二</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　 　当函数Something()返回非零的时候，程序在退出前没有把pOldBmp选回pDC中，这会导致pOldBmp指向的HBITMAP对象发生泄 漏。这个程序如果长时间的运行，可能会导致整个系统花屏。这种问题在Win9x下比较容易暴露出来，因为Win9x的GDI堆比Win2k或NT的要小很 多。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　内存泄漏的发生方式：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　以发生的方式来分类，内存泄漏可以分为4类：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　1. 常发性内存泄漏。发生内存泄漏的代码会被多次执行到，每次被执行的时候都会导致一块内存泄漏。比如例二，如果Something()函数一直返回True，那么pOldBmp指向的HBITMAP对象总是发生泄漏。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　 　2. 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。比如例二，如果Something()函数只有在特定环境下才返回 True，那么pOldBmp指向的HBITMAP对象并不总是发生泄漏。常发性和偶发性是相对的。对于特定的环境，偶发性的也许就变成了常发性的。所以 测试环境和测试方法对检测内存泄漏至关重要。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　3. 一次性内存泄漏。发生内存泄漏的代码只会被执行一次，或者由于算法上的缺陷，导致总会有一块仅且一块内存发生泄漏。比如，在类的构造函数中分配内存，在析 构函数中却没有释放该内存，但是因为这个类是一个Singleton，所以内存泄漏只会发生一次。另一个例子：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>char* g_lpszFileName = NULL;<br /><br />void SetFileName( const char* lpcszFileName )<br />{<br />　if( g_lpszFileName ){<br />　　free( g_lpszFileName );<br />　}<br />　g_lpszFileName = strdup( lpcszFileName );<br />}</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　例三</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　如果程序在结束的时候没有释放g_lpszFileName指向的字符串，那么，即使多次调用SetFileName()，总会有一块内存，而且仅有一块内存发生泄漏。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　 　4. 隐式内存泄漏。程序在运行过程中不停的分配内存，但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏，因为最终程序释放了所有申请的内存。但 是对于一个服务器程序，需要运行几天，几周甚至几个月，不及时释放内存也可能导致最终耗尽系统的所有内存。所以，我们称这类内存泄漏为隐式内存泄漏。举一 个例子：&nbsp;</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>class Connection<br />{<br />　public:<br />　　Connection( SOCKET s);<br />　　~Connection();<br />　　&#8230;<br />　private:<br />　　SOCKET _socket;<br />　　&#8230;<br />};<br /><br />class ConnectionManager<br />{<br />　public:<br />　　ConnectionManager(){}<br />　　~ConnectionManager(){<br />　　　list::iterator it;<br />　　　for( it = _connlist.begin(); it != _connlist.end(); ++it ){<br />　　　　delete （*it）;<br />　　　}<br />　　　_connlist.clear();<br />　　}<br />　　void OnClientConnected( SOCKET s ){<br />　　　Connection* p = new Connection(s);<br />　　　_connlist.push_back(p);<br />　　}<br />　　void OnClientDisconnected( Connection* pconn ){<br />　　　_connlist.remove( pconn );<br />　　　delete pconn;<br />　　}<br />　private:<br />　　list _connlist;<br />};</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　例四</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　 　假设在Client从Server端断开后，Server并没有呼叫OnClientDisconnected()函数，那么代表那次连接的 Connection对象就不会被及时的删除（在Server程序退出的时候，所有Connection对象会在ConnectionManager的析 构函数里被删除）。当不断的有连接建立、断开时隐式内存泄漏就发生了。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　从用户使用程序的角度来看，内存泄漏本身不会产生什么危害，作 为一般的用户，根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积，这会最终消耗尽系统所有的内存。从这个角度来说，一次性内存泄漏并没有什么危 害，因为它不会堆积，而隐式内存泄漏危害性则非常大，因为较之于常发性和偶发性内存泄漏它更难被检测到。&nbsp;</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><strong style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">检测内存泄漏</strong><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　检测内存泄漏的关键是要能截获住对分配内存和释放内存的函数的调用。截获住这两个函数，我们就能跟踪每一 块内存的生命周期，比如，每当成功的分配一块内存后，就把它的指针加入一个全局的list中；每当释放一块内存，再把它的指针从list中删除。这样，当 程序结束的时候，list中剩余的指针就是指向那些没有被释放的内存。这里只是简单的描述了检测内存泄漏的基本原理，详细的算法可以参见Steve Maguire的&lt;&lt;Writing Solid Code&gt;&gt;。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　如果要检测堆内存的泄漏，那么需要截获住 malloc/realloc/free和new/delete就可以了（其实new/delete最终也是用malloc/free的，所以只要截获前 面一组即可）。对于其他的泄漏，可以采用类似的方法，截获住相应的分配和释放函数。比如，要检测BSTR的泄漏，就需要截获 SysAllocString/SysFreeString；要检测HMENU的泄漏，就需要截获CreateMenu/ DestroyMenu。（有的资源的分配函数有多个，释放函数只有一个，比如，SysAllocStringLen也可以用来分配BSTR，这时就需要 截获多个分配函数）</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　在Windows平台下，检测内存泄漏的工具常用的一般有三种，MS C-Runtime Library内建的检测功能；外挂式的检测工具，诸如，Purify，BoundsChecker等；利用Windows NT自带的Performance Monitor。这三种工具各有优缺点，MS C-Runtime Library虽然功能上较之外挂式的工具要弱，但是它是免费的；Performance Monitor虽然无法标示出发生问题的代码，但是它能检测出隐式的内存泄漏的存在，这是其他两类工具无能为力的地方。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　以下我们详细讨论这三种检测工具：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　VC下内存泄漏的检测方法</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　用MFC开发的应用程序，在DEBUG版模式下编译后，都会自动加入内存泄漏的检测代码。在程序结束后，如果发生了内存泄漏，在Debug窗口中会显示出所有发生泄漏的内存块的信息，以下两行显示了一块被泄漏的内存块的信息：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">E:/TestMemLeak/TestDlg.cpp(70) : {59} normal block at 0x00881710, 200 bytes long.</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">Data: &lt;abcdefghijklmnop&gt; 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　 　第一行显示该内存块由TestDlg.cpp文件，第70行代码分配，地址在0x00881710，大小为200字节，{59}是指调用内存分配函数的 Request Order，关于它的详细信息可以参见MSDN中_CrtSetBreakAlloc()的帮助。第二行显示该内存块前16个字节的内容，尖括号内是以 ASCII方式显示，接着的是以16进制方式显示。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　一般大家都误以为这些内存泄漏的检测功能是由MFC提供的，其实不然。MFC只是 封装和利用了MS C-Runtime Library的Debug Function。非MFC程序也可以利用MS C-Runtime Library的Debug Function加入内存泄漏的检测功能。MS C-Runtime Library在实现malloc/free，strdup等函数时已经内建了内存泄漏的检测功能。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　注意观察一下由MFC Application Wizard生成的项目，在每一个cpp文件的头部都有这样一段宏定义：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>#ifdef _DEBUG<br />#define new DEBUG_NEW<br />#undef THIS_FILE<br />static char THIS_FILE[] = __FILE__;<br />#endif</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　有了这样的定义，在编译DEBUG版时，出现在这个cpp文件中的所有new都被替换成DEBUG_NEW了。那么DEBUG_NEW是什么呢？DEBUG_NEW也是一个宏，以下摘自afx.h，1632行</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>#define DEBUG_NEW new(THIS_FILE, __LINE__)</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　所以如果有这样一行代码：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>char* p = new char[200];</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　经过宏替换就变成了：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>char* p = new( THIS_FILE, __LINE__)char[200];</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　根据C++的标准，对于以上的new的使用方法，编译器会去找这样定义的operator new：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>void* operator new(size_t, LPCSTR, int)</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　我们在afxmem.cpp 63行找到了一个这样的operator new 的实现</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>void* AFX_CDECL operator new(size_t nSize, LPCSTR lpszFileName, int nLine)<br />{<br />　return ::operator new(nSize, _NORMAL_BLOCK, lpszFileName, nLine);<br />}<br /><br />void* __cdecl operator new(size_t nSize, int nType, LPCSTR lpszFileName, int nLine)<br />{<br />　&#8230;<br />　pResult = _malloc_dbg(nSize, nType, lpszFileName, nLine);<br />　if (pResult != NULL)<br />　　return pResult;<br />　&#8230;<br />}</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　 　第二个operator new函数比较长，为了简单期间，我只摘录了部分。很显然最后的内存分配还是通过_malloc_dbg函数实现的，这个函数属于MS C-Runtime Library 的Debug Function。这个函数不但要求传入内存的大小，另外还有文件名和行号两个参数。文件名和行号就是用来记录此次分配是由哪一段代码造成的。如果这块内 存在程序结束之前没有被释放，那么这些信息就会输出到Debug窗口里。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　这里顺便提一下THIS_FILE，__FILE和 __LINE__。__FILE__和__LINE__都是编译器定义的宏。当碰到__FILE__时，编译器会把__FILE__替换成一个字符串，这 个字符串就是当前在编译的文件的路径名。当碰到__LINE__时，编译器会把__LINE__替换成一个数字，这个数字就是当前这行代码的行号。在 DEBUG_NEW的定义中没有直接使用__FILE__，而是用了THIS_FILE，其目的是为了减小目标文件的大小。假设在某个cpp文件中有 100处使用了new，如果直接使用__FILE__，那编译器会产生100个常量字符串，这100个字符串都是飧?/SPAN&gt;cpp文件的路径 名，显然十分冗余。如果使用THIS_FILE，编译器只会产生一个常量字符串，那100处new的调用使用的都是指向常量字符串的指针。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　 　再次观察一下由MFC Application Wizard生成的项目，我们会发现在cpp文件中只对new做了映射，如果你在程序中直接使用malloc函数分配内存，调用malloc的文件名和行 号是不会被记录下来的。如果这块内存发生了泄漏，MS C-Runtime Library仍然能检测到，但是当输出这块内存块的信息，不会包含分配它的的文件名和行号。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　要在非MFC程序中打开内存泄漏的检测功能非常容易，你只要在程序的入口处加入以下几行代码：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );<br /><br />tmpFlag |= _CRTDBG_LEAK_CHECK_DF;<br /><br />_CrtSetDbgFlag( tmpFlag );</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　这样，在程序结束的时候，也就是winmain，main或dllmain函数返回之后，如果还有内存块没有释放，它们的信息会被</span><a href="http://oa.yesky.com/" target="_blank" style="color: #ff9900; text-decoration: none; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">打印</a><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">到Debug窗口里。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　如果你试着创建了一个非MFC应用程序，而且在程序的入口处加入了以上代码，并且故意在程序中不释放某些内存块，你会在Debug窗口里看到以下的信息：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>{47} normal block at 0x00C91C90, 200 bytes long.<br /><br />Data: &lt; &gt; 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　内存泄漏的确检测到了，但是和上面MFC程序的例子相比，缺少了文件名和行号。对于一个比较大的程序，没有这些信息，解决问题将变得十分困难。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　为了能够知道泄漏的内存块是在哪里分配的，你需要实现类似MFC的映射功能，把new，maolloc等函数映射到_malloc_dbg函数上。这里我不再赘述，你可以参考MFC的源代码。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　 　由于Debug Function实现在MS C-RuntimeLibrary中，所以它只能检测到堆内存的泄漏，而且只限于malloc，realloc或strdup等分配的内存，而那些系统资 源，比如HANDLE，GDI Object，或是不通过C-Runtime Library分配的内存，比如VARIANT，BSTR的泄漏，它是无法检测到的，这是这种检测法的一个重大的局限性。另外，为了能记录内存块是在哪里 分配的，源代码必须相应的配合，这在调试一些老的程序非常麻烦，毕竟修改源代码不是一件省心的事，这是这种检测法的另一个局限性。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　对 于开发一个大型的程序，MS C-Runtime Library提供的检测功能是远远不够的。接下来我们就看看外挂式的检测工具。我用的比较多的是BoundsChecker，一则因为它的功能比较全 面，更重要的是它的稳定性。这类工具如果不稳定，反而会忙里添乱。到底是出自鼎鼎大名的NuMega，我用下来基本上没有什么大问题。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;使用BoundsChecker检测内存泄漏：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　BoundsChecker采用一种被称为 Code Injection的技术，来截获对分配内存和释放内存的函数的调用。简单地说，当你的程序开始运行时，BoundsChecker的DLL被自动载入进 程的地址空间（这可以通过system-level的Hook实现），然后它会修改进程中对内存分配和释放的函数调用，让这些调用首先转入它的代码，然后 再执行原来的代码。BoundsChecker在做这些动作的时，无须修改被调试程序的源代码或工程配置文件，这使得使用它非常的简便、直接。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　这里我们以malloc函数为例，截获其他的函数方法与此类似。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　需要被截获的函数可能在DLL中，也可能在程序的代码里。比如，如果静态连结C-Runtime Library，那么malloc函数的代码会被连结到程序里。为了截获住对这类函数的调用，BoundsChecker会动态修改这些函数的指令。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　以下两段汇编代码，一段没有BoundsChecker介入，另一段则有BoundsChecker的介入：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>126: _CRTIMP void * __cdecl malloc (<br />127: size_t nSize<br />128: )<br />129: {<br /><br />00403C10 push ebp<br />00403C11 mov ebp,esp<br />130: return _nh_malloc_dbg(nSize, _newmode, _NORMAL_BLOCK, NULL, 0);<br />00403C13 push 0<br />00403C15 push 0<br />00403C17 push 1<br />00403C19 mov eax,[__newmode (0042376c)]<br />00403C1E push eax<br />00403C1F mov ecx,dword ptr [nSize]<br />00403C22 push ecx<br />00403C23 call _nh_malloc_dbg (00403c80)<br />00403C28 add esp,14h<br />131: }</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　以下这一段代码有BoundsChecker介入：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>126: _CRTIMP void * __cdecl malloc (<br />127: size_t nSize<br />128: )<br />129: {<br /><br />00403C10 jmp 01F41EC8<br />00403C15 push 0<br />00403C17 push 1<br />00403C19 mov eax,[__newmode (0042376c)]<br />00403C1E push eax<br />00403C1F mov ecx,dword ptr [nSize]<br />00403C22 push ecx<br />00403C23 call _nh_malloc_dbg (00403c80)<br />00403C28 add esp,14h<br />131: }</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　 　当BoundsChecker介入后，函数malloc的前三条汇编指令被替换成一条jmp指令，原来的三条指令被搬到地址01F41EC8处了。当程 序进入malloc后先jmp到01F41EC8，执行原来的三条指令，然后就是BoundsChecker的天下了。大致上它会先记录函数的返回地址 （函数的返回地址在stack上，所以很容易修改），然后把返回地址指向属于BoundsChecker的代码，接着跳到malloc函数原来的指令，也 就是在00403c15的地方。当malloc函数结束的时候，由于返回地址被修改，它会返回到BoundsChecker的代码中，此时 BoundsChecker会记录由malloc分配的内存的指针，然后再跳转到到原来的返回地址去。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　如果内存分配/释放函数在DLL中，BoundsChecker则采用另一种方法来截获对这些函数的调用。BoundsChecker通过修改程序的DLL Import Table让table中的函数地址指向自己的地址，以达到截获的目的。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　 　截获住这些分配和释放函数，BoundsChecker就能记录被分配的内存或资源的生命周期。接下来的问题是如何与源代码相关，也就是说当 BoundsChecker检测到内存泄漏，它如何报告这块内存块是哪段代码分配的。答案是调试信息（Debug Information）。当我们编译一个Debug版的程序时，编译器会把源代码和二进制代码之间的对应关系记录下来，放到一个单独的文件里 (.pdb)或者直接连结进目标程序，通过直接读取调试信息就能得到分配某块内存的源代码在哪个文件，哪一行上。使用Code Injection和Debug Information，使BoundsChecker不但能记录呼叫分配函数的源代码的位置，而且还能记录分配时的Call Stack，以及Call Stack上的函数的源代码位置。这在使用像MFC这样的类库时非常有用，以下我用一个例子来说明：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>void ShowXItemMenu()<br />{<br />　&#8230;<br />　CMenu menu;<br /><br />　menu.CreatePopupMenu();<br />　//add menu items.<br />　menu.TrackPropupMenu();<br />　&#8230;<br />}<br /><br />void ShowYItemMenu( )<br />{<br />　&#8230;<br />　CMenu menu;<br />　menu.CreatePopupMenu();<br />　//add menu items.<br />　menu.TrackPropupMenu();<br />　menu.Detach();//this will cause HMENU leak<br />　&#8230;<br />}<br /><br />BOOL CMenu::CreatePopupMenu()<br />{<br />　&#8230;<br />　hMenu = CreatePopupMenu();<br />　&#8230;<br />}</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　 　当调用ShowYItemMenu()时，我们故意造成HMENU的泄漏。但是，对于BoundsChecker来说被泄漏的HMENU是在class CMenu::CreatePopupMenu()中分配的。假设的你的程序有许多地方使用了CMenu的CreatePopupMenu()函数，如 CMenu::CreatePopupMenu()造成的，你依然无法确认问题的根结到底在哪里，在ShowXItemMenu()中还是在 ShowYItemMenu()中，或者还有其它的地方也使用了CreatePopupMenu()？有了Call Stack的信息，问题就容易了。BoundsChecker会如下报告泄漏的HMENU的信息：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><table border="1" align="center" bgcolor="#e3e3e3" bordercolor="#cccccc" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; text-align: left; width: 1230px; "><tbody><tr><td>Function<br />File<br />Line<br /><br />CMenu::CreatePopupMenu<br />E:/8168/vc98/mfc/mfc/include/afxwin1.inl<br />1009<br /><br />ShowYItemMenu<br />E:/testmemleak/mytest.cpp<br />100</td></tr></tbody></table><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　这里省略了其他的函数调用</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　如此，我们很容易找到发生问题的函数是ShowYItemMenu()。当使用MFC之类的类库编程时，大部分的API调用都被封装在类库的class里，有了Call Stack信息，我们就可以非常容易的追踪到真正发生泄漏的代码。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　记录Call Stack信息会使程序的运行变得非常慢，因此默认情况下BoundsChecker不会记录Call Stack信息。可以按照以下的步骤打开记录Call Stack信息的选项开关：</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　1. 打开菜单：BoundsChecker|Setting&#8230;&nbsp;</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　2. 在Error Detection页中，在Error Detection Scheme的List中选择Custom</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　3. 在Category的Combox中选择 Pointer and leak error check</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　4. 钩上Report Call Stack复选框</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　5. 点击Ok</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　基于Code Injection，BoundsChecker还提供了API Parameter的校验功能，memory over run等功能。这些功能对于程序的开发都非常有益。由于这些内容不属于本文的主题，所以不在此详述了。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　尽管BoundsChecker的功能如此强大，但是面对隐式内存泄漏仍然显得苍白无力。所以接下来我们看看如何用Performance Monitor检测内存泄漏。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　使用Performance Monitor检测内存泄漏</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　NT的内核在设计过程中已经加入了系统监视功能，比如</span><a href="http://product.yesky.com/catalog/215/" target="_blank" style="color: #ff9900; text-decoration: none; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">CPU</a><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">的使用率，内存的使用情况，I/O操作的频繁度等都作为一个个Counter，应用程序可以通过读取这些Counter了解整个系统的或者某个进程的运行状况。Performance Monitor就是这样一个应用程序。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　 　为了检测内存泄漏，我们一般可以监视Process对象的Handle Count，Virutal Bytes 和Working Set三个Counter。Handle Count记录了进程当前打开的HANDLE的个数，监视这个Counter有助于我们发现程序是否有Handle泄漏；Virtual Bytes记录了该进程当前在虚地址空间上使用的虚拟内存的大小，NT的内存分配采用了两步走的方法，首先，在虚地址空间上保留一段空间，这时操作系统并 没有分配物理内存，只是保留了一段地址。然后，再提交这段空间，这时操作系统才会分配物理内存。所以，Virtual Bytes一般总大于程序的Working Set。监视Virutal Bytes可以帮助我们发现一些系统底层的问题; Working Set记录了操作系统为进程已提交的内存的总量，这个值和程序申请的内存总量存在密切的关系，如果程序存在内存的泄漏这个值会持续增加，但是 Virtual Bytes却是跳跃式增加的。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　监视这些Counter可以让我们了解进程使用内存的情况，如果发生了泄漏，即使是隐 式内存泄漏，这些Counter的值也会持续增加。但是，我们知道有问题却不知道哪里有问题，所以一般使用Performance Monitor来验证是否有内存泄漏，而使用BoundsChecker来找到和解决。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　当Performance Monitor显示有内存泄漏，而BoundsChecker却无法检测到，这时有两种可能：第一种，发生了偶发性内存泄漏。这时你要确保使用 Performance Monitor和使用BoundsChecker时，程序的运行环境和操作方法是一致的。第二种，发生了隐式的内存泄漏。这时你要重新审查程序的设计，然 后仔细研究Performance Monitor记录的Counter的值的变化图，分析其中的变化和程序运行逻辑的关系，找到一些可能的原因。这是一个痛苦的过程，充满了假设、猜想、验 证、失败，但这也是一个积累经验的绝好机会。</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　总结</span><br style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">　　内存泄漏是个大而复杂的问题，即使是Java和.Net这样有 Gabarge Collection机制的环境，也存在着泄漏的可能，比如隐式内存泄漏。由于篇幅和能力的限制，本文只能对这个主题做一个粗浅的研究。其他的问题，比如 多模块下的泄漏检测，如何在程序运行时对内存使用情况进行分析等等，都是可以深入研究的题目。如果您有什么想法，建议或发现了某些错误，欢迎和我交流。</span>&nbsp;<img src ="http://www.cppblog.com/mysileng/aggbug/194769.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-11-06 21:04 <a href="http://www.cppblog.com/mysileng/archive/2012/11/06/194769.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>