﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-每天早晨叫醒你的不是闹钟,而是梦想-随笔分类-C++</title><link>http://www.cppblog.com/mmdengwo/category/16454.html</link><description /><language>zh-cn</language><lastBuildDate>Mon, 17 Feb 2014 18:18:15 GMT</lastBuildDate><pubDate>Mon, 17 Feb 2014 18:18:15 GMT</pubDate><ttl>60</ttl><item><title>C++中的返回值优化</title><link>http://www.cppblog.com/mmdengwo/archive/2014/02/17/205819.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Mon, 17 Feb 2014 10:20:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2014/02/17/205819.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/205819.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2014/02/17/205819.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/205819.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/205819.html</trackback:ping><description><![CDATA[<span style="background-color: #eeeeee; color: #555555; font-family: Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; line-height: 20px;">原文出自<a href="http://www.programlife.net/" style="color: #2970a6; text-decoration: none;">程序人生</a>&nbsp;&gt;&gt;&nbsp;<a href="http://www.programlife.net/cpp-return-value-optimization.html" style="color: #2970a6; text-decoration: none;">C++中的返回值优化(return value optimization)</a></span><span style="background-color: #eeeeee; color: #555555; font-family: Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; line-height: 20px;"><br />返回值优化（Return Value Optimization，简称RVO），是这么一种优化机制：当函数需要返回一个对象的时候，如果自己创建一个临时对象用户返回，那么这个临时对象会消耗一个构造函数（Constructor）的调用、一个复制构造函数的调用（Copy Constructor）以及一个析构函数（Destructor）的调用的代价。而如果稍微做一点优化，就可以将成本降低到一个构造函数的代价，下面是在Visual Studio 2008的Debug模式下做的一个测试：（在GCC下测试的时候可能编译器自己进行了RVO优化，看不到两种代码的区别）<br /><br /></span><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: #008080; ">&nbsp;1</span>&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;C++&nbsp;Return&nbsp;Value&nbsp;Optimization<br /></span><span style="color: #008080; ">&nbsp;2</span>&nbsp;<span style="color: #008000; "></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;作者：代码疯子<br /></span><span style="color: #008080; ">&nbsp;3</span>&nbsp;<span style="color: #008000; "></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;博客：</span><span style="color: #008000; text-decoration: underline; ">http://www.programlife.net/</span><span style="color: #008000; "><br /></span><span style="color: #008080; ">&nbsp;4</span>&nbsp;<span style="color: #008000; "></span>#include&nbsp;&lt;iostream&gt;<br /><span style="color: #008080; ">&nbsp;5</span>&nbsp;<span style="color: #0000FF; ">using</span>&nbsp;<span style="color: #0000FF; ">namespace</span>&nbsp;std;<br /><span style="color: #008080; ">&nbsp;6</span>&nbsp;&nbsp;<br /><span style="color: #008080; ">&nbsp;7</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;Rational<br /><span style="color: #008080; ">&nbsp;8</span>&nbsp;{<br /><span style="color: #008080; ">&nbsp;9</span>&nbsp;<span style="color: #0000FF; ">public</span>:<br /><span style="color: #008080; ">10</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rational(<span style="color: #0000FF; ">int</span>&nbsp;numerator&nbsp;=&nbsp;0,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;denominator&nbsp;=&nbsp;1)&nbsp;:&nbsp;<br /><span style="color: #008080; ">11</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;n(numerator),&nbsp;d(denominator)<br /><span style="color: #008080; ">12</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /><span style="color: #008080; ">13</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Constructor&nbsp;Called<img src="http://www.cppblog.com/Images/dot.gif"  alt="" />"&nbsp;&lt;&lt;&nbsp;endl;<br /><span style="color: #008080; ">14</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><span style="color: #008080; ">15</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;~Rational()<br /><span style="color: #008080; ">16</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /><span style="color: #008080; ">17</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Destructor&nbsp;Called<img src="http://www.cppblog.com/Images/dot.gif"  alt="" />"&nbsp;&lt;&lt;&nbsp;endl;<br /><span style="color: #008080; ">18</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><span style="color: #008080; ">19</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rational(<span style="color: #0000FF; ">const</span>&nbsp;Rational&amp;&nbsp;rhs)<br /><span style="color: #008080; ">20</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /><span style="color: #008080; ">21</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>-&gt;d&nbsp;=&nbsp;rhs.d;<br /><span style="color: #008080; ">22</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>-&gt;n&nbsp;=&nbsp;rhs.n;<br /><span style="color: #008080; ">23</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"Copy&nbsp;Constructor&nbsp;Called<img src="http://www.cppblog.com/Images/dot.gif"  alt="" />"&nbsp;&lt;&lt;&nbsp;endl;<br /><span style="color: #008080; ">24</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><span style="color: #008080; ">25</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;numerator()&nbsp;<span style="color: #0000FF; ">const</span>&nbsp;{&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;n;&nbsp;}<br /><span style="color: #008080; ">26</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;denominator()&nbsp;<span style="color: #0000FF; ">const</span>&nbsp;{&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;d;&nbsp;}<br /><span style="color: #008080; ">27</span>&nbsp;<span style="color: #0000FF; ">private</span>:<br /><span style="color: #008080; ">28</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;n,&nbsp;d;<br /><span style="color: #008080; ">29</span>&nbsp;};<br /><span style="color: #008080; ">30</span>&nbsp;&nbsp;<br /><span style="color: #008080; ">31</span>&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">const&nbsp;Rational&nbsp;operator*(const&nbsp;Rational&amp;&nbsp;lhs,<br /></span><span style="color: #008080; ">32</span>&nbsp;<span style="color: #008000; "></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;Rational&amp;&nbsp;rhs)<br /></span><span style="color: #008080; ">33</span>&nbsp;<span style="color: #008000; "></span><span style="color: #008000; ">//</span><span style="color: #008000; ">{<br /></span><span style="color: #008080; ">34</span>&nbsp;<span style="color: #008000; "></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;Rational(lhs.numerator()&nbsp;*&nbsp;rhs.numerator(),<br /></span><span style="color: #008080; ">35</span>&nbsp;<span style="color: #008000; "></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lhs.denominator()&nbsp;*&nbsp;rhs.denominator());<br /></span><span style="color: #008080; ">36</span>&nbsp;<span style="color: #008000; "></span><span style="color: #008000; ">//</span><span style="color: #008000; ">}</span><span style="color: #008000; "><br /></span><span style="color: #008080; ">37</span>&nbsp;<span style="color: #008000; "></span>&nbsp;<br /><span style="color: #008080; ">38</span>&nbsp;<span style="color: #0000FF; ">const</span>&nbsp;Rational&nbsp;<span style="color: #0000FF; ">operator</span>*(<span style="color: #0000FF; ">const</span>&nbsp;Rational&amp;&nbsp;lhs,<br /><span style="color: #008080; ">39</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">const</span>&nbsp;Rational&amp;&nbsp;rhs)<br /><span style="color: #008080; ">40</span>&nbsp;{<br /><span style="color: #008080; ">41</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"-----------&nbsp;Enter&nbsp;operator*&nbsp;-----------"&nbsp;&lt;&lt;&nbsp;endl;<br /><span style="color: #008080; ">42</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rational&nbsp;tmp(lhs.numerator()&nbsp;*&nbsp;rhs.numerator(),<br /><span style="color: #008080; ">43</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lhs.denominator()&nbsp;*&nbsp;rhs.denominator());<br /><span style="color: #008080; ">44</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"-----------&nbsp;Leave&nbsp;operator*&nbsp;-----------"&nbsp;&lt;&lt;&nbsp;endl;<br /><span style="color: #008080; ">45</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;tmp;<br /><span style="color: #008080; ">46</span>&nbsp;}<br /><span style="color: #008080; ">47</span>&nbsp;&nbsp;<br /><span style="color: #008080; ">48</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;main(<span style="color: #0000FF; ">int</span>&nbsp;argc,&nbsp;<span style="color: #0000FF; ">char</span>&nbsp;**argv)<br /><span style="color: #008080; ">49</span>&nbsp;{<br /><span style="color: #008080; ">50</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rational&nbsp;x(1,&nbsp;5),&nbsp;y(2,&nbsp;9);<br /><span style="color: #008080; ">51</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rational&nbsp;z&nbsp;=&nbsp;x&nbsp;*&nbsp;y;<br /><span style="color: #008080; ">52</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"calc&nbsp;result:&nbsp;"&nbsp;&lt;&lt;&nbsp;z.numerator()&nbsp;<br /><span style="color: #008080; ">53</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;&lt;&nbsp;"/"&nbsp;&lt;&lt;&nbsp;z.denominator()&nbsp;&lt;&lt;&nbsp;endl;<br /><span style="color: #008080; ">54</span>&nbsp;&nbsp;<br /><span style="color: #008080; ">55</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;0;<br /><span style="color: #008080; ">56</span>&nbsp;}</div><span style="background-color: #eeeeee; color: #555555; font-family: Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; line-height: 20px;"><br /></span><p style="margin: 0px 0px 10px; color: #555555; font-family: Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; line-height: 20px; background-color: #eeeeee; padding: 0px;">函数输出截图如下：<br /><a href="http://www.programlife.net/wp-content/uploads/2011/06/non-rvo.jpg" style="color: #2970a6; text-decoration: none;"><img src="http://www.programlife.net/wp-content/uploads/2011/06/non-rvo.jpg" alt="Return Value Optimization" title="non-rvo" width="667" height="233" size-full=""  wp-image-1560"="" style="border: 0px; display: block; margin-left: auto; margin-right: auto; max-width: 723px;" /></a><br />可以看到消耗一个构造函数（Constructor）的调用、一个复制构造函数的调用（Copy Constructor）以及一个析构函数（Destructor）的调用的代价。</p><p style="margin: 0px 0px 10px; color: #555555; font-family: Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; line-height: 20px; background-color: #eeeeee; padding: 0px;">而如果把operator*换成另一种形式：<br /><br /></p><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 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: #008080; ">1</span>&nbsp;<span style="color: #0000FF; ">const</span>&nbsp;Rational&nbsp;<span style="color: #0000FF; ">operator</span>*(<span style="color: #0000FF; ">const</span>&nbsp;Rational&amp;&nbsp;lhs,<br /><span style="color: #008080; ">2</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">const</span>&nbsp;Rational&amp;&nbsp;rhs)<br /><span style="color: #008080; ">3</span>&nbsp;{<br /><span style="color: #008080; ">4</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;Rational(lhs.numerator()&nbsp;*&nbsp;rhs.numerator(),<br /><span style="color: #008080; ">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lhs.denominator()&nbsp;*&nbsp;rhs.denominator());<br /><span style="color: #008080; ">6</span>&nbsp;}<br /><br /><br /><br /><span style="color: #555555; font-family: Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; font-size: 14px; line-height: 20px;">就只会消耗一个构造函数的成本了：</span><br style="color: #555555; font-family: Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; font-size: 14px; line-height: 20px;" /><a href="http://www.programlife.net/wp-content/uploads/2011/06/rvo.jpg" style="color: #2970a6; text-decoration: none; font-family: Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; font-size: 14px; line-height: 20px;"><img src="http://www.programlife.net/wp-content/uploads/2011/06/rvo.jpg" alt="返回值优化" title="rvo" width="670" height="159" size-full=""  wp-image-1561"="" style="border: 0px; display: block; margin-left: auto; margin-right: auto; max-width: 723px;" /></a></div><img src ="http://www.cppblog.com/mmdengwo/aggbug/205819.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2014-02-17 18:20 <a href="http://www.cppblog.com/mmdengwo/archive/2014/02/17/205819.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Copy On Write(写时复制)</title><link>http://www.cppblog.com/mmdengwo/archive/2014/02/17/205812.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Mon, 17 Feb 2014 10:15:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2014/02/17/205812.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/205812.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2014/02/17/205812.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/205812.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/205812.html</trackback:ping><description><![CDATA[<span style="background-color: #eeeeee; color: #555555; font-family: Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; line-height: 20px;">本文最初发表于<a href="http://www.programlife.net/" style="color: #2970a6; text-decoration: none;">程序人生</a>&nbsp;&gt;&gt;&nbsp;<a href="http://www.programlife.net/copy-on-write.html" style="color: #2970a6; text-decoration: none;">Copy On Write(写时复制)</a>&nbsp;作者：<strong><a href="http://www.programlife.net/" target="_blank" style="color: #0000ff; text-decoration: none;">代码疯子</a></strong></span><span style="background-color: #eeeeee; color: #555555; font-family: Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; line-height: 20px;"><p style="margin: 0px 0px 10px; padding: 0px;">Copy On Write（写时复制）是在编程中比较常见的一个技术，面试中也会偶尔出现（好像Java中就经常有字符串写时复制的笔试题），今天在看《More Effective C++》的引用计数时就讲到了Copy On Write&#8212;&#8212;写时复制。下面简单介绍下Copy On Write(写时复制)，我们假设STL中的string支持写时复制（只是假设，具体未经考证，这里以Mircosoft Visual Studio 6.0为例，如果有兴趣，可以自己翻阅源码）</p><p style="margin: 0px 0px 10px; padding: 0px;"><strong>Copy On Write(写时复制)的原理是什么？</strong><br />有一定经验的程序员应该都知道Copy On Write(写时复制)使用了&#8220;引用计数&#8221;，会有一个变量用于保存引用的数量。当第一个类构造时，string的构造函数会根据传入的参数从堆上分配内存，当有其它类需要这块内存时，这个计数为自动累加，当有类析构时，这个计数会减一，直到最后一个类析构时，此时的引用计数为1或是0，此时，程序才会真正的Free这块从堆上分配的内存。<br />引用计数就是string类中写时才拷贝的原理！</p><p style="margin: 0px 0px 10px; padding: 0px;"><strong>什么情况下触发Copy On Write(写时复制)</strong><br />很显然，当然是在共享同一块内存的类发生内容改变时，才会发生Copy On Write(写时复制)。比如string类的[]、=、+=、+等，还有一些string类中诸如insert、replace、append等成员函数等，包括类的析构时。</p><p style="margin: 0px 0px 10px; padding: 0px;">示例代码：<br /></p><pre style="color: #333333; margin-top: 0px; margin-bottom: 0px; padding: 0px; background-image: none; border: none; width: auto; clear: none; overflow: visible; line-height: 1.333;"><span style="color: #666666;">// 作者：代码疯子</span> <br /><span style="color: #666666;">// 博客：http://www.programlife.net/</span> <br /><span style="color: #666666;">// 引用计数 &amp; 写时复制</span> <br /><span style="color: #339900;">#include &lt;iostream&gt;</span> <br /><span style="color: #339900;">#include &lt;string&gt;</span> <br /><span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span> std<span style="color: #008080;">;</span> &nbsp; <br /><span style="color: #0000ff;">int</span> main<span style="color: #008000;">(</span><span style="color: #0000ff;">int</span> argc, <span style="color: #0000ff;">char</span> <span style="color: #000040;">**</span>argv<span style="color: #008000;">)</span> <br /><span style="color: #008000;">{</span> 	<br />&nbsp;&nbsp;&nbsp;string sa <span style="color: #000080;">=</span> <span style="color: #ff0000;">"Copy on write"</span><span style="color: #008080;">;</span> 	<br />&nbsp;&nbsp;&nbsp;string sb <span style="color: #000080;">=</span> sa<span style="color: #008080;">;</span> 	<br />&nbsp;&nbsp;&nbsp;string sc <span style="color: #000080;">=</span> sb<span style="color: #008080;">;</span> 	<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000dd;">printf</span><span style="color: #008000;">(</span><span style="color: #ff0000;">"sa char buffer address: 0x%08X<span style="color: #000099; font-weight: bold;">\n</span>"</span>, sa.<span style="color: #007788;">c_str</span><span style="color: #008000;">(</span><span style="color: #008000;">)</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span> 	<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000dd;">printf</span><span style="color: #008000;">(</span><span style="color: #ff0000;">"sb char buffer address: 0x%08X<span style="color: #000099; font-weight: bold;">\n</span>"</span>, sb.<span style="color: #007788;">c_str</span><span style="color: #008000;">(</span><span style="color: #008000;">)</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span> 	<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000dd;">printf</span><span style="color: #008000;">(</span><span style="color: #ff0000;">"sc char buffer address: 0x%08X<span style="color: #000099; font-weight: bold;">\n</span>"</span>, sc.<span style="color: #007788;">c_str</span><span style="color: #008000;">(</span><span style="color: #008000;">)</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span> &nbsp; 	<br />&nbsp;&nbsp;&nbsp;sc <span style="color: #000080;">=</span> <span style="color: #ff0000;">"Now writing..."</span><span style="color: #008080;">;</span> 	<span style="color: #0000dd;">printf</span><span style="color: #008000;">(</span><span style="color: #ff0000;">"After writing sc:<span style="color: #000099; font-weight: bold;">\n</span>"</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span> 	<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000dd;">printf</span><span style="color: #008000;">(</span><span style="color: #ff0000;">"sa char buffer address: 0x%08X<span style="color: #000099; font-weight: bold;">\n</span>"</span>, sa.<span style="color: #007788;">c_str</span><span style="color: #008000;">(</span><span style="color: #008000;">)</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span> 	<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000dd;">printf</span><span style="color: #008000;">(</span><span style="color: #ff0000;">"sb char buffer address: 0x%08X<span style="color: #000099; font-weight: bold;">\n</span>"</span>, sb.<span style="color: #007788;">c_str</span><span style="color: #008000;">(</span><span style="color: #008000;">)</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span> 	<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000dd;">printf</span><span style="color: #008000;">(</span><span style="color: #ff0000;">"sc char buffer address: 0x%08X<span style="color: #000099; font-weight: bold;">\n</span>"</span>, sc.<span style="color: #007788;">c_str</span><span style="color: #008000;">(</span><span style="color: #008000;">)</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span> &nbsp; 	<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> <br /><span style="color: #008000;">}</span></pre><div style="margin: 0px 0px 10px; overflow-x: auto; overflow-y: hidden; width: 723px; background-color: #f7f7f7; border: 1px solid #cccccc;"><span style="background-color: #eeeeee;">输出结果如下（VC 6.0）：</span></div><p style="margin: 0px 0px 10px; padding: 0px;"><a href="http://www.programlife.net/wp-content/uploads/2011/09/Copy-On-Write.jpg" style="color: #2970a6; text-decoration: none;"><img src="http://www.programlife.net/wp-content/uploads/2011/09/Copy-On-Write.jpg" alt="Copy On Write（写时复制）" title="Copy-On-Write" width="613" height="187" size-full=""  wp-image-1777"="" style="border: 0px; display: block; margin-left: auto; margin-right: auto; max-width: 723px;" /></a><br />可以看到，VC6里面的string是支持写时复制的，但是我的Visual Studio 2008就不支持这个特性（Debug和Release都是）：<br /><a href="http://www.programlife.net/wp-content/uploads/2011/09/Copy-On-Write-VS2008.jpg" style="color: #2970a6; text-decoration: none;"><img src="http://www.programlife.net/wp-content/uploads/2011/09/Copy-On-Write-VS2008.jpg" alt="Visual Studio 2008不支持Copy On Write（写时复制）" title="Copy-On-Write-VS2008" width="613" height="162" size-full=""  wp-image-1778"="" style="border: 0px; display: block; margin-left: auto; margin-right: auto; max-width: 723px;" /></a></p><p style="margin: 0px 0px 10px; padding: 0px;">拓展阅读：（摘自《Windows Via C/C++》5th Edition，不想看英文可以看中文的PDF，中文版第442页）<br /><a href="http://www.programlife.net/copy-on-write.html" target="_blank" style="color: #0000ff; text-decoration: none;">Static Data Is Not Shared by Multiple Instances of an Executable or a DLL</a></p></span><img src ="http://www.cppblog.com/mmdengwo/aggbug/205812.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2014-02-17 18:15 <a href="http://www.cppblog.com/mmdengwo/archive/2014/02/17/205812.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从一道面试题来阐释一个普遍的认知误区(转)</title><link>http://www.cppblog.com/mmdengwo/archive/2013/04/14/199424.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Sat, 13 Apr 2013 16:51:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2013/04/14/199424.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/199424.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2013/04/14/199424.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/199424.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/199424.html</trackback:ping><description><![CDATA[<span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">上午一个师弟在QQ上问我一道笔试题，是他前两天去KONAMI面试时做的，这道题大致是这样的：</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;</span><em style="line-height: 19px; widows: 2; text-transform: none; background-color: rgb(40,85,126); font-variant: normal; text-indent: 0px; letter-spacing: normal; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); font-size: 13px; font-weight: normal; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">解释以下语句的含义：<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;1、new A;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2、new A();&nbsp; &nbsp;</em><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp;</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp;也许很多人包括我自己，都可以马上给出第一种情况的答案：在堆上为A类分配内存，然后调用A的构造函数。这种说法被大家所熟知，因为包括《STL源码剖析》等大作在内也都是这么写的（但是你认为这种说法完全正确吗？</span><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: red; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">其实不尽然，答案后面揭晓)</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp; 第二种情况，对象构造的时候初始化列表为空会和第一种有什么不同呢？对于这种在实际工程中很少使用的情况，我一时还真给不出确切的答案。</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp;网上搜了一下，看到CSDN里面还有专门针对这个问题的一个帖子（原帖链接&nbsp;http://bbs.csdn.net/topics/320161716）。</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp;好像最终也没有可以信服的答案，认同度比较高的是这样的说法：&#8220;</span><span style="widows: 2; text-transform: none; background-color: rgb(245,245,245); text-indent: 0px; letter-spacing: normal; font: 12px/24px Helvetica, Tahoma, Arial, sans-serif; white-space: normal; orphans: 2; color: rgb(51,51,51); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">加括号调用没有参数的构造函数，不加括号调用默认构造函数或唯一的构造函数，看需求</span><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&#8221; （peakflys注：这种说法是错误的，答案后面揭晓）</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp;既然没有特别靠谱的答案，不如自己动手找出答案。</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp;构造以下示例：</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" />
<div style="border-bottom: rgb(204,204,204) 1px solid; border-left: rgb(204,204,204) 1px solid; padding-bottom: 4px; widows: 2; text-transform: none; background-color: rgb(238,238,238); text-indent: 0px; padding-left: 4px; width: 793px; letter-spacing: normal; padding-right: 5px; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-break: break-all; border-top: rgb(204,204,204) 1px solid; border-right: rgb(204,204,204) 1px solid; word-spacing: 0px; padding-top: 4px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><span style="color: rgb(0,128,0)">/*</span><span style="color: rgb(0,128,0)">*<br />&nbsp;*\brief example1 difference&nbsp;between&nbsp;new&nbsp;and&nbsp;new()<br />&nbsp;*\author&nbsp;peakflys<br />&nbsp;*\data&nbsp;12:10:24&nbsp;Monday,&nbsp;April&nbsp;08,&nbsp;2013<br />&nbsp;</span><span style="color: rgb(0,128,0)">*/</span><br /><br /><span style="color: rgb(0,0,255)">class</span>&nbsp;A<br />{<br /><span style="color: rgb(0,0,255)">public</span>:<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(0,0,255)">int</span>&nbsp;a;<br />};<br /><br /><span style="color: rgb(0,0,255)">int</span>&nbsp;main()<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*pa&nbsp;=&nbsp;<span style="color: rgb(0,0,255)">new</span>&nbsp;A;<br />&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*paa&nbsp;=&nbsp;<span style="color: rgb(0,0,255)">new</span>&nbsp;A();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(0,0,255)">return</span>&nbsp;0;<br />}</div><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">查看main函数的汇编代码(编译器：gcc (GCC) 4.4.6 20120305 (Red Hat 4.4.6-4)&nbsp;)</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" />
<div style="border-bottom: rgb(204,204,204) 1px solid; border-left: rgb(204,204,204) 1px solid; padding-bottom: 4px; widows: 2; text-transform: none; background-color: rgb(238,238,238); text-indent: 0px; padding-left: 4px; width: 793px; letter-spacing: normal; padding-right: 5px; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-break: break-all; border-top: rgb(204,204,204) 1px solid; border-right: rgb(204,204,204) 1px solid; word-spacing: 0px; padding-top: 4px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><span style="color: rgb(0,0,255)">int</span>&nbsp;main()<br />{<br />&nbsp;&nbsp;4005c4:&nbsp;&nbsp;&nbsp;55&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;%rbp<br />&nbsp;&nbsp;4005c5:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;e5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rsp,%rbp<br />&nbsp;&nbsp;4005c8:&nbsp;&nbsp;&nbsp;48&nbsp;83&nbsp;ec&nbsp;10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;$0x10,%rsp<br />&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*pa&nbsp;=&nbsp;<span style="color: rgb(0,0,255)">new</span>&nbsp;A;<br />&nbsp;&nbsp;4005cc:&nbsp;&nbsp;&nbsp;bf&nbsp;04&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;$0x4,%edi<br />&nbsp;&nbsp;4005d1:&nbsp;&nbsp;&nbsp;e8&nbsp;f2&nbsp;fe&nbsp;ff&nbsp;ff&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;callq&nbsp;&nbsp;4004c8&nbsp;&lt;_Znwm@plt&gt; &nbsp; &nbsp; &nbsp; &nbsp; //调用new<br />&nbsp;&nbsp;4005d6:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;45&nbsp;f0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rax,-0x10(%rbp) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //rax寄存器内容赋给指针pa(rax寄存器里是new调用产生的A对象堆内存地址)<br />&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*paa&nbsp;=&nbsp;<span style="color: rgb(0,0,255)">new</span>&nbsp;A();<br />&nbsp;&nbsp;4005da:&nbsp;&nbsp;&nbsp;bf&nbsp;04&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;$0x4,%edi<br />&nbsp;&nbsp;4005df:&nbsp;&nbsp;&nbsp;e8&nbsp;e4&nbsp;fe&nbsp;ff&nbsp;ff&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;callq&nbsp;&nbsp;4004c8&nbsp;&lt;_Znwm@plt&gt; &nbsp; &nbsp; &nbsp; &nbsp; //调用new<br />&nbsp;&nbsp;4005e4:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;c2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rax,%rdx &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//rax的内容放入rdx，执行之后，rdx里存放的即是通过new A()产生的内存地址<br />&nbsp;&nbsp;4005e7:&nbsp;&nbsp;&nbsp;c7&nbsp;02&nbsp;00&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;movl&nbsp;&nbsp;&nbsp;$0x0,(%rdx) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //把rdx内存指向的内容赋为0值，即把A::a赋值为0<br />&nbsp;&nbsp;4005ed:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;45&nbsp;f8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rax,-0x8(%rbp) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //rax寄存器内容赋给指针paa(rax寄存器里是new()调用产生的A对象堆内存地址)<br />&nbsp; &nbsp; &nbsp;<span style="color: rgb(0,0,255)">return</span>&nbsp;0;<br />&nbsp;&nbsp;4005f1:&nbsp;&nbsp;&nbsp;b8&nbsp;00&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;$0x0,%eax<br />}<br />&nbsp;&nbsp;4005f6:&nbsp;&nbsp;&nbsp;c9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;leaveq&nbsp;<br />&nbsp;&nbsp;4005f7:&nbsp;&nbsp;&nbsp;c3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;retq</div><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp; 通过上面产生的汇编代码(对AT&amp;T汇编不熟悉的可以看注释)可以很容易看出，</span><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: red; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">new A()的执行，在调用完operator new分配内存后，马上对新分配内存中的对象使用0值初始化，而new A 仅仅是调用了operator new分配内存！</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp;是不是这样就可以下结论 new A()比new A多了一步，即初始化对象的步骤呢？</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp;我们再看看下面这种情况：</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" />
<div style="border-bottom: rgb(204,204,204) 1px solid; border-left: rgb(204,204,204) 1px solid; padding-bottom: 4px; widows: 2; text-transform: none; background-color: rgb(238,238,238); text-indent: 0px; padding-left: 4px; width: 793px; letter-spacing: normal; padding-right: 5px; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-break: break-all; border-top: rgb(204,204,204) 1px solid; border-right: rgb(204,204,204) 1px solid; word-spacing: 0px; padding-top: 4px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><span style="color: rgb(0,128,0)">/*</span><span style="color: rgb(0,128,0)">*<br />&nbsp;*\brief example2 difference&nbsp;between&nbsp;new&nbsp;and&nbsp;new()<br />&nbsp;*\author&nbsp;peakflys<br />&nbsp;*\data&nbsp;12:23:20&nbsp;Monday,&nbsp;April&nbsp;08,&nbsp;2013<br />&nbsp;</span><span style="color: rgb(0,128,0)">*/</span><br /><br /><span style="color: rgb(0,0,255)">class</span>&nbsp;A<br />{<br /><span style="color: rgb(0,0,255)">public</span>:<br />&nbsp;&nbsp;&nbsp;&nbsp;A(){a&nbsp;=&nbsp;10;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(0,0,255)">int</span>&nbsp;a;<br />};<br /><br /><span style="color: rgb(0,0,255)">int</span>&nbsp;main()<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*pa&nbsp;=&nbsp;<span style="color: rgb(0,0,255)">new</span>&nbsp;A;<br />&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*paa&nbsp;=&nbsp;<span style="color: rgb(0,0,255)">new</span>&nbsp;A();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(0,0,255)">return</span>&nbsp;0;<br />}</div><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp;&nbsp;&nbsp;这种情况是类显示提供含默认值的构造函数。</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp;查看汇编实现如下：</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" />
<div style="border-bottom: rgb(204,204,204) 1px solid; border-left: rgb(204,204,204) 1px solid; padding-bottom: 4px; widows: 2; text-transform: none; background-color: rgb(238,238,238); text-indent: 0px; padding-left: 4px; width: 793px; letter-spacing: normal; padding-right: 5px; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-break: break-all; border-top: rgb(204,204,204) 1px solid; border-right: rgb(204,204,204) 1px solid; word-spacing: 0px; padding-top: 4px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><span style="color: rgb(0,0,255)">int</span>&nbsp;main()<br />{<br />&nbsp;&nbsp;4005c4:&nbsp;&nbsp;&nbsp;55&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;%rbp<br />&nbsp;&nbsp;4005c5:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;e5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rsp,%rbp<br />&nbsp;&nbsp;4005c8:&nbsp;&nbsp;&nbsp;53&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;%rbx<br />&nbsp;&nbsp;4005c9:&nbsp;&nbsp;&nbsp;48&nbsp;83&nbsp;ec&nbsp;18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;$0x18,%rsp<br />&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*pa&nbsp;=&nbsp;<span style="color: rgb(0,0,255)">new</span>&nbsp;A;<br />&nbsp;&nbsp;4005cd:&nbsp;&nbsp;&nbsp;bf&nbsp;04&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;$0x4,%edi<br />&nbsp;&nbsp;4005d2:&nbsp;&nbsp;&nbsp;e8&nbsp;f1&nbsp;fe&nbsp;ff&nbsp;ff&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;callq&nbsp;&nbsp;4004c8&nbsp;&lt;_Znwm@plt&gt;<br />&nbsp;&nbsp;4005d7:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;c3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rax,%rbx<br />&nbsp;&nbsp;4005da:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;d8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rbx,%rax<br />&nbsp;&nbsp;4005dd:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;c7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rax,%rdi<br />&nbsp;&nbsp;4005e0:&nbsp;&nbsp;&nbsp;e8&nbsp;2d&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;callq&nbsp;&nbsp;400612&nbsp;&lt;_ZN1AC1Ev&gt;<br />&nbsp;&nbsp;4005e5:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;5d&nbsp;e0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rbx,-0x20(%rbp)<br />&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*paa&nbsp;=&nbsp;<span style="color: rgb(0,0,255)">new</span>&nbsp;A();<br />&nbsp;&nbsp;4005e9:&nbsp;&nbsp;&nbsp;bf&nbsp;04&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;$0x4,%edi<br />&nbsp;&nbsp;4005ee:&nbsp;&nbsp;&nbsp;e8&nbsp;d5&nbsp;fe&nbsp;ff&nbsp;ff&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;callq&nbsp;&nbsp;4004c8&nbsp;&lt;_Znwm@plt&gt;<br />&nbsp;&nbsp;4005f3:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;c3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rax,%rbx<br />&nbsp;&nbsp;4005f6:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;d8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rbx,%rax<br />&nbsp;&nbsp;4005f9:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;c7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rax,%rdi<br />&nbsp;&nbsp;4005fc:&nbsp;&nbsp;&nbsp;e8&nbsp;11&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;callq&nbsp;&nbsp;400612&nbsp;&lt;_ZN1AC1Ev&gt;<br />&nbsp;&nbsp;400601:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;5d&nbsp;e8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rbx,-0x18(%rbp)<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(0,0,255)">return</span>&nbsp;0;<br />&nbsp;&nbsp;400605:&nbsp;&nbsp;&nbsp;b8&nbsp;00&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;$0x0,%eax<br />}<br />&nbsp;&nbsp;40060a:&nbsp;&nbsp;&nbsp;48&nbsp;83&nbsp;c4&nbsp;18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;$0x18,%rsp<br />&nbsp;&nbsp;40060e:&nbsp;&nbsp;&nbsp;5b&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pop&nbsp;&nbsp;&nbsp;&nbsp;%rbx<br />&nbsp;&nbsp;40060f:&nbsp;&nbsp;&nbsp;c9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;leaveq&nbsp;<br />&nbsp;&nbsp;400610:&nbsp;&nbsp;&nbsp;c3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;retq&nbsp;</div><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp;&nbsp;&nbsp;上面的汇编代码就不在添加注释了，因为两种操作产生的汇编代码是一样的，都是先调用operator new分配内存，然后调用构造函数。</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp;上面的情况在VS2010下验证是一样的情况，有兴趣的朋友可以自己去看，这里就不再贴出VS2010下的汇编代码了。</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp;通过上面的分析，对于</span><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: red; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">new A和 new A() 的区别</span><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">，我们可以得出下面的结论：</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span></span><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: red; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">1、类体含有显示适合地默认构造函数时，new A和new A()的作用一致，都是首先调用operator new分配内存，然后调用默认构造函数初始化对象。</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: red; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp; &nbsp; 2、类体无显示构造函数时，new A()首先调用operator new来为对象分配内存，然后使用空值初始化对象成员变量，而new A仅仅是调用operator new分配内存，对象的成员变量是无意义的随机值！</span><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; （peakflys注：对于基本数据类型，如int等 适用此条）</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp;注意到，现在很多书籍对new操作符的说明都存在纰漏，例如《STL源码剖析》中2.2.2节中有以下的描述：</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><img style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/peakflys/STL%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90_error.png" width="874" height="275" /><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">事实证明，new Foo的操作是否有构造函数的调用是不确定的，具体要看Foo类体里是否有显示构造函数的出现。</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; by peakflys&nbsp;13:40:00&nbsp;Monday, April 08, 2013</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">/*****************************************华丽分割线**************************************</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" /><span style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; display: inline !important; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; float: none; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">补充：刚才发现，在C++Primer第四版5.11节中，已经有了对于new A()的说明：</span><br style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" />
<div style="widows: 2; text-transform: none; background-color: rgb(40,85,126); text-indent: 0px; letter-spacing: normal; font: 13px/19px Verdana, Geneva, Arial, Helvetica, sans-serif; white-space: normal; orphans: 2; color: rgb(0,0,0); word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">&nbsp;&nbsp;&nbsp;We indicate that we want to value-initialize the newly allocated object by following the type nameby a pair of empty parentheses. The empty parentheses signal that we want initialization but arenot supplying a specific initial value. In the case of class types (such as string) that define their own constructors, requesting value-initialization is of no consequence: The object is initialized by running the default constructor whether we leave it apparently uninitialized orask for value-initialization. In the case of built-in types or types that do not define any constructors, the difference is significant：<br />
<div>&nbsp; &nbsp; &nbsp;int *pi = new int; &nbsp; &nbsp; &nbsp; &nbsp; // pi points to an uninitialized int&nbsp;</div>
<div>&nbsp; &nbsp; &nbsp;int *pi = new int(); &nbsp; &nbsp; &nbsp; // pi points to an int value-initialized to 0&nbsp;<br />
<div>In the first case, the int is uninitialized; in the second case, the int is initialized to zero.<br />&nbsp; &nbsp;这里给出的解释和上面自己分析的new A()的行为是一致的。<br />/***************************************再次华丽分割线************************************<br />鉴于上面的结论是通过GCC和VS2010得出的，而且有朋友也提出同样的质疑，为了确定这种结果是否是编译器相关的，刚才特意查看了一下C++的标准化文档。<br />摘自：ISO/IEC 14882:2003(E) 5.3.4 - 15<br />&#8212; If the new-initializer is omitted:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8212; If T is a (possibly cv-qualified) non-POD class type (or array thereof), the object is default-initialized(8.5). If T is a const-qualified type, the underlying class type shall have a user-declared default constructor.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8212; Otherwise, the object created has indeterminate value. If T is a const-qualified type, or a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of const-qualified type, the program is ill-formed;<br />&#8212; If the new-initializer is of the form (), the item is value-initialized (8.5);<br />所以可以确定，这种情况完全是编译器无关的(当然那些不完全按照标准实现的编译器除外)。<br />但是通过上面标准化文档的描述，我们可以看出文中对new A在无显示构造函数时的总结并不是特别准确，鉴于很多公司都有这道面试题(撇去这些题目的实际考察意义不说)，我们有必要再补充一下： &nbsp; 对于new A: 这样的语句，再调用完operator new分配内存之后，如果A类体内含有POD类型，则POD类型的成员变量处于未定义状态，如果含有非POD类型则调用该类型的默认构造函数。而 new A()在这些情况下都会初始化。<br /></div></div></div><img src ="http://www.cppblog.com/mmdengwo/aggbug/199424.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2013-04-14 00:51 <a href="http://www.cppblog.com/mmdengwo/archive/2013/04/14/199424.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ogre是如何实现低耦合的类间消息传递机制的？</title><link>http://www.cppblog.com/mmdengwo/archive/2011/05/12/146265.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Thu, 12 May 2011 10:12:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/05/12/146265.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/146265.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/05/12/146265.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/146265.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/146265.html</trackback:ping><description><![CDATA[<p>关于低耦合的消息传递，实现的方式有很多，哪种方法更好与具体的使用环境有关，本文使用试错的方法，逐步探索达成这一目的具体方式，并理解实现方式背后的原因。</p>
<p>面向对象的系统当中，不可避免的有大量的类间消息传递的需求：一个类需要通知另一个或几个类做些什么。</p>
<p>这种类间消息传递，简单的说，就是调用其他类的方法。</p>
<p>如下：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">1</span><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;A::OnMessageXX()<br></span><span style="COLOR: #008080">2</span><span style="COLOR: #000000"><img id=Codehighlighter1_22_67_Open_Image onclick="this.style.display='none'; Codehighlighter1_22_67_Open_Text.style.display='none'; Codehighlighter1_22_67_Closed_Image.style.display='inline'; Codehighlighter1_22_67_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_22_67_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_22_67_Closed_Text.style.display='none'; Codehighlighter1_22_67_Open_Image.style.display='inline'; Codehighlighter1_22_67_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_22_67_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_22_67_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">3</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;B::GetInstance()</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">DoSomething();<br></span><span style="COLOR: #008080">4</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">5</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">6</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">7</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p>&nbsp;</p>
<p>在这里，类A需要通知类B做些事情。这种调用在所有的面向对象程序中都是极其常见的。</p>
<p>但是如果类A需要调用类B，就不可避免的产生了耦合性。虽然耦合性终归是不可能完全避免的，但是在一定程度上降低耦合性是完全可能的。</p>
<p>（至于为什么在设计中应该尽可能降低耦合性，不在本文的探讨范围之内）</p>
<p>上面的例子，我们使用了Singleton的模式，从全局作用域中获取了B的实例，并调用了B的相关方法。使用Singleton的一个缺点是，假若我们希望对类A编写测试代码，我们需要做一些额外的解耦合工作。（关于编写测试与解耦合，可以参考Robert C. Martin Series 的Working Effectively with Legacy Code一书，该书的中译版在这 ）</p>
<p>我们也可以通过将B参数化的方法降低A与B间的耦合程度，像下面这样：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">1</span>&nbsp;<span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;A::OnMessageXX(B</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pBInstance)<br></span><span style="COLOR: #008080">2</span>&nbsp;<span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">3</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pBInstance</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">DoSomething();<br></span><span style="COLOR: #008080">4</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">5</span>&nbsp;<span style="COLOR: #000000">}<br></span><span style="COLOR: #008080">6</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">7</span>&nbsp;<span style="COLOR: #000000"></span></div>
<p>&nbsp;</p>
<p>现在的写法要比之前的做法耦合性低，通过使用多态的方法，现在传入函数的类B指针可能是另一个实现了B的相应接口的派生类，A并不关心B接口背后的具体实现。</p>
<p>但是等等，你说，现在对类B的耦合性虽然在A中被降低了，但是依旧存在于调用A::OnMessageXX的地方。在那里我们还是需要取得B的实例，然后传递给A。</p>
<p>没错，是这样。</p>
<p>通过参数化类A的方法，我们把类A与类B间的耦合转移了一部分到A的调用者那里。实际上总的耦合并没有消除，只是被分解了。但是程序设计中不可能完全不存在耦合，我们需要做的是&#8221;正确&#8221;，而不是&#8221;完美&#8221;。类A的耦合性降低了，使得我们在未来需求变更的时候，类A有更大的可能性不需要被修改，并且对功能的扩展更加友好，这就达成了我们的目标了。</p>
<p>基于上述做法，如果我们在未来扩展是派生出一个B的子类，override相关的方法，那么类A的代码基本是不需要修改的。</p>
<p>不过，问题是，假若A::OnMessageXX中，并不仅仅需要对类B发出消息，还需要对一系列相关的类B1，B2，B3等等发出消息呢？</p>
<p>哦，或许我们可以这样做：<br></p>
<p>&#160;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;A::OnMessageXX(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;std::list</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">B</span><span style="COLOR: #000000">*&gt;&amp;</span><span style="COLOR: #000000">&nbsp;lstBInstances)<br><img id=Codehighlighter1_56_269_Open_Image onclick="this.style.display='none'; Codehighlighter1_56_269_Open_Text.style.display='none'; Codehighlighter1_56_269_Closed_Image.style.display='inline'; Codehighlighter1_56_269_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_56_269_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_56_269_Closed_Text.style.display='none'; Codehighlighter1_56_269_Open_Image.style.display='inline'; Codehighlighter1_56_269_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_56_269_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_56_269_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(std::list</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">B</span><span style="COLOR: #000000">*&gt;</span><span style="COLOR: #000000">::const_iterator&nbsp;itr&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;lstBInstances.begin();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;itr&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;lstBInstances.end();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">itr)<br><img id=Codehighlighter1_213_267_Open_Image onclick="this.style.display='none'; Codehighlighter1_213_267_Open_Text.style.display='none'; Codehighlighter1_213_267_Closed_Image.style.display='inline'; Codehighlighter1_213_267_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_213_267_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_213_267_Closed_Text.style.display='none'; Codehighlighter1_213_267_Open_Image.style.display='inline'; Codehighlighter1_213_267_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_213_267_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_213_267_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">itr)</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">DoSomething();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>是的，上面这是一种做法，有一系列B的对象需要被通知到，所以我们可以用一个列表把他们串起来，然后在循环中通知他们去干活。不过这样做的前提是，这一系列B对象都是派生自一个公共基类B，有共通的接口；此外，我们需要在A的OnMessageXX被调用之前构造一个需要接受通知的B对象列表。</p>
<p>当A需要通知B，C，D等一系列没有公共接口的对象的时候，上面的这种做法就无法处理了。</p>
<p>对于B、C、D等需要由A来调用的类来说，它们需要在A通知它们的时候，做一些特定的事情。而又A则是在某些特定的时刻需要通知B、C、D。这样，我们可以把问题看成一个消息响应机制。</p>
<p>B、C、D可以在A的某些事件上注册一些回调函数，当事件发生时，A确保注册该事件的函数被调用到。</p>
<p>如下：</p>
<p>typedef void(callback*)();</p>
<p>class A {</p>
<p>public:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; enum EventIds {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EVENT_MSG1,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EVENT_MSG2,</p>
<p>};</p>
<p>void RegisterEvent(int nEventId, callback pfn);</p>
<p>private:</p>
<p>callback m_pfnCallback;</p>
<p>};</p>
<p>现在，B可以调用A::RegisterEvent注册一个事件，并传递一个函数指针给A。</p>
<p>当A中发生了注册的事件时，这个函数指针会被回调到。</p>
<p>不过这种简单的做法适应性很差：</p>
<p>1、&nbsp; 不能支持单个事件的多个callback (可能有很多类都需要注册该事件，并在事件发生时依次被回调)</p>
<p>2、&nbsp; 不能支持多个事件的同时存在</p>
<p>3、&nbsp; 回调函数没有参数&#8217;</p>
<p>针对问题1，2，我们可以使用一个事件映射解决问题，做法如下：</p>
<p>typedef int EventId;</p>
<p>typedef void (callback*)();</p>
<p>typedef std::list&lt;callback&gt; CallbackList;</p>
<p>typedef std::map&lt;EventId, CallbackList&gt; CallbackMap;</p>
<p>现在这个数据结构就能够支持多个event同时存在，且每个event都可以支持多个回调函数了。</p>
<p>但是这种用法依旧很不方便，如果类B想要注册A上的一个事件，他需要定义一个 callback类型的函数，并把这个函数的地址传递给A。问题是，往往我们希望类B的回调函数在被调用到的时候，对类B中的数据和状态进行修改，而一个单独的函数，若想获得/修改B中的状态，则必须要与类B紧密耦合。（通过获取全局对象，或者Singleton的方式）</p>
<p>这种紧密耦合引发我们的思考，能否在Callback中同时包含类B的指针与类B的成员函数。</p>
<p>答案是肯定的：泛型回调 就可以做到这一点。关于泛型回调(Generic callback)的信息，在Herb Sutter的Exceptional C++ Style 的35条中有详细介绍。</p>
<p>一下比较简单的泛型回调的定义如下：</p>
<p>class callbackbase {</p>
<p>public:</p>
<p>virtual void operator()() const {};</p>
<p>virtual ~callbackbase() = 0 {};</p>
<p>};</p>
<p>template &lt;class T&gt;</p>
<p>class callback : public callbackbase {</p>
<p>public:</p>
<p>typedef void (T::*Func)();</p>
<p>callback(T&amp; t, Func func) : object(t), f(func) {}&nbsp;&nbsp;&nbsp;&nbsp; // 绑定到实际对象</p>
<p>void operator() () const { (object-&gt;*f)(); }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 调用回调函数</p>
<p>private:</p>
<p>T* object;</p>
<p>Func f;</p>
<p>};</p>
<p>有了这种泛型回调类，我们就可以将类B的实例与B的成员回调函数绑定在一起注册到容器当中了，而不必再被如何在普通函数中修改B对象状态的问题所困扰了。不过回调函数的参数问题依旧。如果想支持参数，我们不得不对每一种参数类型做一个不同的typedef，像上面定义的这样 typedef void (T::*Func)();（如：typedef void (T::*Func)(int);）</p>
<p>一种解决方案是借助于Any（一种任意类型类）进行参数传递。</p>
<p>但是还有更完善的解决方案，不需要id号，也不需要泛型回调，Ogre采用Listener的方式实现的类间消息传递不仅可以支持单个类B对类A中某个事件的单次/多次注册，也可以支持类B、C、D对同一个事件的注册。而且可以完美的解决参数传递问题。</p>
<p>具体的方案如下：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;1</span><img id=Codehighlighter1_8_654_Open_Image onclick="this.style.display='none'; Codehighlighter1_8_654_Open_Text.style.display='none'; Codehighlighter1_8_654_Closed_Image.style.display='inline'; Codehighlighter1_8_654_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_8_654_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_8_654_Closed_Text.style.display='none'; Codehighlighter1_8_654_Open_Image.style.display='inline'; Codehighlighter1_8_654_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;A&nbsp;</span><span id=Codehighlighter1_8_654_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_8_654_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;2</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br></span><span style="COLOR: #008080">&nbsp;3</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Listener&nbsp;<br></span><span style="COLOR: #008080">&nbsp;4</span><span style="COLOR: #000000"><img id=Codehighlighter1_51_246_Open_Image onclick="this.style.display='none'; Codehighlighter1_51_246_Open_Text.style.display='none'; Codehighlighter1_51_246_Closed_Image.style.display='inline'; Codehighlighter1_51_246_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_51_246_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_51_246_Closed_Text.style.display='none'; Codehighlighter1_51_246_Open_Image.style.display='inline'; Codehighlighter1_51_246_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_51_246_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_51_246_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;5</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br></span><span style="COLOR: #008080">&nbsp;6</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;7</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;OnMessageXX(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;param1,&nbsp;</span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000">&nbsp;param2)&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">&nbsp;8</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;9</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;OnMessageYY(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;param1,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;std::</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;param2)&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">10</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">11</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">12</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">13</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;registerListener(Listener</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;obj)&nbsp;<br></span><span style="COLOR: #008080">14</span><span style="COLOR: #000000"><img id=Codehighlighter1_288_325_Open_Image onclick="this.style.display='none'; Codehighlighter1_288_325_Open_Text.style.display='none'; Codehighlighter1_288_325_Closed_Image.style.display='inline'; Codehighlighter1_288_325_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_288_325_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_288_325_Closed_Text.style.display='none'; Codehighlighter1_288_325_Open_Image.style.display='inline'; Codehighlighter1_288_325_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top></span><span id=Codehighlighter1_288_325_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_288_325_Open_Text><span style="COLOR: #000000">{&nbsp;<br></span><span style="COLOR: #008080">15</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;m_lstListener.push_back(obj);&nbsp;<br></span><span style="COLOR: #008080">16</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">17</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">18</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;removeListener(Listener</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;obj)<br></span><span style="COLOR: #008080">19</span><span style="COLOR: #000000"><img id=Codehighlighter1_363_551_Open_Image onclick="this.style.display='none'; Codehighlighter1_363_551_Open_Text.style.display='none'; Codehighlighter1_363_551_Closed_Image.style.display='inline'; Codehighlighter1_363_551_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_363_551_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_363_551_Closed_Text.style.display='none'; Codehighlighter1_363_551_Open_Image.style.display='inline'; Codehighlighter1_363_551_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top></span><span id=Codehighlighter1_363_551_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_363_551_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">20</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ListenerList::iterator&nbsp;itr&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;std::find(m_lstListener.begin(),&nbsp;m_lstListener.end(),&nbsp;obj);&nbsp;<br></span><span style="COLOR: #008080">21</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">22</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(itr&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;m_lstListener.end())<br></span><span style="COLOR: #008080">23</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_lstListener.erase(itr);<br></span><span style="COLOR: #008080">24</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">25</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">26</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br></span><span style="COLOR: #008080">27</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;std::list</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">Listener</span><span style="COLOR: #000000">*&gt;</span><span style="COLOR: #000000">&nbsp;ListenerList;<br></span><span style="COLOR: #008080">28</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">29</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ListenerList&nbsp;m_lstListeners;<br></span><span style="COLOR: #008080">30</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">31</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">32</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p>&nbsp;</p>
<p>有了以上定义，当类A收到某个消息XX之后，只需遍历m_lstListeners列表，调用所有列表成员的OnMessageXX即可。</p>
<p>而所有注册A的消息的类，都必须从A::Listener派生一个类，在它感兴趣的消息处理函数中做出相应处理，而对不感兴趣的消息，只需设为空函数即可。</p>
<p>一个简单的类B的定义如下：</p>
<p>class B {</p>
<p>public:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; friend class BListener;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class BListener : public A::Listener {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BListener(B* pBInstance) : m_pBInstance(pBInstance) {}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual void OnMessageXX(int param1, float param2)</p>
<p>{ m_pBInstance-&gt;DoSomething(); }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual void OnMessageYY(int param1, const std::string&amp; param2) {}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B* m_pBInstance;</p>
<p>};</p>
<p>explicit B(A* pAInstance) : m_pAInstance(pAInstance)</p>
<p>{</p>
<p>m_pListener(new BListener(this));</p>
<p>m_pAInstance-&gt;registerListener(m_pListener);</p>
<p>}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ~B() { m_pAInstance-&gt;removeListener(m_pListener); delete m_pListener; }</p>
<p>void DoSomething();</p>
<p>private:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BListener* m_pListener;</p>
<p>}</p>
<p>类B在创建自身实例时，接受一个A的指针（这是合理的，因为类B需要监听类A的消息，理应知道A的存在），并创建一个派生自A::Listener 的监听者对象，并把自身的指针传递给该对象，以使得该监听者改变类B的状态，而后类B将创建好的监听者对象加入到A的监听者列表中。</p>
<p>在B进行析构的时候，需要从A中删除自己注册的监听者。而后将该对象释放。</p>
<p>这种做法的好处：</p>
<p>1、&nbsp; 类B（以及类C等）对类A实现了信息隐藏，类A不再关注任何需要监听它自身消息的其他类，只需关注其自身的状态。从而减低了类A与其他与之关联的类之间的耦合。（类A不必再费尽心机的去获取B的指针，不管是通过全局变量，还是Singleton，还是参数，还是类成员变量，都不再需要了，A只关心在 Listener中定义好的一组接口即可）而且，如果有必要类B可以对同一个消息注册多次，且可以对同一消息有不同的反应（通过定义不同的 BListener实现达到这一目的），只需在B不再需要监听相关消息时将所注册过的对象注销掉即可。</p>
<p>2、&nbsp; 由于1中所述，类A的实现无需关心类B的实现，因此类A的逻辑中不需要包含任何类B的方法调用，从而，类A的cpp文件中，无需包含类B的头文件，（可能还包括类C，D等等，此处类B指代需要根据类A状态而做出动作的类）从而降低编译时间，这是解耦合所带来的附加好处。</p>
<p>3、&nbsp; 同样是解耦合带来的好处：因为无需关注类B等等其他类的实现，类A的代码逻辑变得更加清晰，并且减少未来逻辑需求变更的改动所需要付出的代价（逻辑变更可能需要更改接口，需要增加状态判断，无论是调试时间还是编译时间都是不可忽视的代价）。</p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/zougangx/archive/2009/07/30/4395775.aspx">http://blog.csdn.net/zougangx/archive/2009/07/30/4395775.aspx</a></p>
<img src ="http://www.cppblog.com/mmdengwo/aggbug/146265.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-05-12 18:12 <a href="http://www.cppblog.com/mmdengwo/archive/2011/05/12/146265.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++著名程序库的比较和学习经验</title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/29/145349.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Fri, 29 Apr 2011 09:59:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/29/145349.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/145349.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/29/145349.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/145349.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/145349.html</trackback:ping><description><![CDATA[1、C++各大有名库的介绍——C++标准库<br>2、C++各大有名库的介绍——准标准库Boost<br>3、C++各大有名库的介绍——GUI<br>4、C++各大有名库的介绍——网络通信<br>5、C++各大有名库的介绍——XML<br>6、C++各大有名库的介绍——科学计算<br>7、C++各大有名库的介绍——游戏开发<br>8、C++各大有名库的介绍——线程<br>9、C++各大有名库的介绍——序列化<br>10、C++各大有名库的介绍——字符串<br>11、C++各大有名库的介绍——综合<br>12、C++各大有名库的介绍——其他库<br>13、C++名人的网站<br><br>在 C++中，库的地位是非常高的。C++之父 Bjarne Stroustrup先生多次表示了设计库来扩充功能要好过设计更多的语法的言论。现实中，C++的库门类繁多，解决的问题也是极其广泛，库从轻量级到重量级的都有。不少都是让人眼界大开，亦或是望而生叹的思维杰作。由于库的数量非常庞大，而且限于笔者水平，其中很多并不了解。所以文中所提的一些库都是比较著名的大型库。<br><br><br>1、C++各大有名库的介绍——C++标准库<br>
<table border=0>
    <tbody>
        <tr>
            <td>标准库中提供了C++程序的基本设施。虽然C++标准库随着C++标准折腾了许多年，直到标准的出台才正式定型，但是在标准库的实现上却很令人欣慰得看到多种实现，并且已被实践证明为有工业级别强度的佳作。 <br>
            <p>1.1、Dinkumware C++ Library</p>
            <p>参考站点：<a href="http://www.dinkumware.com/" target=_blank><font color=#1d58d1>http://www.dinkumware.com/</font></a></p>
            <p>P.J. Plauger编写的高品质的标准库。P.J. Plauger博士是Dr. Dobb's程序设计杰出奖的获得者。其编写的库长期被Microsoft采用，并且最近Borland也取得了其OEM的license，在其 C/C++的产品中采用Dinkumware的库。</p>
            <p>1.2、RogueWave Standard C++ Library</p>
            <p>参考站点：<a href="http://www.roguewave.com/" target=_blank><font color=#1d58d1>http://www.roguewave.com/</font></a></p>
            <p>这个库在Borland C++ Builder的早期版本中曾经被采用，后来被其他的库给替换了。笔者不推荐使用。</p>
            <p>1.3、SGI STL</p>
            <p>参考站点：<a href="http://www.roguewave.com/" target=_blank><font color=#1d58d1>http://www.roguewave.com/</font></a></p>
            <p>SGI公司的C++标准模版库。</p>
            <p>1.4、STLport</p>
            <p>参考站点：<a href="http://www.stlport.org/" target=_blank><font color=#1d58d1>http://www.stlport.org/</font></a></p>
            <p>SGI STL库的跨平台可移植版本。</p>
            </td>
        </tr>
    </tbody>
</table>
<br><br>2、C++各大有名库的介绍——准标准库Boost<br>
<table border=0>
    <tbody>
        <tr>
            <td>Boost库是一个经过千锤百炼、可移植、提供源代码的C++库，作为标准库的后备，是C++标准化进程的发动机之一。 Boost库由C++标准委员会库工作组成员发起，在C++社区中影响甚大，其成员已近2000人。 Boost库为我们带来了最新、最酷、最实用的技术，是不折不扣的&#8220;准&#8221;标准库。
            <p>Boost中比较有名气的有这么几个库：</p>
            <p>2.1 Regex&nbsp; 正则表达式库</p>
            <p>2.2 Spirit&nbsp;&nbsp; LL parser framework，用C++代码直接表达EBNF</p>
            <p>2.3 Graph&nbsp; 图组件和算法</p>
            <p>2.4 Lambda&nbsp; 在调用的地方定义短小匿名的函数对象，很实用的functional功能</p>
            <p>2.5 concept check&nbsp;&nbsp; 检查泛型编程中的concept</p>
            <p>2.6 Mpl&nbsp;&nbsp; 用模板实现的元编程框架</p>
            <p>2.7 Thread&nbsp;&nbsp; 可移植的C++多线程库</p>
            <p>2.8 Python&nbsp;&nbsp; 把C++类和函数映射到Python之中</p>
            <p>2.9 Pool&nbsp;&nbsp;&nbsp; 内存池管理</p>
            <p>2.10 smart_ptr&nbsp;&nbsp; 5个智能指针，学习智能指针必读，一份不错的参考是来自CUJ的文章：</p>
            <p>Smart Pointers in Boost,哦，这篇文章可以查到，CUJ是提供在线浏览的。中文版见笔者在《Dr.Dobb's Journal软件研发杂志》第7辑上的译文。</p>
            <p>　　Boost总体来说是实用价值很高，质量很高的库。并且由于其对跨平台的强调，对标准C++的强调，是编写平台无关，现代C++的开发者必备的 工具。但是Boost中也有很多是实验性质的东西，在实际的开发中实用需要谨慎。并且很多Boost中的库功能堪称对语言功能的扩展，其构造用尽精巧的手 法，不要贸然的花费时间研读。Boost另外一面，比如Graph这样的库则是具有工业强度，结构良好，非常值得研读的精品代码，并且也可以放心的在产品 代码中多多利用。</p>
            <p>参考站点：<a href="http://www.boost.org/" target=_blank><font color=#1d58d1>http://www.boost.org</font></a></p>
            </td>
        </tr>
    </tbody>
</table>
<br>3、C++各大有名库的介绍——GUI<br>
<table border=0>
    <tbody>
        <tr>
            <td>在众多C++的库中，GUI部分的库算是比较繁荣，也比较引人注目的。在实际开发中，GUI库的选择也是非常重要的一件事情，下面我们综述一下可选择的GUI库，各自的特点以及相关工具的支持。
            <p>3.1、MFC</p>
            <p>　　大名鼎鼎的微软基础类库（Microsoft Foundation Class）。大凡学过VC++的人都应该知道这个库。虽然从技术角度讲，MFC是不大漂亮的，但是它构建于Windows API 之上，能够使程序员的工作更容易,编程效率高，减少了大量在建立 Windows 程序时必须编写的代码，同时它还提供了所有一般 C++ 编程的优点，例如继承和封装。MFC 编写的程序在各个版本的Windows操作系统上是可移植的，例如，在Windows 3.1下编写的代码可以很容易地移植到 Windows NT 或 Windows 95 上。但是在最近发展以及官方支持上日渐势微。</p>
            <p>3.2、QT</p>
            <p>参考网站：<a href="http://www.trolltech.com/" target=_blank><font color=#1d58d1>http://www.trolltech.com</font></a></p>
            <p>　　Qt是Trolltech公司的一个多平台的C++图形用户界面应用程序框架。它提供给应用程序开发者建立艺术级的图形用户界面所需的所用功 能。Qt是完全面向对象的很容易扩展，并且允许真正地组件编程。自从1996年早些时候，Qt进入商业领域，它已经成为全世界范围内数千种成功的应用程序 的基础。Qt也是流行的Linux桌面环境KDE 的基础，同时它还支持Windows、Macintosh、Unix/X11等多种平台。[<a href="http://wangxinus.cublog.cn/" target=_blank><font color=#1d58d1>wangxinus</font></a>注:QT目前已经是Nokia旗下的产品，原官方网站已经失效，目前为<a href="http://qt.nokia.com/" target=_blank><font color=#1d58d1>http://qt.nokia.com</font></a>.2009年初发布的Qt4.5版本开始使用LGPL协议，诺基亚希望以此来吸引更多的开发人员使用Qt库]</p>
            <p>3.3、WxWindows</p>
            <p>参考网站：<a href="http://www.wxwindows.org/" target=_blank><font color=#1d58d1>http://www.wxwindows.org</font></a></p>
            <p>　　跨平台的GUI库。因为其类层次极像MFC，所以有文章介绍从MFC到WxWindows的代码移植以实现跨平台的功能。通过多年的开发也是一个日趋完善的GUI库，支持同样不弱于前面两个库。并且是完全开放源代码的。新近的C++ Builder X的GUI设计器就是基于这个库的。[<a href="http://wangxinus.cublog.cn/" target=_blank><font color=#1d58d1>wangxinus</font></a>注:迫于微软的施压，已经由WxWindows更名为wxWidgets]</p>
            <p>3.4、Fox</p>
            <p>参考网站：<a href="http://www.fox-toolkit.org/" target=_blank><font color=#1d58d1>http://www.fox-toolkit.org/</font></a></p>
            <p>　　开放源代码的GUI库。作者从自己亲身的开发经验中得出了一个理想的GUI库应该是什么样子的感受出发，从而开始了对这个库的开发。有兴趣的可以尝试一下。</p>
            <p>3.5、WTL</p>
            <p>　　基于ATL的一个库。因为使用了大量ATL的轻量级手法，模板等技术，在代码尺寸，以及速度优化方面做得非常到位。主要面向的使用群体是开发COM轻量级供网络下载的可视化控件的开发者。</p>
            <p>3.6、GTK</p>
            <p>参考网站：<a href="http://gtkmm.sourceforge.net/" target=_blank><font color=#1d58d1>http://gtkmm.sourceforge.net/</font></a></p>
            <p>　　GTK是一个大名鼎鼎的C的开源GUI库。在Linux世界中有Gnome这样的杀手应用。而Qt就是这个库的C++封装版本。[<a href="http://wangxinus.cublog.cn/" target=_blank><font color=#1d58d1>wangxinus</font></a>注:&#8220;Qt 就是这个库的C++封装版本&#8221;是错误的。Qt早于GTK，最初Qt由于协议的原因引起社区的不满，另外开发了一个基于C语言的GTK库，后面的扩展版本为 GTK＋。GTK+的Gnome和Qt的KDE是目前linux桌面的两大阵营，曾有水火不容之势。目前双方都以及开源社区的精神，已经和解。]</p>
            </td>
        </tr>
    </tbody>
</table>
<br>4、C++各大有名库的介绍——网络通信<br>
<table border=0>
    <tbody>
        <tr>
            <td>
            <p>4.１、ACE</p>
            <p>参考网站：<a href="http://www.cs.wustl.edu/%7Eschmidt/ACE.html" target=_blank><font color=#1d58d1>http://www.cs.wustl.edu/~schmidt/ACE.html</font></a></p>
            <p>　　C++库的代表，超重量级的网络通信开发框架。ACE自适配通信环境（Adaptive Communication Environment）是可以自由使用、开放源代码的面向对象框架，在其中实现了许多用于并发通信软件的核心模式。ACE提供了一组丰富的可复用C++ 包装外观（Wrapper Facade）和框架组件，可跨越多种平台完成通用的通信软件任务，其中包括：事件多路分离和事件处理器分派、信号处理、服务初始化、进程间通信、共享内存管理、消息路由、分布式服务动态（重）配置、并发执行和同步，等等。</p>
            <p>4.２、StreamModule</p>
            <p>参考网站：<a href="http://www.omnifarious.org/StrMod" target=_blank><font color=#1d58d1>http://www.omnifarious.org/StrMod</font></a></p>
            <p>　　设计用于简化编写分布式程序的库。尝试着使得编写处理异步行为的程序更容易，而不是用同步的外壳包起异步的本质。</p>
            <p>4.３、SimpleSocket</p>
            <p>参考网站：<a href="http://home.hetnet.nl/%7Elcbokkers/simsock.htm" target=_blank><font color=#1d58d1>http://home.hetnet.nl/~lcbokkers/simsock.htm</font></a></p>
            <p>　　这个类库让编写基于socket的客户/服务器程序更加容易。</p>
            <p>4.４、A Stream Socket API for C++</p>
            <p>参考网站：<a href="http://www.pcs.cnu.edu/%7Edgame/sockets/socketsC++/sockets.html" target=_blank><font color=#1d58d1>http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.html</font></a></p>
            <p>　　又一个对Socket的封装库。</p>
            </td>
        </tr>
    </tbody>
</table>
<br>5、C++各大有名库的介绍——XML<br>
<table border=0>
    <tbody>
        <tr>
            <td>
            <p>5.１、Xerces</p>
            <p>参考网站：<a href="http://xml.apache.org/xerces-c/" target=_blank><font color=#1d58d1>http://xml.apache.org/xerces-c/</font></a></p>
            <p>　　Xerces-C++ 是一个非常健壮的XML解析器，它提供了验证，以及SAX和DOM API。XML验证在文档类型定义(Document Type Definition，DTD)方面有很好的支持，并且在2001年12月增加了支持W3C XMLSchema 的基本完整的开放标准。</p>
            <p>5.２、XMLBooster</p>
            <p>参考网站：<a href="http://www.xmlbooster.com/" target=_blank><font color=#1d58d1>http://www.xmlbooster.com/</font></a></p>
            <p>　　这个库通过产生特制的parser的办法极大的提高了XML解析的速度，并且能够产生相应的GUI程序来修改这个parser。在DOM和SAX两大主流XML解析办法之外提供了另外一个可行的解决方案。</p>
            <p>5.３、Pull Parser</p>
            <p>参考网站：<a href="http://www.extreme.indiana.edu/xgws/xsoap/xpp" target=_blank><font color=#1d58d1>http://www.extreme.indiana.edu/xgws/xsoap/xpp</font></a></p>
            <p>　　这个库采用pull方法的parser。在每个SAX的parser底层都有一个pull的parser，这个xpp把这层暴露出来直接给大家使用。在要充分考虑速度的时候值得尝试。</p>
            <p>5.４、Xalan</p>
            <p>参考网站：<a href="http://xml.apache.org/xalan-c/" target=_blank><font color=#1d58d1>http://xml.apache.org/xalan-c/</font></a></p>
            <p>　　Xalan是一个用于把XML文档转换为HTML，纯文本或者其他XML类型文档的XSLT处理器。</p>
            <p>5.５、CMarkup</p>
            <p>参考网站：<a href="http://www.firstobject.com/xml.htm" target=_blank><font color=#1d58d1>http://www.firstobject.com/xml.htm</font></a></p>
            <p>　　这是一种使用EDOM的XML解析器。在很多思路上面非常灵活实用。值得大家在DOM和SAX之外寻求一点灵感。</p>
            <p>5.６、libxml++</p>
            <p><a href="http://libxmlplusplus.sourceforge.net/" target=_blank><font color=#1d58d1>http://libxmlplusplus.sourceforge.net/</font></a></p>
            <p>　　libxml++是对著名的libxml XML解析器的C++封装版本。</p>
            <p>5.7. TinyXML [<a href="http://wangxinus.cublog.cn/" target=_blank><font color=#1d58d1>wangxinus</font></a>注:一个非常小巧的XML解析库，基于DOM的。]</p>
            </td>
        </tr>
    </tbody>
</table>
<br>6、C++各大有名库的介绍——科学计算<br>
<table border=0>
    <tbody>
        <tr>
            <td>
            <p>6.１、Blitz++</p>
            <p>参考网站：<a href="http://www.oonumerics.org/blitz" target=_blank><font color=#1d58d1>http://www.oonumerics.org/blitz</font></a></p>
            <p>　　Blitz++ 是一个高效率的数值计算函数库，它的设计目的是希望建立一套既具像C++ 一样方便，同时又比Fortran速度更快的数值计算环境。通常，用C++所写出的数值程序，比 Fortran慢20%左右，因此Blitz++正是要改掉这个缺点。方法是利用C++的template技术，程序执行甚至可以比Fortran更快。</p>
            <p>　　Blitz++目前仍在发展中，对于常见的SVD，FFTs，QMRES等常见的线性代数方法并不提供，不过使用者可以很容易地利用Blitz++所提供的函数来构建。</p>
            <p>6.２、POOMA</p>
            <p>参考网站：<a href="http://www.oonumerics.org/blitz" target=_blank><font color=#1d58d1>http://www.codesourcery.com/pooma/pooma</font></a></p>
            <p>　　POOMA是一个免费的高性能的C++库，用于处理并行式科学计算。POOMA的面向对象设计方便了快速的程序开发，对并行机器进行了优化以达到最高的效率，方便在工业和研究环境中使用。</p>
            <p>6.３、MTL</p>
            <p>参考网站：<a href="http://www.osl.iu.edu/research/mtl" target=_blank><font color=#1d58d1>http://www.osl.iu.edu/research/mtl</font></a></p>
            <p>　　Matrix Template Library(MTL)是一个高性能的泛型组件库，提供了各种格式矩阵的大量线性代数方面的功能。在某些应用使用高性能编译器的情况下，比如Intel的编译器，从产生的汇编代码可以看出其与手写几乎没有两样的效能。</p>
            <p>6.４、CGAL</p>
            <p>参考网站：<a href="http://www.cgal.org/" target=_blank><font color=#1d58d1>www.cgal.org</font></a></p>
            <p>　　Computational Geometry Algorithms Library的目的是把在计算几何方面的大部分重要的解决方案和方法以C++库的形式提供给工业和学术界的用户。</p>
            </td>
        </tr>
    </tbody>
</table>
<br>7、C++各大有名库的介绍——游戏开发<br>
<table border=0>
    <tbody>
        <tr>
            <td>
            <p>7.１、Audio/Video 3D C++ Programming Library</p>
            <p>参考网站：<a href="http://www.galacticasoftware.com/products/av/" target=_blank><font color=#1d58d1>http://www.galacticasoftware.com/products/av/</font></a><br><br>　　AV3D是一个跨平台，高性能的C++库。主要的特性是提供3D图形，声效支持（SB,以及S3M），控制接口（键盘，鼠标和遥感），XMS。</p>
            <p>7.２、KlayGE</p>
            <p>参考网站：<a href="http://home.g365.net/enginedev/" target=_blank><font color=#1d58d1>http://home.g365.net/enginedev/</font></a></p>
            <p>　　国内游戏开发高手自己用C++开发的游戏引擎。KlayGE是一个开放源代码、跨平台的游戏引擎，并使用Python作脚本语言。KlayGE在LGPL协议下发行。感谢龚敏敏先生为中国游戏开发事业所做出的贡献。</p>
            <p>[<a href="http://wangxinus.cublog.cn/" target=_blank><font color=#1d58d1>wangxinus</font></a>注:这个库国人了解很少，百度百科的<a href="http://baike.baidu.com/view/2592444.htm" target=_blank><font color=#1d58d1>KlayGE词条</font></a>还是本人创建的。一个人开发一个游戏引擎库，是在让笔者汗颜，对作者表示钦佩！]</p>
            <p>7.３、OGRE</p>
            <p>参考网站：<a href="http://www.ogre3d.org/" target=_blank><font color=#1d58d1>http://www.ogre3d.org</font></a></p>
            <p>　　OGRE（面向对象的图形渲染引擎）是用C++开发的，使用灵活的面向对象3D引擎。它的目的是让开发者能更方便和直接地开发基于3D硬件设备 的应用程序或游戏。引擎中的类库对更底层的系统库（如：Direct3D和OpenGL）的全部使用细节进行了抽象，并提供了基于现实世界对象的接口和其 它类。</p>
            </td>
        </tr>
    </tbody>
</table>
<br>8、C++各大有名库的介绍——线程<br>
<table border=0>
    <tbody>
        <tr>
            <td>
            <p>8.１、C++ Threads</p>
            <p>参考网站：<a href="http://threads.sourceforge.net/" target=_blank><font color=#1d58d1>http://threads.sourceforge.net/</font></a></p>
            <p>　　这个库的目标是给程序员提供易于使用的类，这些类被继承以提供在Linux环境中很难看到的大量的线程方面的功能。</p>
            <p>8.２、ZThreads</p>
            <p>参考网站：<a href="http://zthread.sourceforge.net/" target=_blank><font color=#1d58d1>http://zthread.sourceforge.net/</font></a></p>
            <p>　　一个先进的面向对象，跨平台的C++线程和同步库。</p>
            </td>
        </tr>
    </tbody>
</table>
<br>9、C++各大有名库的介绍——序列化<br>
<table border=0>
    <tbody>
        <tr>
            <td>
            <p>9.１、s11n</p>
            <p>参考网站：<a href="http://s11n.net/" target=_blank><font color=#1d58d1>http://s11n.net/</font></a></p>
            <p>　　一个基于STL的C++库，用于序列化POD，STL容器以及用户定义的类型。</p>
            <p>9.２、Simple XML Persistence Library</p>
            <p>参考网站：<a href="http://sxp.sourceforge.net/" target=_blank><font color=#1d58d1>http://sxp.sourceforge.net/</font></a></p>
            <p>　　这是一个把对象序列化为XML的轻量级的C++库。</p>
            </td>
        </tr>
    </tbody>
</table>
<br>10、C++各大有名库的介绍——字符串<br>
<table border=0>
    <tbody>
        <tr>
            <td>
            <p>10.１、C++ Str Library</p>
            <p>参考网站：<a href="http://www.utilitycode.com/str/" target=_blank><font color=#1d58d1>http://www.utilitycode.com/str/</font></a></p>
            <p>　　操作字符串和字符的库，支持Windows和支持gcc的多种平台。提供高度优化的代码，并且支持多线程环境和Unicode，同时还有正则表达式的支持。</p>
            <p>10.２、Common Text Transformation Library</p>
            <p>参考网站：<a href="http://cttl.sourceforge.net/" target=_blank><font color=#1d58d1>http://cttl.sourceforge.net/</font></a></p>
            <p>　　这是一个解析和修改STL字符串的库。CTTL substring类可以用来比较，插入，替换以及用EBNF的语法进行解析。</p>
            <p>10.３、GRETA</p>
            <p>参考网站：<a href="http://research.microsoft.com/projects/greta/" target=_blank><font color=#1d58d1>http://research.microsoft.com/projects/greta/</font></a></p>
            <p>　　这是由微软研究院的研究人员开发的处理正则表达式的库。在小型匹配的情况下有非常优秀的表现。</p>
            </td>
        </tr>
    </tbody>
</table>
<br>11、C++各大有名库的介绍——综合<br>
<table border=0>
    <tbody>
        <tr>
            <td>
            <p>11.１、P::Classes</p>
            <p>参考网站：<a href="http://pclasses.com/" target=_blank><font color=#1d58d1>http://pclasses.com/</font></a></p>
            <p>　　一个高度可移植的C++应用程序框架。当前关注类型和线程安全的signal/slot机制，i/o系统包括基于插件的网络协议透明的i/o架构，基于插件的应用程序消息日志框架，访问sql数据库的类等等。</p>
            <p>11.２、ACDK - Artefaktur Component Development Kit</p>
            <p>参考网站：<a href="http://acdk.sourceforge.net/" target=_blank><font color=#1d58d1>http://acdk.sourceforge.net/</font></a></p>
            <p>　　这是一个平台无关的C++组件框架，类似于Java或者.NET中的框架（反射机制，线程，Unicode，废料收集，I/O，网络，实用工具，XML，等等），以及对Java, Perl, Python, TCL, Lisp, COM 和 CORBA的集成。</p>
            <p>11.３、dlib C++ library</p>
            <p>参考网站：<a href="http://www.cis.ohio-state.edu/%7Ekingd/dlib/" target=_blank><font color=#1d58d1>http://www.cis.ohio-state.edu/~kingd/dlib/</font></a></p>
            <p>　　各种各样的类的一个综合。大整数，Socket，线程，GUI，容器类,以及浏览目录的API等等。</p>
            <p>11.４、Chilkat C++ Libraries</p>
            <p>参考网站：<a href="http://www.chilkatsoft.com/cpp_libraries.asp" target=_blank><font color=#1d58d1>http://www.chilkatsoft.com/cpp_libraries.asp</font></a></p>
            <p>　　这是提供zip，e-mail，编码，S/MIME，XML等方面的库。</p>
            <p>11.５、C++ Portable Types Library (PTypes)</p>
            <p>参考网站：<a href="http://www.melikyan.com/ptypes/" target=_blank><font color=#1d58d1>http://www.melikyan.com/ptypes/</font></a></p>
            <p>　　这是STL的比较简单的替代品，以及可移植的多线程和网络库。</p>
            <p>11.６、LFC</p>
            <p>参考网站：<a href="http://lfc.sourceforge.net/" target=_blank><font color=#1d58d1>http://lfc.sourceforge.net/</font></a></p>
            <p>　　哦，这又是一个尝试提供一切的C++库</p>
            </td>
        </tr>
    </tbody>
</table>
<br>12、C++各大有名库的介绍——其他库<br>
<table border=0>
    <tbody>
        <tr>
            <td>
            <p>12.１、Loki</p>
            <p>参考网站：<a href="http://www.moderncppdesign.com/" target=_blank><font color=#1d58d1>http://www.moderncppdesign.com/</font></a></p>
            <p>　　哦，你可能抱怨我早该和Boost一起介绍它，一个实验性质的库。作者在loki中把C++模板的功能发挥到了极致。并且尝试把类似设计模式这样思想层面的东西通过库来提供。同时还提供了智能指针这样比较实用的功能。</p>
            <p>12.２、ATL</p>
            <p>　　ATL(Active Template Library)是一组小巧、高效、灵活的类，这些类为创建可互操作的COM组件提供了基本的设施。</p>
            <p>12.３、FC++: The Functional C++ Library</p>
            <p>　　这个库提供了一些函数式语言中才有的要素。属于用库来扩充语言的一个代表作。如果想要在OOP之外寻找另一分的乐趣，可以去看看函数式程序设计的世界。大师Peter Norvig在 &#8220;Teach Yourself Programming in Ten Years&#8221;一文中就将函数式语言列为至少应当学习的6类编程语言之一。</p>
            <p>12.４、FACT!</p>
            <p>参考网站：<a href="http://www.kfa-juelich.de/zam/FACT/start/index.html" target=_blank><font color=#1d58d1>http://www.kfa-juelich.de/zam/FACT/start/index.html</font></a></p>
            <p>　　另外一个实现函数式语言特性的库</p>
            <p>12.５、Crypto++</p>
            <p>　　提供处理密码，消息验证，单向hash，公匙加密系统等功能的免费库。</p>
            <p>　　还有很多非常激动人心或者是极其实用的C++库，限于我们的水平以及文章的篇幅不能包括进来。在对于这些已经包含近来的库的介绍中，由于并不是每一个我们都使用过，所以难免有偏颇之处，请读者见谅。</p>
            </td>
        </tr>
    </tbody>
</table>
<br>13、C++名人的网站<br>正如我们可以通过计算机历史上的重要人物了解计算机史的发展，C++相关人物的网站也可以使我们得到最有价值的参考与借鉴，下面的人物我们认为没有介绍的必要，只因下面的人物在C++领域的地位众所周知，我们只将相关的资源进行罗列以供读者学习，他们有的工作于贝尔实验室，有的工作于知名编译器厂商，有的在不断推进语言的标准化，有的为读者撰写了多部千古奇作&#8230;&#8230;<br><br>
<table border=0>
    <tbody>
        <tr>
            <td>
            <p>１、Bjarne Stroustrup</p>
            <p><a href="http://www.research.att.com/%7Ebs/" target=_blank><font color=#1d58d1>http://www.research.att.com/~bs/</font></a></p>
            <p>２、Stanley B. Lippman</p>
            <p><a href="http://blogs.msdn.com/slippman/" target=_blank><font color=#1d58d1>http://blogs.msdn.com/slippman/　</font></a>(中文版)</p>
            <p><a href="http://www.zengyihome.net/slippman/index.htm" target=_blank><font color=#1d58d1>http://www.zengyihome.net/slippman/index.htm</font></a></p>
            <p>３、Scott Meyers</p>
            <p><a href="http://www.aristeia.com/" target=_blank><font color=#1d58d1>http://www.aristeia.com/</font></a></p>
            <p>４、David Musser</p>
            <p><a href="http://www.cs.rpi.edu/%7Emusser/" target=_blank><font color=#1d58d1>http://www.cs.rpi.edu/~musser/</font></a></p>
            <p>５、Bruce Eckel</p>
            <p><a href="http://www.bruceeckel.com/" target=_blank><font color=#1d58d1>http://www.bruceeckel.com</font></a></p>
            <p><a href="http://blog.csdn.net/beckel" target=_blank><font color=#1d58d1>http://blog.csdn.net/beckel</font></a>　Bruce Eckel 博客中文版</p>
            <p>６、Nicolai M. Josuttis</p>
            <p><a href="http://www.josuttis.com/" target=_blank><font color=#1d58d1>http://www.josuttis.com/</font></a></p>
            <p>７、Herb Sutter</p>
            <p><a href="http://www.gotw.ca/" target=_blank><font color=#1d58d1>http://www.gotw.ca/</font></a></p>
            <p><a href="http://blog.csdn.net/hsutter/" target=_blank><font color=#1d58d1>http://blog.csdn.net/hsutter/</font></a>　Herb Sutter 中文博客</p>
            <p>８、Andrei Alexandrescu</p>
            <p><a href="http://www.moderncppdesign.com/" target=_blank><font color=#1d58d1>http://www.moderncppdesign.com</font></a></p>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<img src ="http://www.cppblog.com/mmdengwo/aggbug/145349.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-29 17:59 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/29/145349.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>程序员面试题精选题-C++</title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/29/145319.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Fri, 29 Apr 2011 05:44:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/29/145319.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/145319.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/29/145319.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/145319.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/145319.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;题目（一）：我们可以用static修饰一个类的成员函数，也可以用const修饰类的成员函数（写在函数的最后表示不能修改成员变量，不是指写在前面表示返回值为常量）。请问：能不能同时用static和const修饰类的成员函数？分析：答案是不可以。C++编译器在实现const的成员函数的时候为了确保该函数不能修改类的实例的状态，会在函数中添加一个隐式的参数const this*。但当...&nbsp;&nbsp;<a href='http://www.cppblog.com/mmdengwo/archive/2011/04/29/145319.html'>阅读全文</a><img src ="http://www.cppblog.com/mmdengwo/aggbug/145319.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-29 13:44 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/29/145319.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>静态对象、全局对象与程序的运行机制</title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/29/145302.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Fri, 29 Apr 2011 03:06:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/29/145302.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/145302.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/29/145302.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/145302.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/145302.html</trackback:ping><description><![CDATA[<p>1、 在介绍静态对象、全局对象与程序的运行机制之间的关系之前，我们首先看一下atexit函数。<br><br>atexit函数的声明为：int atexit( void ( __cdecl *func )( void ) );<br><br>参数为函数指针，返回值为整型，0表示成功，其他表示失败。当程序运行结束时，他调用atexit函数注册的所有函数。如果多次调用atexit函数，那么系统将以LIFO(last-in-first-out)的方式调用所有的注册函数。<br><br>举例如下（代码摘自MSDN）：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;1</span><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">stdlib.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;2</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">stdio.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;3</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br></span><span style="COLOR: #008080">&nbsp;4</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;fn1(&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;),&nbsp;fn2(&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;),&nbsp;fn3(&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;),&nbsp;fn4(&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;);<br></span><span style="COLOR: #008080">&nbsp;5</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br></span><span style="COLOR: #008080">&nbsp;6</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;main(&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;)<br></span><span style="COLOR: #008080">&nbsp;7</span><span style="COLOR: #000000"><img id=Codehighlighter1_118_230_Open_Image onclick="this.style.display='none'; Codehighlighter1_118_230_Open_Text.style.display='none'; Codehighlighter1_118_230_Closed_Image.style.display='inline'; Codehighlighter1_118_230_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_118_230_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_118_230_Closed_Text.style.display='none'; Codehighlighter1_118_230_Open_Image.style.display='inline'; Codehighlighter1_118_230_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_118_230_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_118_230_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;8</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;atexit(&nbsp;fn1&nbsp;);<br></span><span style="COLOR: #008080">&nbsp;9</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;atexit(&nbsp;fn2&nbsp;);<br></span><span style="COLOR: #008080">10</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;atexit(&nbsp;fn3&nbsp;);<br></span><span style="COLOR: #008080">11</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;atexit(&nbsp;fn4&nbsp;);<br></span><span style="COLOR: #008080">12</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;printf(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">This&nbsp;is&nbsp;executed&nbsp;first.\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;);<br></span><span style="COLOR: #008080">13</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">14</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br></span><span style="COLOR: #008080">15</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;fn1()<br></span><span style="COLOR: #008080">16</span><span style="COLOR: #000000"><img id=Codehighlighter1_245_271_Open_Image onclick="this.style.display='none'; Codehighlighter1_245_271_Open_Text.style.display='none'; Codehighlighter1_245_271_Closed_Image.style.display='inline'; Codehighlighter1_245_271_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_245_271_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_245_271_Closed_Text.style.display='none'; Codehighlighter1_245_271_Open_Image.style.display='inline'; Codehighlighter1_245_271_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_245_271_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_245_271_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">17</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;printf(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">next.\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;);<br></span><span style="COLOR: #008080">18</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">19</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br></span><span style="COLOR: #008080">20</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;fn2()<br></span><span style="COLOR: #008080">21</span><span style="COLOR: #000000"><img id=Codehighlighter1_286_313_Open_Image onclick="this.style.display='none'; Codehighlighter1_286_313_Open_Text.style.display='none'; Codehighlighter1_286_313_Closed_Image.style.display='inline'; Codehighlighter1_286_313_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_286_313_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_286_313_Closed_Text.style.display='none'; Codehighlighter1_286_313_Open_Image.style.display='inline'; Codehighlighter1_286_313_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_286_313_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_286_313_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">22</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;printf(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">executed&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;);<br></span><span style="COLOR: #008080">23</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">24</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br></span><span style="COLOR: #008080">25</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;fn3()<br></span><span style="COLOR: #008080">26</span><span style="COLOR: #000000"><img id=Codehighlighter1_328_349_Open_Image onclick="this.style.display='none'; Codehighlighter1_328_349_Open_Text.style.display='none'; Codehighlighter1_328_349_Closed_Image.style.display='inline'; Codehighlighter1_328_349_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_328_349_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_328_349_Closed_Text.style.display='none'; Codehighlighter1_328_349_Open_Image.style.display='inline'; Codehighlighter1_328_349_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_328_349_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_328_349_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">27</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;printf(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">is&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;);<br></span><span style="COLOR: #008080">28</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">29</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br></span><span style="COLOR: #008080">30</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;fn4()<br></span><span style="COLOR: #008080">31</span><span style="COLOR: #000000"><img id=Codehighlighter1_364_387_Open_Image onclick="this.style.display='none'; Codehighlighter1_364_387_Open_Text.style.display='none'; Codehighlighter1_364_387_Closed_Image.style.display='inline'; Codehighlighter1_364_387_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_364_387_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_364_387_Closed_Text.style.display='none'; Codehighlighter1_364_387_Open_Image.style.display='inline'; Codehighlighter1_364_387_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_364_387_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_364_387_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">32</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;printf(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">This&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;);<br></span><span style="COLOR: #008080">33</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">34</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br><br>编译、运行程序后，程序的输出为：<br>This is executed first.<br>This is executed next.<br>注册函数的顺序为：fn1、fn2、fn3、fn4，但是调用顺序为fn4、fn3、fn2、fn1。<br><br>2、理解了atexit函数之后，我们就可以来看看局部静态对象了。<br><br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">1</span>&nbsp;<span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;AAA{&nbsp;&#8230;&nbsp;}&nbsp;;<br></span><span style="COLOR: #008080">2</span>&nbsp;<span style="COLOR: #000000">AAA</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;createAAA()&nbsp;<br></span><span style="COLOR: #008080">3</span>&nbsp;<span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">4</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;AAA&nbsp;a&nbsp;;<br></span><span style="COLOR: #008080">5</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">a&nbsp;;<br></span><span style="COLOR: #008080">6</span>&nbsp;<span style="COLOR: #000000">}<br></span><span style="COLOR: #008080">7</span>&nbsp;<span style="COLOR: #000000"></span></div>
<p><br>在调试状态下，汇编代码如下(请观察蓝色标记出来的代码)：<br>AAA* createAAA() <br>{<br>&nbsp;&nbsp;&nbsp;&nbsp; &#8230;<br>&nbsp;&nbsp;&nbsp;&nbsp; static AAA a ;<br>&#8230;<br>[1] 00401056 call&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AAA::AAA (4010A0h) <br>[2] 0040105B push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;offset `createAAA'::`2'::a::`dynamic atexit destructor' (442410h) <br>[3] 00401060 call&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; atexit (409A50h) <br>00401065 add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,4 <br>00401068 mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ebp-4],0FFFFFFFFh <br>&nbsp;&nbsp;&nbsp;&nbsp; return &amp;a ;<br>0040106F mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,offset a (452620h) <br>}<br>&#8230; <br>00401091 ret&nbsp;&nbsp; <br>注：[1]、[2]、[3]为方便说明加入的字符，实际代码中并不存在。<br>[1]语句很明显的调用AAA的构造函数。<br>[2]语句将442410h压入栈中。<br>[3]语句调用atexit函数，根据我们的了解，atexit的参数应该是函数指针。那么我们来分析一下442410h处的代码，从注释来看，我们看到了destructor。代码如下：<br>`createAAA'::`2'::a::`dynamic atexit destructor':<br>&#8230;<br>[1] 0044242E mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ecx,offset a (452620h) <br>[2] 00442433 call&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AAA::~AAA (403A90h) <br>&#8230;<br>0044244B ret&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>[1]语句将a的地址放在ecx寄存器中，这是this调用规范的风格。<br>[2]语句调用AAA的析构函数。</p>
<p>程序结束时，将调用atexit函数注册的442410h处的函数，进而调用了AAA的析构函数。从而保证了析构函数的调用。</p>
<p>&nbsp;<br>3、&nbsp;&nbsp; 了解了局部静态对象之后，我们来看看全局对象。<br>我们知道全局对象必须在main函数前已经被构造。为了弄清楚全局对象何时被构造，我在全局对象的实例化处设置了断点，调用堆栈如下：<br>static.exe!aaaa::`dynamic initializer'() Line 22 C++ <br>static.exe!_initterm(void (void)* * pfbegin=0x00451038, void (void)* * pfend=0x00451064) Line 707 C <br>static.exe!_cinit(int initFloatingPrecision=1) Line 208 + 0xf bytes C <br>static.exe!mainCRTStartup() Line 266 + 0x7 bytes C </p>
<p><br>作为对比，我在AAA的析构函数出设置了断点，调用堆栈如下：<br>&nbsp;&nbsp;&nbsp;&nbsp; static.exe!AAA::~AAA() Line 19&nbsp; C++ <br>&nbsp;&nbsp;&nbsp;&nbsp; static.exe!aaaa::`dynamic atexit destructor'() + 0x28 bytes&nbsp; C++ <br>&nbsp;&nbsp;&nbsp;&nbsp; static.exe!doexit(int code=0, int quick=0, int retcaller=0) Line 451&nbsp; C <br>&nbsp;&nbsp;&nbsp;&nbsp; static.exe!exit(int code=0) Line 311 + 0xd bytes&nbsp; C <br>&nbsp;&nbsp;&nbsp;&nbsp; static.exe!mainCRTStartup() Line 289&nbsp; C </p>
<p><br>由此我们可以看出程序的实际入口点位mainCRTStartup而不是main函数（相对于ANSI的控制台程序而言）。<br>我们来分析一下_cinit函数：<br>注释中有一句[3. General C initializer routines]，看来该函数的功能之一是完成C的初始化例程。<br>函数的核心代码如下：<br>/*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * do initializations<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; initret = _initterm_e( __xi_a, __xi_z );<br>/*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * do C++ initializations<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _initterm( __xc_a, __xc_z );<br>看来该函数主要进行C、C++的初始化。我们进一步分析函数_initterm_e和_initterm，两个函数的功能进本相同，都是遍历函数指针（由参数指定函数指针的开始位置[__xi_a、__xi_z]、结束位置[__xc_a、__xc_z]），如果函数指针不为null，那么调用该函数。<br>那么__xi_a、__xi_z和__xc_a、__xc_z到底代表了什么呢？在cinitexe.c文件中有如下代码：<br>#pragma data_seg(".CRT$XIA")<br>_CRTALLOC(".CRT$XIA") _PVFV __xi_a[] = { NULL };<br>&nbsp;<br>#pragma data_seg(".CRT$XIZ")<br>_CRTALLOC(".CRT$XIZ") _PVFV __xi_z[] = { NULL };/* C initializers */<br>&nbsp;<br>#pragma data_seg(".CRT$XCA")<br>_CRTALLOC(".CRT$XCA") _PVFV __xc_a[] = { NULL };<br>&nbsp;<br>#pragma data_seg(".CRT$XCZ")<br>_CRTALLOC(".CRT$XCZ") _PVFV __xc_z[] = { NULL };/* C++ initializers */<br>#pragma comment(linker, "/merge:.CRT=.data")<br>可以看出这四个变量分别在数据段.CRT$XIA、.CRT$XIZ、.CRT$XCA、.CRT$XCZ中。当连接器布局代码时，它按根据的名称，按照字母排序的规则，排列所有段。这样在段.CRT$XIA中的变量出现在段.CRT$XIZ所有变量之前，从而形成链表。对于.CRT$XCA、.CRT$XCZ数据段同理。最后这四个数据段被合并到.data数据段中。<br>再看看这些变量的类型，typedef void (__cdecl *_PVFV)(void); 所以这些变量组成了2个初始化函数指针链表。<br>调试过程中，看到__xc_a、__xc_z链表中，指向的初始化函数很多是构造函数，如：<br>static std::_Init_locks initlocks;<br>static filebuf fout(_cpp_stdout);<br>extern _CRTDATA2 ostream cout(&amp;fout);<br>cout对象也在此时被构造。<br>对于析构函数的调用也是采用相同的方式，只是此时每一种初始化，都有一种终止函数与之对应。</p>
<p>4、 总结<br>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 编译、连接程序时，编译器将所有全局对象的初始化函数放入.CRT$Xx中，连接器将所有的.CRT$XCx段合并成为.rdata数据段。在.CRT$XCA 到 .CRT$XCZ的所有段的数据组成初始化函数指针列表。<br>l&nbsp;&nbsp; 函数执行时，_initterm( __xc_a, __xc_z )函数调用所有的初始化函数。构造全局对象。构造对象完毕，调用atexit函数来保证析构函数的调用。Modern C++ Design就是通过控制调用atexit函数来决定对象的析构顺序的。<br>l&nbsp;&nbsp; 对于静态对象使用atexit来保证析构函数的调用。<br>l&nbsp;&nbsp;&nbsp; 程序结束时，调用exit来析构全局对象或静态对象。</p>
<p><br>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/coffeemay/archive/2006/11/19/1395250.aspx">http://blog.csdn.net/coffeemay/archive/2006/11/19/1395250.aspx</a></p>
<img src ="http://www.cppblog.com/mmdengwo/aggbug/145302.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-29 11:06 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/29/145302.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>debug和release的区别</title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/29/145297.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Fri, 29 Apr 2011 02:48:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/29/145297.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/145297.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/29/145297.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/145297.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/145297.html</trackback:ping><description><![CDATA[<p>Debug和Release有什么区别?怎么把Debug转成Release ?<br>1。Debug和Release有什么区别，为什么要使用Release版本！&nbsp; <br>2。怎么把Debug转成Release&nbsp; </p>
<p>转载：&nbsp; </p>
<p>Debug版本包括调试信息，所以要比Release版本大很多（可能大数百K至数M）。至于是否需要DLL支持，主要看你采用的编译选项。如果是基于ATL的，则Debug和Release版本对DLL的要求差不多。如果采用的编译选项为使用MFC动态库，则需要MFC42D.DLL等库支持，而Release版本需要MFC42.DLL支持。Release&nbsp;&nbsp; Build不对源代码进行调试，不考虑MFC的诊断宏，使用的是MFC&nbsp;&nbsp; Release库，编译十对应用程序的速度进行优化，而Debug&nbsp;&nbsp; Build则正好相反，它允许对源代码进行调试，可以定义和使用MFC的诊断宏，采用MFC&nbsp;&nbsp; Debug库，对速度没有优化。&nbsp;&nbsp;&nbsp; </p>
<p><br>一、Debug&nbsp;&nbsp; 和&nbsp;&nbsp; Release&nbsp;&nbsp; 编译方式的本质区别&nbsp; </p>
<p>Debug&nbsp;&nbsp; 通常称为调试版本，它包含调试信息，并且不作任何优化，便于程序员调试程序。Release&nbsp;&nbsp; 称为发布版本，它往往是进行了各种优化，使得程序在代码大小和运行速度上都是最优的，以便用户很好地使用。&nbsp; <br>Debug&nbsp;&nbsp; 和&nbsp;&nbsp; Release&nbsp;&nbsp; 的真正秘密，在于一组编译选项。下面列出了分别针对二者的选项（当然除此之外还有其他一些，如/Fd&nbsp;&nbsp; /Fo，但区别并不重要，通常他们也不会引起&nbsp;&nbsp; Release&nbsp;&nbsp; 版错误，在此不讨论）&nbsp; </p>
<p>Debug&nbsp;&nbsp; 版本：&nbsp; <br>/MDd&nbsp;&nbsp; /MLd&nbsp;&nbsp; 或&nbsp;&nbsp; /MTd&nbsp;&nbsp; 使用&nbsp;&nbsp; Debug&nbsp;&nbsp; runtime&nbsp;&nbsp; library(调试版本的运行时刻函数库)&nbsp; <br>/Od&nbsp;&nbsp; 关闭优化开关&nbsp; <br>/D&nbsp;&nbsp; "_DEBUG"&nbsp;&nbsp; 相当于&nbsp;&nbsp; #define&nbsp;&nbsp; _DEBUG,打开编译调试代码开关(主要针对&nbsp; <br>assert函数)&nbsp; <br>/ZI&nbsp;&nbsp; 创建&nbsp;&nbsp; Edit&nbsp;&nbsp; and&nbsp;&nbsp; continue(编辑继续)数据库，这样在调试过&nbsp; <br>程中如果修改了源代码不需重新编译&nbsp; <br>/GZ&nbsp;&nbsp; 可以帮助捕获内存错误&nbsp; <br>/Gm&nbsp;&nbsp; 打开最小化重链接开关，减少链接时间&nbsp; </p>
<p>Release&nbsp;&nbsp; 版本：&nbsp;&nbsp;&nbsp; <br>/MD&nbsp;&nbsp; /ML&nbsp;&nbsp; 或&nbsp;&nbsp; /MT&nbsp;&nbsp; 使用发布版本的运行时刻函数库&nbsp; <br>/O1&nbsp;&nbsp; 或&nbsp;&nbsp; /O2&nbsp;&nbsp; 优化开关，使程序最小或最快&nbsp; <br>/D&nbsp;&nbsp; "NDEBUG"&nbsp;&nbsp; 关闭条件编译调试代码开关(即不编译assert函数)&nbsp; <br>/GF&nbsp;&nbsp; 合并重复的字符串，并将字符串常量放到只读内存，防止&nbsp; <br>被修改&nbsp; </p>
<p>实际上，Debug&nbsp;&nbsp; 和&nbsp;&nbsp; Release&nbsp;&nbsp; 并没有本质的界限，他们只是一组编译选项的集合，编译器只是按照预定的选项行动。事实上，我们甚至可以修改这些选项，从而得到优化过的调试版本或是带跟踪语句的发布版本。&nbsp; </p>
<p>二、哪些情况下&nbsp;&nbsp; Release&nbsp;&nbsp; 版会出错&nbsp; </p>
<p>有了上面的介绍，我们再来逐个对照这些选项看看&nbsp;&nbsp; Release&nbsp;&nbsp; 版错误是怎样产生的&nbsp; </p>
<p>1.&nbsp;&nbsp; Runtime&nbsp;&nbsp; Library：链接哪种运行时刻函数库通常只对程序的性能产生影响。调试版本的&nbsp;&nbsp; Runtime&nbsp;&nbsp; Library&nbsp;&nbsp; 包含了调试信息，并采用了一些保护机制以帮助发现错误，因此性能不如发布版本。编译器提供的&nbsp;&nbsp; Runtime&nbsp;&nbsp; Library&nbsp;&nbsp; 通常很稳定，不会造成&nbsp;&nbsp; Release&nbsp;&nbsp; 版错误；倒是由于&nbsp;&nbsp; Debug&nbsp;&nbsp; 的&nbsp;&nbsp; Runtime&nbsp;&nbsp; Library&nbsp;&nbsp; 加强了对错误的检测，如堆内存分配，有时会出现&nbsp;&nbsp; Debug&nbsp;&nbsp; 有错但&nbsp;&nbsp; Release&nbsp;&nbsp; 正常的现象。应当指出的是，如果&nbsp;&nbsp; Debug&nbsp;&nbsp; 有错，即使&nbsp;&nbsp; Release&nbsp;&nbsp; 正常，程序肯定是有&nbsp;&nbsp; Bug&nbsp;&nbsp; 的，只不过可能是&nbsp;&nbsp; Release&nbsp;&nbsp; 版的某次运行没有表现出来而已。&nbsp; </p>
<p>2.&nbsp;&nbsp; 优化：这是造成错误的主要原因，因为关闭优化时源程序基本上是直接翻译的，而打开优化后编译器会作出一系列假设。这类错误主要有以下几种：&nbsp; </p>
<p>(1)&nbsp;&nbsp; 帧指针(Frame&nbsp;&nbsp; Pointer)省略（简称&nbsp;&nbsp; FPO&nbsp;&nbsp; ）：在函数调用过程中，所有调用信息（返回地址、参数）以及自动变量都是放在栈中的。若函数的声明与实现不同（参数、返回值、调用方式），就会产生错误————但&nbsp;&nbsp; Debug&nbsp;&nbsp; 方式下，栈的访问通过&nbsp;&nbsp; EBP&nbsp;&nbsp; 寄存器保存的地址实现，如果没有发生数组越界之类的错误（或是越界&#8220;不多&#8221;），函数通常能正常执行；Release&nbsp;&nbsp; 方式下，优化会省略&nbsp;&nbsp; EBP&nbsp;&nbsp; 栈基址指针，这样通过一个全局指针访问栈就会造成返回地址错误是程序崩溃。C++&nbsp;&nbsp; 的强类型特性能检查出大多数这样的错误，但如果用了强制类型转换，就不行了。你可以在&nbsp;&nbsp; Release&nbsp;&nbsp; 版本中强制加入&nbsp;&nbsp; /Oy-&nbsp;&nbsp; 编译选项来关掉帧指针省略，以确定是否此类错误。此类错误通常有：&nbsp; </p>
<p>●&nbsp;&nbsp; MFC&nbsp;&nbsp; 消息响应函数书写错误。正确的应为&nbsp; <br>afx_msg&nbsp;&nbsp; LRESULT&nbsp;&nbsp; OnMessageOwn(WPARAM&nbsp;&nbsp; wparam,&nbsp;&nbsp; LPARAM&nbsp;&nbsp; lparam);&nbsp; <br>ON_MESSAGE&nbsp;&nbsp; 宏包含强制类型转换。防止这种错误的方法之一是重定义&nbsp;&nbsp; ON_MESSAGE&nbsp;&nbsp; 宏，把下列代码加到&nbsp;&nbsp; stdafx.h&nbsp;&nbsp; 中（在#include&nbsp;&nbsp; "afxwin.h"之后）,函数原形错误时编译会报错&nbsp; <br>#undef&nbsp;&nbsp; ON_MESSAGE&nbsp; <br>#define&nbsp;&nbsp; ON_MESSAGE(message,&nbsp;&nbsp; memberFxn)&nbsp;&nbsp; \&nbsp; <br>{&nbsp;&nbsp; message,&nbsp;&nbsp; 0,&nbsp;&nbsp; 0,&nbsp;&nbsp; 0,&nbsp;&nbsp; AfxSig_lwl,&nbsp;&nbsp; \&nbsp; <br>(AFX_PMSG)(AFX_PMSGW)(static_cast&lt;&nbsp;&nbsp; LRESULT&nbsp;&nbsp; (AFX_MSG_CALL&nbsp;&nbsp; \&nbsp; <br>CWnd::*)(WPARAM,&nbsp;&nbsp; LPARAM)&nbsp;&nbsp; &gt;&nbsp;&nbsp; (&amp;memberFxn)&nbsp;&nbsp; },&nbsp; </p>
<p>(2)&nbsp;&nbsp; volatile&nbsp;&nbsp; 型变量：volatile&nbsp;&nbsp; 告诉编译器该变量可能被程序之外的未知方式修改（如系统、其他进程和线程）。优化程序为了使程序性能提高，常把一些变量放在寄存器中（类似于&nbsp;&nbsp; register&nbsp;&nbsp; 关键字），而其他进程只能对该变量所在的内存进行修改，而寄存器中的值没变。如果你的程序是多线程的，或者你发现某个变量的值与预期的不符而你确信已正确的设置了，则很可能遇到这样的问题。这种错误有时会表现为程序在最快优化出错而最小优化正常。把你认为可疑的变量加上&nbsp;&nbsp; volatile&nbsp;&nbsp; 试试。&nbsp; </p>
<p>(3)&nbsp;&nbsp; 变量优化：优化程序会根据变量的使用情况优化变量。例如，函数中有一个未被使用的变量，在&nbsp;&nbsp; Debug&nbsp;&nbsp; 版中它有可能掩盖一个数组越界，而在&nbsp;&nbsp; Release&nbsp;&nbsp; 版中，这个变量很可能被优化调，此时数组越界会破坏栈中有用的数据。当然，实际的情况会比这复杂得多。与此有关的错误有：&nbsp; <br>●&nbsp;&nbsp; 非法访问，包括数组越界、指针错误等。例如&nbsp; <br>void&nbsp;&nbsp; fn(void)&nbsp; <br>{&nbsp; <br>int&nbsp;&nbsp; i;&nbsp; <br>i&nbsp;&nbsp; =&nbsp;&nbsp; 1;&nbsp; <br>int&nbsp;&nbsp; a[4];&nbsp; <br>{&nbsp; <br>int&nbsp;&nbsp; j;&nbsp; <br>j&nbsp;&nbsp; =&nbsp;&nbsp; 1;&nbsp; <br>}&nbsp; <br>a[-1]&nbsp;&nbsp; =&nbsp;&nbsp; 1;//当然错误不会这么明显，例如下标是变量&nbsp; <br>a[4]&nbsp;&nbsp; =&nbsp;&nbsp; 1;&nbsp; <br>}&nbsp; <br>j&nbsp;&nbsp; 虽然在数组越界时已出了作用域，但其空间并未收回，因而&nbsp;&nbsp; i&nbsp;&nbsp; 和&nbsp;&nbsp; j&nbsp;&nbsp; 就会掩盖越界。而&nbsp;&nbsp; Release&nbsp;&nbsp; 版由于&nbsp;&nbsp; i、j&nbsp;&nbsp; 并未其很大作用可能会被优化掉，从而使栈被破坏。&nbsp; </p>
<p>3.&nbsp;&nbsp; _DEBUG&nbsp;&nbsp; 与&nbsp;&nbsp; NDEBUG&nbsp;&nbsp; ：当定义了&nbsp;&nbsp; _DEBUG&nbsp;&nbsp; 时，assert()&nbsp;&nbsp; 函数会被编译，而&nbsp;&nbsp; NDEBUG&nbsp;&nbsp; 时不被编译。除此之外，VC++中还有一系列断言宏。这包括：&nbsp; </p>
<p>ANSI&nbsp;&nbsp; C&nbsp;&nbsp; 断言&nbsp;&nbsp; void&nbsp;&nbsp; assert(int&nbsp;&nbsp; expression&nbsp;&nbsp; );&nbsp; <br>C&nbsp;&nbsp; Runtime&nbsp;&nbsp; Lib&nbsp;&nbsp; 断言&nbsp;&nbsp; _ASSERT(&nbsp;&nbsp; booleanExpression&nbsp;&nbsp; );&nbsp; <br>_ASSERTE(&nbsp;&nbsp; booleanExpression&nbsp;&nbsp; );&nbsp; <br>MFC&nbsp;&nbsp; 断言&nbsp;&nbsp; ASSERT(&nbsp;&nbsp; booleanExpression&nbsp;&nbsp; );&nbsp; <br>VERIFY(&nbsp;&nbsp; booleanExpression&nbsp;&nbsp; );&nbsp; <br>ASSERT_VALID(&nbsp;&nbsp; pObject&nbsp;&nbsp; );&nbsp; <br>ASSERT_KINDOF(&nbsp;&nbsp; classname,&nbsp;&nbsp; pobject&nbsp;&nbsp; );&nbsp; <br>ATL&nbsp;&nbsp; 断言&nbsp;&nbsp; ATLASSERT(&nbsp;&nbsp; booleanExpression&nbsp;&nbsp; );&nbsp; <br>此外，TRACE()&nbsp;&nbsp; 宏的编译也受&nbsp;&nbsp; _DEBUG&nbsp;&nbsp; 控制。&nbsp; </p>
<p>所有这些断言都只在&nbsp;&nbsp; Debug版中才被编译，而在&nbsp;&nbsp; Release&nbsp;&nbsp; 版中被忽略。唯一的例外是&nbsp;&nbsp; VERIFY()&nbsp;&nbsp; 。事实上，这些宏都是调用了&nbsp;&nbsp; assert()&nbsp;&nbsp; 函数，只不过附加了一些与库有关的调试代码。如果你在这些宏中加入了任何程序代码，而不只是布尔表达式（例如赋值、能改变变量值的函数调用&nbsp;&nbsp; 等），那么&nbsp;&nbsp; Release&nbsp;&nbsp; 版都不会执行这些操作，从而造成错误。初学者很容易犯这类错误，查找的方法也很简单，因为这些宏都已在上面列出，只要利用&nbsp;&nbsp; VC++&nbsp;&nbsp; 的&nbsp;&nbsp; Find&nbsp;&nbsp; in&nbsp;&nbsp; Files&nbsp;&nbsp; 功能在工程所有文件中找到用这些宏的地方再一一检查即可。另外，有些高手可能还会加入&nbsp;&nbsp; #ifdef&nbsp;&nbsp; _DEBUG&nbsp;&nbsp; 之类的条件编译，也要注意一下。&nbsp; <br>顺便值得一提的是&nbsp;&nbsp; VERIFY()&nbsp;&nbsp; 宏，这个宏允许你将程序代码放在布尔表达式里。这个宏通常用来检查&nbsp;&nbsp; Windows&nbsp;&nbsp; API&nbsp;&nbsp; 的返回值。有些人可能为这个原因而滥用&nbsp;&nbsp; VERIFY()&nbsp;&nbsp; ，事实上这是危险的，因为&nbsp;&nbsp; VERIFY()&nbsp;&nbsp; 违反了断言的思想，不能使程序代码和调试代码完全分离，最终可能会带来很多麻烦。因此，专家们建议尽量少用这个宏。&nbsp; </p>
<p>4.&nbsp;&nbsp; /GZ&nbsp;&nbsp; 选项：这个选项会做以下这些事&nbsp; </p>
<p>(1)&nbsp;&nbsp; 初始化内存和变量。包括用&nbsp;&nbsp; 0xCC&nbsp;&nbsp; 初始化所有自动变量，0xCD&nbsp;&nbsp; (&nbsp;&nbsp; Cleared&nbsp;&nbsp; Data&nbsp;&nbsp; )&nbsp;&nbsp; 初始化堆中分配的内存（即动态分配的内存，例如&nbsp;&nbsp; new&nbsp;&nbsp; ），0xDD&nbsp;&nbsp; (&nbsp;&nbsp; Dead&nbsp;&nbsp; Data&nbsp;&nbsp; )&nbsp;&nbsp; 填充已被释放的堆内存（例如&nbsp;&nbsp; delete&nbsp;&nbsp; ），0xFD(&nbsp;&nbsp; deFencde&nbsp;&nbsp; Data&nbsp;&nbsp; )&nbsp;&nbsp; 初始化受保护的内存（debug&nbsp;&nbsp; 版在动态分配内存的前后加入保护内存以防止越界访问），其中括号中的词是微软建议的助记词。这样做的好处是这些值都很大，作为指针是不可能的（而且&nbsp;&nbsp; 32&nbsp;&nbsp; 位系统中指针很少是奇数值，在有些系统中奇数的指针会产生运行时错误），作为数值也很少遇到，而且这些值也很容易辨认，因此这很有利于在&nbsp;&nbsp; Debug&nbsp;&nbsp; 版中发现&nbsp;&nbsp; Release&nbsp;&nbsp; 版才会遇到的错误。要特别注意的是，很多人认为编译器会用&nbsp;&nbsp; 0&nbsp;&nbsp; 来初始化变量，这是错误的（而且这样很不利于查找错误）。&nbsp; <br>(2)&nbsp;&nbsp; 通过函数指针调用函数时，会通过检查栈指针验证函数调用的匹配性。（防止原形不匹配）&nbsp; <br>(3)&nbsp;&nbsp; 函数返回前检查栈指针，确认未被修改。（防止越界访问和原形不匹配，与第二项合在一起可大致模拟帧指针省略&nbsp;&nbsp; FPO&nbsp;&nbsp; ）&nbsp; </p>
<p>通常&nbsp;&nbsp; /GZ&nbsp;&nbsp; 选项会造成&nbsp;&nbsp; Debug&nbsp;&nbsp; 版出错而&nbsp;&nbsp; Release&nbsp;&nbsp; 版正常的现象，因为&nbsp;&nbsp; Release&nbsp;&nbsp; 版中未初始化的变量是随机的，这有可能使指针指向一个有效地址而掩盖了非法访问。&nbsp; </p>
<p>除此之外，/Gm&nbsp;&nbsp; /GF&nbsp;&nbsp; 等选项造成错误的情况比较少，而且他们的效果显而易见，比较容易发现。&nbsp; <br>--------------------------------------------------------------&nbsp; <br>Release是发行版本,比Debug版本有一些优化，文件比Debug文件小&nbsp; <br>Debug是调试版本，包括的程序信息更多&nbsp; <br>Release方法：&nbsp; <br>build-&gt;batch&nbsp;&nbsp; build-&gt;build就OK.&nbsp; </p>
<p>-----------------------------------------------------&nbsp; </p>
<p>一、"Debug是调试版本，包括的程序信息更多"&nbsp; </p>
<p>补充：只有DEBUG版的程序才能设置断点、单步执行、使用TRACE/ASSERT等调试输出语句。REALEASE不包含任何调试信息，所以体积小、运行速度快。&nbsp; </p>
<p>二、一般发布release的方法除了hzh_shat(水)&nbsp;&nbsp; 所说的之外，还可以project-&gt;Set&nbsp;&nbsp; Active&nbsp;&nbsp; Config，选中release版本。此后，按F5或F7编译所得的结果就是release版本。</p>
<p><br>Trackback: <a href="http://tb.donews.net/TrackBack.aspx?PostId=131016">http://tb.donews.net/TrackBack.aspx?PostId=131016</a></p>
<p>----------------------------------------------------------------------------------------<br>-------------------------------------------------------------------------------------------<br>VC下关于debug和release的不同的讨论<br>在使用VC开发软件的过程中，正当要享受那种兴奋的时候突然发现：release与debug运行结果不一致，甚至出错，而release又不方便调试，真的是当头一棒啊，可是疼归疼，问题总要解决，下面将讲述一下我的几点经验，看看是不是其中之一：</p>
<p>1. 变量。<br>大家都知道，debug跟release在初始化变量时所做的操作是不同的，debug是将每个字节位都赋成0xcc(注1)，而release的赋值近似于随机(我想是直接从内存中分配的，没有初始化过)。这样就明确了，如果你的程序中的某个变量没被初始化就被引用，就很有可能出现异常：用作控制变量将导致流程导向不一致；用作数组下标将会使程序崩溃；更加可能是造成其他变量的不准确而引起其他的错误。所以在声明变量后马上对其初始化一个默认的值是最简单有效的办法，否则项目大了你找都没地方找。代码存在错误在debug方式下可能会忽略而不被察觉到，如debug方式下数组越界也大多不会出错，在release中就暴露出来了，这个找起来就比较难了:( 还是自己多加注意吧</p>
<p>2. 自定义消息的消息参数。<br>MFC为我们提供了很好的消息机制，更增加了自定义消息，好处我就不用多说了。这也存在debug跟release的问题吗？答案是肯定的。在自定义消息的函数体声明时，时常会看到这样的写法：afx_msg LRESULT OnMessageOwn(); Debug情况下一般不会有任何问题，而当你在Release下且多线程或进程间使用了消息传递时就会导致无效句柄之类的错误。导致这个错误直接原因是消息体的参数没有添加，即应该写成：afx_msg LRESULT OnMessageOwn(WPARAM wparam, LPARAM lparam); (注2)</p>
<p>3. release模式下不出错，但debug模式下报错。<br>这种情况下大多也是因为代码书写不正确引起的，查看MFC的源码，可以发现好多ASSERT的语句(断言)，这个宏只是在debug模式下才有效，那么就清楚了，release版不报错是忽略了错误而不是没有错误，这可能存在很大的隐患，因为是Debug模式下，比较方便调试，好好的检查自己的代码，再此就不多说了。</p>
<p>4. ASSERT, VERIFY, TRACE&#8230;&#8230;&#8230;.调试宏<br>这种情况很容易解释。举个例子：请在VC下输入ASSERT然后选中按F12跳到宏定义的地方，这里你就能够发现Debug中ASSERT要执行AfxAssertFailedLine，而Release下的宏定义却为&#8221;#define ASSERT(f) ((void)0)&#8221;。所以注意在这些调试宏的语句不要用程序相关变量如i++写操作的语句。VERIFY是个例外，&#8221;#define VERIFY(f) ((void)(f))&#8221;，即执行，这里的作用就不多追究了，有兴趣可自己研究:)。</p>
<p>总结：<br>Debug与Release不同的问题在刚开始编写代码时会经常发生，99%是因为你的代码书写错误而导致的，所以不要动不动就说系统问题或编译器问题，努力找找自己的原因才是根本。我从前就常常遇到这情况，经历过一次次的教训后我就开始注意了，现在我所写过的代码我已经好久没遇到这种问题了。下面是几个避免的方面，即使没有这种问题也应注意一下：</p>
<p>1. 注意变量的初始化，尤其是指针变量，数组变量的初始化(很大的情况下另作考虑了)。<br>2. 自定义消息及其他声明的标准写法<br>3. 使用调试宏时使用后最好注释掉<br>4. 尽量使用try - catch(&#8230;)<br>5. 尽量使用模块，不但表达清楚而且方便调试。<br>注1：<br>afc(afc) 网友提供：<br>debug版初始化成0xcc是因为0xcc在x86下是一条int 3单步中断指令，这样程序如果跑飞了遇到0xcc就会停下来，这和单片机编程时一般将没用的代码空间填入jmp 0000语句是一样地<br>注2：<br>不知大家有没有遇到过这种情况，具体原因我也不太清楚，是不是调用时按着默认的参数多分配了WPARAM+LPARAM的空间而破坏了应用程序的内存空间?还请高手来补充。<br>NightWolf 网友提供：我遇见过，好像是在函数调用的时候参数入栈的问题。因为MFC的消息使用宏写的，所以如果定义了OnMessage()的函数，编译能够通过，但是调用一次后，堆栈指针发生了偏移。然后就。。。<br>------------------------------------------------------------<br>_________________________________________________________<br>---------------------------------------------------------<br>DEBUG和RELEASE 版本差异及调试相关问题：</p>
<p>I.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 内存分配问题</p>
<p>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 变量未初始化。下面的程序在debug中运行的很好。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thing * search(thing * something)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BOOL found;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i = 0; i &lt; whatever.GetSize(); i++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(whatever[i]-&gt;field == something-&gt;field)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { /* found it */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; found = TRUE;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } /* found it */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp; if(found)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return whatever[i];<br>&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return NULL;<br>而在release中却不行，因为debug中会自动给变量初始化found=FALSE,而在release版中则不会。所以尽可能的给变量、类或结构初始化。</p>
<p>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 数据溢出的问题&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如：char buffer[10];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int counter;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lstrcpy(buffer, "abcdefghik");</p>
<p>在debug版中buffer的NULL覆盖了counter的高位，但是除非counter&gt;16M,什么问题也没有。但是在release版中，counter可能被放在寄存器中，这样NULL就覆盖了buffer下面的空间，可能就是函数的返回地址，这将导致ACCESS ERROR。</p>
<p>3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DEBUG版和RELEASE版的内存分配方式是不同的 。如果你在DEBUG版中申请&nbsp;&nbsp;&nbsp; ele 为 6*sizeof(DWORD)=24bytes,实际上分配给你的是32bytes（debug版以32bytes为单位分配）， 而在release版，分配给你的就是24bytes（release版以8bytes为单位），所以在debug版中如果你写ele[6],可能不会有什么问题，而在release版中，就有ACCESS VIOLATE。</p>
<p>II.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT和VERIFY</p>
<p>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT在Release版本中是不会被编译的。</p>
<p>ASSERT宏是这样定义的</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #ifdef _DEBUG<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #define ASSERT(x) if( (x) == 0) report_assert_failure()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #define ASSERT(x)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #endif<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 实际上复杂一些，但无关紧要。假如你在这些语句中加了程序中必须要有的代码<br>比如</p>
<p>ASSERT(pNewObj = new CMyClass);</p>
<p>pNewObj-&gt;MyFunction();</p>
<p>这种时候Release版本中的pNewObj不会分配到空间</p>
<p>所以执行到下一个语句的时候程序会报该程序执行了非法操作的错误。这时可以用VERIFY ：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #ifdef _DEBUG<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #define VERIFY(x) if( (x) == 0) report_assert_failure()<br>&nbsp;&nbsp;&nbsp;&nbsp; #else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #define VERIFY(x) (x)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #endif<br>这样的话，代码在release版中就可以执行了。</p>
<p>III.&nbsp;&nbsp;&nbsp; 参数问题：</p>
<p>自定义消息的处理函数，必须定义如下：</p>
<p>afx_msg LRESULT OnMyMessage(WPARAM, LPARAM);</p>
<p>返回值必须是HRESULT型，否则Debug会过，而Release出错</p>
<p>IV.&nbsp;&nbsp; 内存分配</p>
<p>保证数据创建和清除的统一性：如果一个DLL提供一个能够创建数据的函数，那么这个DLL同时应该提供一个函数销毁这些数据。数据的创建和清除应该在同一个层次上。</p>
<p>V.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DLL的灾难</p>
<p>人们将不同版本DLL混合造成的不一致性形象的称为 &#8220;动态连接库的地狱&#8220;(DLL Hell) ，甚至微软自己也这么说(<a href="http://msdn.microsoft.com/library/techart/dlldanger1.htm">http://msdn.microsoft.com/library/techart/dlldanger1.htm</a>)。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果你的程序使用你自己的DLL时请注意：</p>
<p>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不能将debug和release版的DLL混合在一起使用。debug都是debug版，release版都是release版。</p>
<p>解决办法是将debug和release的程序分别放在主程序的debug和release目录下</p>
<p><br>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 千万不要以为静态连接库会解决问题，那只会使情况更糟糕。</p>
<p>VI.&nbsp;&nbsp; RELEASE板中的调试 ：</p>
<p>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 将ASSERT() 改为 VERIFY() 。找出定义在"#ifdef _DEBUG"中的代码，如果在RELEASE版本中需要这些代码请将他们移到定义外。查找TRACE(...)中代码，因为这些代码在RELEASE中也不被编译。 请认真检查那些在RELEASE中需要的代码是否并没有被便宜。</p>
<p>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 变量的初始化所带来的不同，在不同的系统，或是在DEBUG/RELEASE版本间都存在这样的差异，所以请对变量进行初始化。</p>
<p>3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 是否在编译时已经有了警告?请将警告级别设置为3或4,然后保证在编译时没有警告出现.</p>
<p>VII.&nbsp;&nbsp;&nbsp; 将Project Settings" 中 "C++/C " 项目下优化选项改为Disbale（Debug）。编译器的优化可能导致许多意想不到的错误，请参考<a href="http://www.pgh.net/~newcomer/debug_release.htm">http://www.pgh.net/~newcomer/debug_release.htm</a></p>
<p>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 此外对RELEASE版本的软件也可以进行调试，请做如下改动： </p>
<p>在"Project Settings" 中 "C++/C " 项目下设置 "category" 为 "General" 并且将"Debug Info"设置为 "Program Database"。</p>
<p>在"Link"项目下选中"Generate Debug Info"检查框。 </p>
<p>"Rebuild All" </p>
<p>如此做法会产生的一些限制： </p>
<p>无法获得在MFC DLL中的变量的值。 </p>
<p>必须对该软件所使用的所有DLL工程都进行改动。 </p>
<p>另：</p>
<p>MS BUG：MS的一份技术文档中表明，在VC5中对于DLL的"Maximize Speed"优化选项并未被完全支持，因此这将会引起内存错误并导致程序崩溃。</p>
<p>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.sysinternals.com/">www.sysinternals.com</a>有一个程序DebugView，用来捕捉OutputDebugString的输出，运行起来后（估计是自设为system debugger）就可以观看所有程序的OutputDebugString的输出。此后，你可以脱离VC来运行你的程序并观看调试信息。 </p>
<p>3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有一个叫Gimpel Lint的静态代码检查工具，据说比较好用。</p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/Jiao2_vc/archive/2009/03/07/3967364.aspx">http://blog.csdn.net/Jiao2_vc/archive/2009/03/07/3967364.aspx</a></p>
<img src ="http://www.cppblog.com/mmdengwo/aggbug/145297.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-29 10:48 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/29/145297.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>_STDCALL&amp;_CDECL 调用约定</title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/29/145296.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Fri, 29 Apr 2011 02:45:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/29/145296.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/145296.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/29/145296.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/145296.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/145296.html</trackback:ping><description><![CDATA[<span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px; LINE-HEIGHT: 21px; FONT-FAMILY: verdana, sans-serif; TEXT-ALIGN: left">
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px"><font size=-1><br class=Apple-interchange-newline>&nbsp;&nbsp;&nbsp;&nbsp;_stdcall将参数压栈是按C语言的顺序（从右到左），但与C 语言不同的是它是由被调用者将参数从栈中清除的,所以它的编译文件比_cdecl小。_stdcall是Windows API函数中默认的调用约定，VB、VFP等也采用这个约定。<span class=Apple-converted-space>&nbsp;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;_cdecl是C语言采用的默认调用方法，它的优点是支持printf这样的可变参数调用。<span class=Apple-converted-space>&nbsp;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;另外，VC++对于两种调用方法的名称转换方法也不同。</font></p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px"><span style="FONT-SIZE: small; LINE-HEIGHT: 19px"></span></p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　在C语言中，假设我们有这样的一个函数：<br>　　<br>　　int function(int a,int b)<br>　　<br>　　调用时只要用result = function(1,2)这样的方式就可以使用这个函数。但是，当高级语言被编译成计算机可以识别的机器码时，有一个问题就凸现出来：在CPU中，计算机没有办法知道一个函数调用需要多少个、什么样的参数，也没有硬件可以保存这些参数。也就是说，计算机不知道怎么给这个函数传递参数，传递参数的工作必须由函数调用者和函数本身来协调。为此，计算机提供了一种被称为栈的数据结构来支持参数传递。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　栈是一种先进后出的数据结构，栈有一个存储区、一个栈顶指针。栈顶指针指向堆栈中第一个可用的数据项（被称为栈顶）。用户可以在栈顶上方向栈中加入数据，这个操作被称为压栈(Push)，压栈以后，栈顶自动变成新加入数据项的位置，栈顶指针也随之修改。用户也可以从堆栈中取走栈顶，称为弹出栈(pop)，弹出栈后，栈顶下的一个元素变成栈顶，栈顶指针随之修改。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　函数调用时，调用者依次把参数压栈，然后调用函数，函数被调用以后，在堆栈中取得数据，并进行计算。函数计算结束以后，或者调用者、或者函数本身修改堆栈，使堆栈恢复原装。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　在参数传递中，有两个很重要的问题必须得到明确说明：<br>　　<br>　　当参数个数多于一个时，按照什么顺序把参数压入堆栈<span class=Apple-converted-space>&nbsp;</span><br>　　函数调用后，由谁来把堆栈恢复原装<span class=Apple-converted-space>&nbsp;</span><br>　　在高级语言中，通过函数调用约定来说明这两个问题。常见的调用约定有：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　stdcall<span class=Apple-converted-space>&nbsp;</span><br>　　cdecl<span class=Apple-converted-space>&nbsp;</span><br>　　fastcall<span class=Apple-converted-space>&nbsp;</span><br>　　thiscall<span class=Apple-converted-space>&nbsp;</span><br>　　naked call</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　stdcall调用约定<br>　　stdcall很多时候被称为pascal调用约定，因为pascal是早期很常见的一种教学用计算机程序设计语言，其语法严谨，使用的函数调用约定就是stdcall。在Microsoft C++系列的C/C++编译器中，常常用PASCAL宏来声明这个调用约定，类似的宏还有WINAPI和CALLBACK。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　stdcall调用约定声明的语法为(以前文的那个函数为例）：<br>　　<br>　　int __stdcall function(int a,int b)<br>　　<br>　　stdcall的调用约定意味着：1）参数从右向左压入堆栈，2）函数自身修改堆栈 3)函数名自动加前导的下划线，后面紧跟一个@符号，其后紧跟着参数的尺寸</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　以上述这个函数为例，参数b首先被压栈，然后是参数a，函数调用function(1,2)调用处翻译成汇编语言将变成：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　push 2　　　　　　　 第二个参数入栈<br>　　push 1　　　　　　　 第一个参数入栈<br>　　call function　　　　调用参数，注意此时自动把cs:eip入栈</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　而对于函数自身，则可以翻译为：<span class=Apple-converted-space>&nbsp;</span><br>　　push ebp　　　　　　 保存ebp寄存器，该寄存器将用来保存堆栈的栈顶指针，可以在函数退出时恢复<br>　　mov　ebp, esp　　　　保存堆栈指针<br>　　mov　eax,[ebp + 8H]　堆栈中ebp指向位置之前依次保存有ebp, cs:eip, a, b, ebp +8指向a<br>　　add　eax,[ebp + 0CH] 堆栈中ebp + 12处保存了b<br>　　mov　esp, ebp　　　　恢复esp<br>　　pop　ebp<br>　　ret　8</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　而在编译时，这个函数的名字被翻译成<a style="COLOR: rgb(51,102,153); TEXT-DECORATION: none" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#95;&#102;&#117;&#110;&#99;&#116;&#105;&#111;&#110;&#64;&#56;"><span style="COLOR: rgb(51,102,153); LINE-HEIGHT: 21px">_function@8</span></a></p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　注意不同编译器会插入自己的汇编代码以提供编译的通用性，但是大体代码如此。其中在函数开始处保留esp到ebp中，在函数结束恢复是编译器常用的方法。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　从函数调用看，2和1依次被push进堆栈，而在函数中又通过相对于ebp(即刚进函数时的堆栈指针）的偏移量存取参数。函数结束后，ret 8表示清理8个字节的堆栈，函数自己恢复了堆栈。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　<br>　　cdecl调用约定<br>　　cdecl调用约定又称为C调用约定，是C语言缺省的调用约定，它的定义语法是：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　int function (int a ,int b)　//不加修饰就是C调用约定<br>　　int __cdecl function(int a,int b)//明确指出C调用约定</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　在写本文时，出乎我的意料，发现cdecl调用约定的参数压栈顺序是和stdcall是一样的，参数首先由右向左压入堆栈。所不同的是，函数本身不清理堆栈，调用者负责清理堆栈。由于这种变化，C调用约定允许函数的参数的个数是不固定的，这也是C语言的一大特色。对于前面的function函数，使用cdecl后的汇编码变成：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　调用处<br>　　push 1<br>　　push 2<br>　　call function<br>　　add　esp, 8　　　　　注意：这里调用者在恢复堆栈</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　被调用函数_function处<br>　　push ebp　　　　　　 保存ebp寄存器，该寄存器将用来保存堆栈的栈顶指针，可以在函数退出时恢复<br>　　mov　ebp,esp　　　　 保存堆栈指针<br>　　mov　eax,[ebp + 8H]　堆栈中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向a<br>　　add　eax,[ebp + 0CH] 堆栈中ebp + 12处保存了b<br>　　mov　esp,ebp　　　　 恢复esp<br>　　pop　ebp<br>　　ret　　　　　　　　　注意，这里没有修改堆栈</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　MSDN中说，该修饰自动在函数名前加前导的下划线，因此函数名在符号表中被记录为_function，但是我在编译时似乎没有看到这种变化。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　由于参数按照从右向左顺序压栈，因此最开始的参数在最接近栈顶的位置，因此当采用不定个数参数时，第一个参数在栈中的位置肯定能知道，只要不定的参数个数能够根据第一个后者后续的明确的参数确定下来，就可以使用不定参数，例如对于CRT中的sprintf函数，定义为：<span class=Apple-converted-space>&nbsp;</span><br>　　int sprintf(char* buffer,const char* format,...)<br>　　由于所有的不定参数都可以通过format确定，因此使用不定个数的参数是没有问题的。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　fastcall<br>　　fastcall调用约定和stdcall类似，它意味着：<span class=Apple-converted-space>&nbsp;</span><br>　　<br>　　函数的第一个和第二个DWORD参数（或者尺寸更小的）通过ecx和edx传递，其他参数通过从右向左的顺序压栈<span class=Apple-converted-space>&nbsp;</span><br>　　被调用函数清理堆栈<span class=Apple-converted-space>&nbsp;</span><br>　　函数名修改规则同stdcall<span class=Apple-converted-space>&nbsp;</span><br>　　其声明语法为：int fastcall function(int a, int b)</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　thiscall<br>　　thiscall是唯一一个不能明确指明的函数修饰，因为thiscall不是关键字。它是C++类成员函数缺省的调用约定。由于成员函数调用还有一个this指针，因此必须特殊处理，thiscall意味着：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　参数从右向左入栈<span class=Apple-converted-space>&nbsp;</span><br>　　如果参数个数确定，this指针通过ecx传递给被调用者；如果参数个数不确定，this指针在所有参数压栈后被压入堆栈。对参数个数不定的，调用者清理堆栈，否则函数自己清理堆栈为了说明这个调用约定，定义如下类和使用代码：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　class A<br>　　{<br>　　public:<br>　　　 int function1(int a,int b);<br>　　　 int function2(int a,...);<br>　　};</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　int A::function1 (int a,int b)<br>　　{<br>　　　 return a+b;<br>　　}</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　#include &lt;stdarg.h&gt;<br>　　int A::function2(int a,...)<br>　　{<br>　　　 va_list ap;<br>　　　 va_start(ap,a);<br>　　　 int i;<br>　　　 int result = 0;<br>　　　 for(i = 0 ; i &lt; a ; i ++)<br>　　　 {<br>　　　　　result += va_arg(ap,int);<br>　　　 }<br>　　　 return result;<br>　　}</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　void callee()<br>　　{<br>　　　 A a;<br>　　　 a.function1(1, 2);<br>　　　 a.function2(3, 1, 2, 3);<br>　　}</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">callee函数被翻译成汇编后就变成：<span class=Apple-converted-space>&nbsp;</span><br>　　//函数function1调用<br>　　00401C1D　 push　　　　2<br>　　00401C1F　 push　　　　1<br>　　00401C21　 lea　　　　 ecx,[ebp-8]<br>　　00401C24　 call　　　　function1　　　　　注意，这里this没有被入栈</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　//函数function2调用<br>　　00401C29　 push　　　　3<br>　　00401C2B　 push　　　　2<br>　　00401C2D　 push　　　　1<br>　　00401C2F　 push　　　　3<br>　　00401C31　 lea　　　　 eax, [ebp-8]　　　 这里引入this指针<br>　　00401C34　 push　　　　eax<br>　　00401C35　 call　　　　function2<br>　　00401C3A　 add　　　　 esp, 14h<br>　　<br>　　可见，对于参数个数固定情况下，它类似于stdcall，不定时则类似cdecl</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　naked call<br>　　这是一个很少见的调用约定，一般程序设计者建议不要使用。编译器不会给这种函数增加初始化和清理代码，更特殊的是，你不能用return返回返回值，只能用插入汇编返回结果。这一般用于实模式驱动程序设计，假设定义一个求和的加法程序，可以定义为：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　__declspec(naked) int　add(int a,int b)<br>　　{<br>　　　 __asm mov eax,a<br>　　　 __asm add eax,b<br>　　　 __asm ret<span class=Apple-converted-space>&nbsp;</span><br>　　}</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　注意，这个函数没有显式的return返回值，返回通过修改eax寄存器实现，而且连退出函数的ret指令都必须显式插入。上面代码被翻译成汇编以后变成：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　mov eax,[ebp+8]<br>　　add eax,[ebp+12]<br>　　ret 8</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　 注意这个修饰是和__stdcall及cdecl结合使用的，前面是它和cdecl结合使用的代码，对于和stdcall结合的代码，则变成：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　__declspec(naked) int __stdcall function(int a,int b)<br>　 {<br>　　　　__asm mov eax,a<br>　　　　__asm add eax,b<br>　　　　__asm ret 8　　　　//注意后面的8<br>　　}</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　至于这种函数被调用，则和普通的cdecl及stdcall调用函数一致。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　函数调用约定导致的常见问题<br>　　如果定义的约定和使用的约定不一致，则将导致堆栈被破坏，导致严重问题，下面是两种常见的问题：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　函数原型声明和函数体定义不一致<span class=Apple-converted-space>&nbsp;</span><br>　　DLL导入函数时声明了不同的函数约定<span class=Apple-converted-space>&nbsp;</span><br>　　以后者为例，假设我们在dll种声明了一种函数为：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　__declspec(dllexport) int func(int a,int b);//注意，这里没有stdcall，使用的是cdecl<br>　　使用时代码为：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　typedef int (*WINAPI DLLFUNC)func(int a,int b);<br>　　hLib = LoadLibrary(...);</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　DLLFUNC func = (DLLFUNC)GetProcAddress(...)//这里修改了调用约定<br>　　result = func(1,2);//导致错误</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">　　由于调用者没有理解WINAPI的含义错误的增加了这个修饰，上述代码必然导致堆栈被破坏，MFC在编译时插入的checkesp函数将告诉你，堆栈被破坏</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">1)调用约定(Calling convention)</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; 决定函数参数传送时入栈和出栈的顺序，由调用者还是被调用者把参数弹出栈，以及编译器用来识别函数名字的修饰约定。函数调用约定有多种：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">1、__stdcall调用约定</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; 相当于16位动态库中经常使用的 PASCAL 调用约定。在32位的 VC++5.0 中PASCAL 调用约定不再被支持（实际上它已被定义为__stdcall。函数的参数自右向左通过栈传递，被调用的函数在返回前清理传送参数的内存栈。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; _stdcall 是 Pascal 程序的缺省调用方式，通常用于 Win32 API 中，函数采用从右到左的压栈方式，自己在退出时清空堆栈。VC 将函数编译后会在函数名前面加上下划线前缀，在函数名后加上 "@" 和参数的字节数。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">2、C 调用约定（即用__cdecl）</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; 按从右至左的顺序压参数入栈，由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的（正因为如此，实现可变参数的函数只能使用该调用约定）。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; _cdecl 是 C 和 C++ 程序缺省的调用方式。每一个调用它的函数都包含清空堆栈的代码，所以产生的可执行文件大小会比调用 _stdcall 函数的大。函数采用从右到左的压栈方式。VC 将函数编译后会在函数名前面加上下划线前缀。 它是 MFC 缺省调用约定。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">3、__fastcall 调用约定</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; "人" 如其名，它的主要特点就是快，因为它是通过寄存器来传送参数的（实际上，它用 ECX 和 EDX 传送前两个双字（DWORD）或更小的参数，剩下的参数仍旧自右向左压栈传送，被调用的函数在返回前清理传送参数的内存栈）。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; _fastcall方式的函数采用寄存器传递参数，VC 将函数编译后会在函数名前面加上"@"前缀，在函数名后加上"@"和参数的字节数。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">4、thiscall调用约定</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; 仅仅应用于 "C++" 成员函数。this 指针存放于 CX 寄存器，参数从右到左压。thiscall 不是关键词，因此不能被程序员指定。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">5、naked call调用约定</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; 采用 1-4 的调用约定时，如果必要的话，进入函数时编译器会产生代码来保存ESI，EDI，EBX，EBP寄存器，退出函数时则产生代码恢复这些寄存器的内容。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; naked call不产生这样的代码。naked call不是类型修饰符，故必须和_declspec 共同使用。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; 关键字 __stdcall、__cdecl 和 __fastcall 可以直接加在要输出的函数前，也可以在编译环境的 Setting...\C/C++ \Code Generation 项选择。当加在输出函数前的关键字与编译环境中的选择不同时，直接加在输出函数前的关键字有效。它们对应的命令行参数分别为/Gz、/Gd 和 /Gr。缺省状态为/Gd，即__cdecl。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px"><br>2)名字修饰约定</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">1、修饰名(Decoration name)</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; "C" 或者 "C++" 函数在内部（编译和链接）通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字符串。有些情况下使用函数的修饰名是必要的，如在模块定义文件里头指定输出"C++"重载函数、构造函数、析构函数，又如在汇编代码里调用"C""或"C++"函数等。修饰名由函数名、类名、调用约定、返回类型、参数等共同决定。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">2、函数名修饰约定随编译种类(C或C++)和调用约定的不同而不同，下面分别说明。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">a、C编译时函数名修饰约定规则：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">__stdcall调用约定：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; 在输出函数名前加上一个下划线前缀，后面加上一个"@"符号和其参数的字节数，格式为<span class=Apple-converted-space>&nbsp;</span><a style="COLOR: rgb(51,102,153); TEXT-DECORATION: none" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#95;&#102;&#117;&#110;&#99;&#116;&#105;&#111;&#110;&#110;&#97;&#109;&#101;&#64;&#110;&#117;&#109;&#98;&#101;&#114;">_functionname@number</a>。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">__cdecl调用约定：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; 仅在输出函数名前加上一个下划线前缀，格式为 _functionname。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">__fastcall调用约定：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; 在输出函数名前加上一个"@"符号，后面也是一个"@"符号和其参数的字节数，格式为@functionname@number。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">它们均不改变输出函数名中的字符大小写，这和PASCAL调用约定不同，PASCAL约定输出的函数名无任何修饰且全部大写。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">b、C++编译时函数名修饰约定规则：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">__stdcall调用约定：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">以"?"标识函数名的开始，后跟函数名；<br>函数名后面以"@@YG"标识参数表的开始，后跟参数表；<br>参数表以代号表示：<br>&nbsp;&nbsp;&nbsp; X——void，</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; D——char，</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; E——unsigned char，</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; F——short，</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; H——int，</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; I——unsigned int，</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; J——long，</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; K——unsigned long，</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; M——float，</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; N——double，</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; _N——bool，</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; ....</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; PA——表示指针，后面的代号表明指针类型，如果相同类型的指针连&nbsp;&nbsp;&nbsp; 续出现，以"0"代替，一个"0"代表一次重复；</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px"><br>参数表的第一项为该函数的返回值类型，其后依次为参数的数据类型,指针标识在其所指数据类型前；<br>参数表后以"@Z"标识整个名字的结束，如果该函数无参数，则以"Z"标识结束。其格式为<br>&nbsp;&nbsp;&nbsp; "<a style="COLOR: rgb(51,102,153); TEXT-DECORATION: none" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#63;&#102;&#117;&#110;&#99;&#116;&#105;&#111;&#110;&#110;&#97;&#109;&#101;&#64;&#64;&#89;&#71;&#42;&#42;&#42;&#42;&#42;&#64;&#90;">?functionname@@YG*****@Z</a>"或</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; "<a style="COLOR: rgb(51,102,153); TEXT-DECORATION: none" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#63;&#102;&#117;&#110;&#99;&#116;&#105;&#111;&#110;&#110;&#97;&#109;&#101;&#64;&#64;&#89;&#71;&#42;&#88;&#90;">?functionname@@YG*XZ</a>"，</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; 例如</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; int Test1（char *var1,unsigned long）&nbsp;&nbsp;&nbsp;<span class=Apple-converted-space>&nbsp;</span><a style="COLOR: rgb(51,102,153); TEXT-DECORATION: none" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#45;&#45;&#45;&#45;&#45;&#37;&#69;&#50;&#37;&#56;&#48;&#37;&#57;&#67;&#63;&#84;&#101;&#115;&#116;&#49;&#64;&#64;&#89;&#71;&#72;&#80;&#65;&#68;&#75;&#64;&#90;">-----&#8220;?Test1@@YGHPADK@Z</a>&#8221;<br>&nbsp;&nbsp;&nbsp; void Test2（）　　　　　　　　　　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=Apple-converted-space>&nbsp;</span><a style="COLOR: rgb(51,102,153); TEXT-DECORATION: none" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#45;&#45;&#45;&#45;&#45;&#37;&#69;&#50;&#37;&#56;&#48;&#37;&#57;&#67;&#63;&#84;&#101;&#115;&#116;&#50;&#64;&#64;&#89;&#71;&#88;&#88;&#90;">-----&#8220;?Test2@@YGXXZ</a>&#8221;</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">__cdecl调用约定：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; 规则同上面的_stdcall调用约定，只是参数表的开始标识由上面的"@@YG"变为"@@YA"。VC++对函数的省缺声明是"__cedcl",将只能被C/C++调用。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">__fastcall调用约定：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">&nbsp;&nbsp;&nbsp; 规则同上面的_stdcall调用约定，只是参数表的开始标识由上面的"@@YG"变为"@@YI"。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px"><br>文章出处：<a style="COLOR: rgb(51,102,153); TEXT-DECORATION: none" href="http://www.diybl.com/course/3_program/c++/cppjs/2008617/126024.html">http://www.diybl.com/course/3_program/c++/cppjs/2008617/126024.html</a></p>
</span></span>
<img src ="http://www.cppblog.com/mmdengwo/aggbug/145296.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-29 10:45 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/29/145296.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>类的成员函数指针(比较深入)</title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/27/145158.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Wed, 27 Apr 2011 09:14:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/27/145158.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/145158.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/27/145158.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/145158.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/145158.html</trackback:ping><description><![CDATA[<p>From:http://blog.csdn.net/hairetz/archive/2009/05/06/4153252.aspx</p>
<p>个人感觉对于类的成员函数指针这块讲解的比较深入详细</p>
<p>推荐阅读</p>
<p>/////////////////////////////////////////////////</p>
<p>先看这样一段代码</p>
<p>class test <br>{ <br>&nbsp;&nbsp; public: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test(int i){ m_i=i;} <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test(){} </p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void hello() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("hello\n"); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br>&nbsp;&nbsp; private: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int m_i; <br>}; </p>
<p>int main() <br>{ <br>&nbsp;&nbsp;&nbsp;&nbsp; test *p=new test(); <br>&nbsp;&nbsp;&nbsp;&nbsp; p-&gt;hello(); <br>&nbsp;&nbsp;&nbsp;&nbsp; p=NULL; <br>&nbsp;&nbsp;&nbsp;&nbsp; p-&gt;hello(); <br>} </p>
<p>&nbsp;</p>
<p>结果是:</p>
<p>hello</p>
<p>hello</p>
<p>为何</p>
<p>p=NULL; <br>&nbsp;&nbsp;&nbsp;&nbsp; p-&gt;hello();&nbsp;&nbsp; 这样之后，NULL-&gt;hello()也依然有效呢？</p>
<p>我们第一反应一定是觉得类的实例的成员函数，不同于成员变量，成员函数全部都存在同一个地方，所以的类的实例来调用的时候，一定是调用相同的函数指针。（想想也是有道理的，成员函数都是一样的功能，根本不需要实例化多个）</p>
<p>于是我们把代码改成这样</p>
<p>&nbsp;</p>
<p>class test <br>{ <br>&nbsp;&nbsp;&nbsp; public: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test(int i){ m_i=i;} <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test(){} </p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void hello() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("hello\n"); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </p>
<p><br>&nbsp;&nbsp;&nbsp; private: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int m_i; <br>}; </p>
<p><br>typedef void (test::*HELLO_FUNC)();</p>
<p>int main() <br>{ <br>&nbsp;&nbsp;&nbsp;&nbsp; test *p=new test(); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test q;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p-&gt;hello(); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HELLO_FUNC phello_fun=&amp;test::hello;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("%p\n",phello_fun);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p=NULL; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; phello_fun=&amp;test::hello;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("%p\n",phello_fun);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; phello_fun=p-&gt;hello;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("%p\n",phello_fun);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; phello_fun=q.hello;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("%p\n",phello_fun);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p-&gt;hello(); <br>} </p>
<p>&nbsp;</p>
<p>结果是：</p>
<p>hello<br>00401005<br>00401005<br>00401005<br>00401005<br>hello<br>Press any key to continue</p>
<p>&nbsp;</p>
<p>也就是说不管是&amp;test::hello,还是p-&gt;hello，或者q.hello,甚至于NULL-&gt;hello.</p>
<p>调用的地址都是0x00401005,也基本印证了我们的猜想。</p>
<p>&nbsp;</p>
<p>事情到这里算是完了么？没有。</p>
<p>有人问道这样一段代码：</p>
<p>SSVector&amp; SSVector::assign2product4setup(const SVSet&amp; A, const SSVector&amp; x) <br>{ <br>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp; ret_val=&nbsp; </p>
<p>pthread_create(&amp;pt,NULL,(void(*)(void*))SSVector::prefun,x); <br>} </p>
<p>void* SSVector::prefun (void* arg){ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const SSVector &amp;tx =*((SSVector*) arg); <br>} <br>行报错：invalid conversion from 'void (*)(void*)' to 'void* (*)(void*)' </p>
<p>pthread_create我就不解释了，第3个参数是线程函数的指针，为何这里报错呢？</p>
<p>&nbsp;</p>
<p>说明普通的类成员函数的指针（如果它有函数指针的话），不同于一般的函数指针。</p>
<p>&nbsp;</p>
<p>看看下面这篇文章关于这个问题的分析：</p>
<p>前言：在CSDN论坛经常会看到一些关于类成员函数指针的问题，起初我并不在意，以为成员函数指针和普通的函数指针是一样的，没有什么太多需要讨论的。当我找来相关书籍查阅了一番以后，突然意识到我以前对成员函数指针的理解太过于幼稚和肤浅了，它即不像我以前认为的那样简单,它也不像我以前认为的那样"默默无闻"。强烈的求知欲促使我对成员函数进行进一步的学习并有了这篇文章。</p>
<p>一。理论篇<br>在进行深入学习和分析之前，还是先看看书中是怎么介绍成员函数的。总结一下类成员函数指针的内容，应该包含以下几个知识点：<br>1。成员函数指针并不是普通的函数指针。<br>2。编译器提供了几个新的操作符来支持成员函数指针操作： </p>
<p>1) 操作符"::*"用来声明一个类成员函数指针，例如：<br>&nbsp;&nbsp;&nbsp; typedef void (Base::*PVVBASEMEMFUNC)(void);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Base is a class<br>2) 操作符"-&gt;*"用来通过对象指针调用类成员函数指针，例如：<br>&nbsp;&nbsp;&nbsp; //pBase is a Base pointer and well initialized<br>&nbsp;&nbsp;&nbsp; //pVIBaseMemFunc is a member function pointer and well initialized<br>&nbsp;&nbsp;&nbsp; (pBase-&gt;*pVIBaseMemFunc)();<br>3) 操作符".*"用来通过对象调用类成员函数指针，例如：<br>&nbsp;&nbsp;&nbsp; //baseObj is a Base object<br>&nbsp;&nbsp;&nbsp; //pVIBaseMemFunc is a member function pointer and well initialized<br>&nbsp;&nbsp;&nbsp; (baseObj.*pVIBaseMemFunc)(); </p>
<p><br>3。成员函数指针是强类型的。 </p>
<p>&nbsp;&nbsp;&nbsp; typedef void (Base::*PVVBASEMEMFUNC)(void);<br>&nbsp;&nbsp;&nbsp; typedef void (Derived::*PVVDERIVEMEMFUNC)(void);<br>PVVBASEMEMFUNC和PVVDERIVEMEMFUNC是两个不同类型的成员函数指针类型。</p>
<p><br>4。由于成员函数指针并不是真真意义上的指针，所以成员函数指针的转化就受限制。具体的转化细节依赖于不同的编译器，甚至是同一个编译器的不同版本。不过，处于同一个继承链中的不同类之间override的不同函数和虚函数还是可以转化的。 </p>
<p>&nbsp;&nbsp;&nbsp; void* pVoid = reinterpret_cast&lt;void*&gt;(pVIBaseMemFunc);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //error<br>&nbsp;&nbsp;&nbsp; int*&nbsp; pInt&nbsp; = reinterpret_cast&lt;int*&gt;(pVIBaseMemFunc);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //error<br>&nbsp; pVIDeriveMemFunc = static_cast&lt;PVIDERIVEMEMFUNC&gt;(pVIBaseMemFunc);&nbsp;&nbsp; //OK</p>
<p><br>二。实践篇<br>有了上面的理论知识，我们对类成员函数指针有了大概的了解，但是我们对成员函数指针还存在太多的疑惑。既然说成员函数指针不是指针，那它到底是什么东东? 编译器为什么要限制成员函数指针转化？老办法，我们还是分析汇编代码揭示其中的秘密。首先，我写了这样两个具有继承关系的类： <br>接着，我又定义了一些成员函数指针类型： <br>最后，在main函数写了一些测试代码： <br>成功编译后生成汇编代码。老规矩，在分析汇编代码的过程中还是只分析对解决问题有意义的汇编代码，其他的就暂时忽略。<br>1。成员函数指针不是指针。从代码看出，在main函数的调用栈(calling stack)中首先依次压入四个成员函数指针，如果它们是普通指针的话，它们之间的偏移量应该是4个字节,可是实际的情况却是这样的： </p>
<p>&nbsp;</p>
<p>&nbsp;&#8221;The implementation of the pointer to member function must store within itself information as to whether the member function to which it refers is virtual or nonvirtual, information about where to find the appropriate virtual function table pointer (see The Compiler Puts Stuff in Classes [11, 37]), an offset to be added to or subtracted from the function's this pointer (see Meaning of Pointer Comparison [28, 97]), and possibly other information. A pointer to member function is commonly implemented as a small structure that contains this information, although many other implementations are also in use. Dereferencing and calling a pointer to member function usually involves examining the stored information and conditionally executing the appropriate virtual or nonvirtual function calling sequence.&#8220;</p>
<p>2。成员函数指针的转化。本文所采用的代码是想比较普通成员函数指针和虚函数指针在转化的过程中存在那些差异： <br>对于符号<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#8221;&#63;&#63;&#95;&#57;&#64;&#36;&#66;&#51;&#65;&#69;">&#8221;??_9@$B3AE</a>&#8220;，我又找到了这样的汇编代码： 由此可以看出，对于虚函数，即使是用过成员函数指针间接调用，仍然具有和直接调用一样的特性。</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; ; PVIBASEMEMFUNC pVIBaseMemFunc = &amp;Base::setValue;<br>&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp; DWORD PTR _pVIBaseMemFunc$[ebp], OFFSET FLAT:?setValue@Base@@QAEXH@Z ; <br>&nbsp;&nbsp;&nbsp; 取出Base::setValue函数的地址，存放于变量pVIBaseMemFunc所占内存的前4个字节(DWORD)中。</p>
<p>&nbsp;</p>
<p>; PVVBASEMEMFUNC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pVVBaseMemFunc&nbsp;&nbsp; = &amp;Base::foobar;<br>mov&nbsp;&nbsp;&nbsp; DWORD PTR _pVVBaseMemFunc$[ebp], OFFSET FLAT:??_9@$B3AE ; `vcall'<br>取出符号<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#8221;&#63;&#63;&#95;&#57;&#64;&#36;&#66;&#51;&#65;&#69;">&#8221;??_9@$B3AE</a>&#8220;的值，存放于变量pVVBaseMemFunc所占内存的前4个字节(DWORD)中。</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; _TEXT&nbsp;&nbsp;&nbsp; SEGMENT<br>&nbsp;&nbsp;&nbsp; <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#63;&#63;&#95;&#57;&#64;&#36;&#66;&#51;&#65;&#69;">??_9@$B3AE</a> PROC NEAR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; `vcall', COMDAT<br>&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp; eax, DWORD PTR [ecx]<br>&nbsp;&nbsp;&nbsp; jmp&nbsp;&nbsp;&nbsp; DWORD PTR [eax+4]<br>&nbsp;&nbsp;&nbsp; <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#63;&#63;&#95;&#57;&#64;&#36;&#66;&#51;&#65;&#69;">??_9@$B3AE</a> ENDP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; `vcall'<br>&nbsp;&nbsp;&nbsp; _TEXT&nbsp;&nbsp;&nbsp; ENDS<br>符号<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#8221;&#63;&#63;&#95;&#57;&#64;&#36;&#66;&#51;&#65;&#69;">&#8221;??_9@$B3AE</a>&#8220;代表的应该是一个存根函数，这个函数首先根据this指针获得虚函数表的指针，然后将指令再跳转到相应的虚函数的地址。</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; ; PVIDERIVEMEMFUNC pVIDeriveMemFunc = static_cast&lt;PVIDERIVEMEMFUNC&gt;(pVIBaseMemFunc);<br>&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp; eax, DWORD PTR _pVIBaseMemFunc$[ebp]<br>&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp; DWORD PTR _pVIDeriveMemFunc$[ebp], eax<br>直接将变量pVIBaseMemFunc所占内存的前4个字节(DWORD)的值付给了变量_pVIDeriveMemFunc所占内存的前4个字节中。</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; ; PVVDERIVEMEMFUNC&nbsp;&nbsp;&nbsp; pVVDeriveMemFunc = static_cast&lt;PVVDERIVEMEMFUNC&gt;(pVVBaseMemFunc);<br>&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp; eax, DWORD PTR _pVVBaseMemFunc$[ebp]<br>&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp; DWORD PTR _pVVDeriveMemFunc$[ebp], eax<br>直接将变量pVVBaseMemFunc所占内存的前4个字节(DWORD)的值付给了变量pVVDeriveMemFunc所占内存的前4个字节中。</p>
<p>由此可以看出，基类的成员函数指针转化到相应的派生类的成员函数指针，值保持不变。当然这里的例子继承关系相对来说比较简单，如果存在多继承和虚继承的情况下，结果可能会复杂的多。</p>
<p>3。函数调用<br>下面的函数调用都大同小异，这里是列出其中的一个： 这里的汇编代码并没有给我们太多新鲜的内容：将对象的首地址(this指针)存放于寄存器ECX中，接着就将指令转到变量_pVIBaseMemFunc所占内存的前4个字节所表示的地址。</p>
<p>到了这里，我们应该对成员函数指针有了进一步的了解。</p>
<p>&nbsp;&nbsp;&nbsp; ; (baseObj.*pVIBaseMemFunc)(10);<br>&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp; esi, esp<br>&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 0000000aH<br>&nbsp;&nbsp;&nbsp; lea&nbsp;&nbsp;&nbsp; ecx, DWORD PTR _baseObj$[ebp]<br>&nbsp;&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; DWORD PTR _pVIBaseMemFunc$[ebp]<br>&nbsp;&nbsp;&nbsp; cmp&nbsp;&nbsp;&nbsp; esi, esp<br>&nbsp;&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; __RTC_CheckEsp&nbsp;&nbsp; </p>
<p>&nbsp;</p>
<p><br>由此可以看出，他们之间的偏移量是12个字节。这12个字节中应该可以包含三个指针，其中的一个指针应该指向函数的地址，那另外两个指针又指向那里呢？在《C++ Common Knowledge: Essential Intermediate Programming》(中文译名：</p>
<p>C++必知必会)这本书的第16章对这部分的内容做了说明，这个12个字节的偏移量正好印证了书中的内容：</p>
<p>class Base {<br>public:<br>&nbsp;&nbsp;&nbsp; //ordinary member function<br>&nbsp;&nbsp;&nbsp; void setValue(int iValue);</p>
<p>&nbsp;&nbsp;&nbsp; //virtual member function<br>&nbsp;&nbsp;&nbsp; virtual void dumpMe();<br>&nbsp;&nbsp;&nbsp; virtual void foobar();</p>
<p>protected:<br>&nbsp;&nbsp;&nbsp; int m_iValue;<br>};</p>
<p>class Derived:public Base{<br>public:<br>&nbsp;&nbsp;&nbsp; //ordinary member function<br>&nbsp;&nbsp;&nbsp; void setValue(int iValue);</p>
<p>&nbsp;&nbsp;&nbsp; //virtual member function<br>&nbsp;&nbsp;&nbsp; virtual void dumpMe();<br>&nbsp;&nbsp;&nbsp; virtual void foobar();<br>private:<br>&nbsp;&nbsp;&nbsp; double m_fValue;<br>};</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; typedef void (Base::*PVVBASEMEMFUNC)(void);<br>&nbsp;&nbsp;&nbsp; typedef void (Derived::*PVVDERIVEMEMFUNC)(void);<br>&nbsp;&nbsp;&nbsp; typedef void (Base::*PVIBASEMEMFUNC)(int);<br>&nbsp;&nbsp;&nbsp; typedef void (Derived::*PVIDERIVEMEMFUNC)(int);</p>
<p>&nbsp;</p>
<p>int _tmain(int argc, _TCHAR* argv[])<br>{<br>&nbsp;&nbsp;&nbsp; PVIBASEMEMFUNC pVIBaseMemFunc = &amp;Base::setValue;<br>&nbsp;&nbsp;&nbsp; PVIDERIVEMEMFUNC pVIDeriveMemFunc = static_cast&lt;PVIDERIVEMEMFUNC&gt;(pVIBaseMemFunc);</p>
<p>&nbsp;&nbsp;&nbsp; PVVBASEMEMFUNC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pVVBaseMemFunc&nbsp;&nbsp; = &amp;Base::foobar;<br>&nbsp;&nbsp;&nbsp; PVVDERIVEMEMFUNC&nbsp;&nbsp;&nbsp; pVVDeriveMemFunc = static_cast&lt;PVVDERIVEMEMFUNC&gt;(pVVBaseMemFunc);</p>
<p>&nbsp;&nbsp;&nbsp; Base baseObj;<br>&nbsp;&nbsp;&nbsp; (baseObj.*pVIBaseMemFunc)(10);<br>&nbsp;&nbsp;&nbsp; (baseObj.*pVVBaseMemFunc)();</p>
<p>&nbsp;&nbsp;&nbsp; Derived deriveObj;<br>&nbsp;&nbsp;&nbsp; (deriveObj.*pVIDeriveMemFunc)(20);<br>&nbsp;&nbsp;&nbsp; (deriveObj.*pVVDeriveMemFunc)();</p>
<p>&nbsp;&nbsp;&nbsp; return 0;<br>}</p>
<p>&nbsp;</p>
<p>_deriveObj$ = -88<br>_baseObj$ = -60<br>_pVVDeriveMemFunc$ = -44<br>_pVVBaseMemFunc$ = -32<br>_pVIDeriveMemFunc$ = -20<br>_pVIBaseMemFunc$ = -8<br>_argc$ = 8<br>_argv$ = 12</p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/eroswang/archive/2009/05/06/4153356.aspx">http://blog.csdn.net/eroswang/archive/2009/05/06/4153356.aspx</a></p>
<img src ="http://www.cppblog.com/mmdengwo/aggbug/145158.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-27 17:14 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/27/145158.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>可变参数的实现 </title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/14/144258.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Thu, 14 Apr 2011 14:39:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/14/144258.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/144258.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/14/144258.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/144258.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/144258.html</trackback:ping><description><![CDATA[&nbsp;void error(int severity...)<br>{<br>&nbsp;&nbsp;&nbsp;va_list ap;<br>&nbsp;&nbsp;&nbsp;va_start(ap,severity);<br>&nbsp;&nbsp;&nbsp;for(;;)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char* p=va_arg(ap,char*);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(p==0) break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cerr&lt;&lt;p&lt;&lt;' ';<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;va_end(ap);<br>&nbsp;&nbsp;&nbsp;cerr&lt;&lt;'\n';<br>&nbsp;&nbsp;&nbsp;if(severity) exit(severity);<br>}<br>&nbsp;&nbsp;&nbsp; 首先，通过调用va_start定义并初始化一个va_list，宏va_start以一个va_list的名字和函数的最后一个有名形参的名字作为参数。宏va_arg用户按顺序取出各个无名参数。在每次调用va_arg时，程序员都必须提供一个类型，va_arg假定这就是被传递的实际参数的类型，但一般说它并没有办法保证这一点。从一个使用过va_start的函数退出之前，必须调用一次va_end，这是因为va_start可能以某种形式修改了堆栈，这种修改可能导致返回无法完成，va_end能将有关的修改复原。 
<img src ="http://www.cppblog.com/mmdengwo/aggbug/144258.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-14 22:39 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/14/144258.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>new的三中使用方法 </title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/14/144257.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Thu, 14 Apr 2011 14:38:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/14/144257.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/144257.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/14/144257.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/144257.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/144257.html</trackback:ping><description><![CDATA[&nbsp;new有三种使用方式：plain new，nothrow new和placement new。<br>（1）plain new顾名思义就是普通的new，就是我们惯常使用的new。在C++中是这样定义的：<br>&nbsp;&nbsp;&nbsp; void* operator new(std::size_t) throw(std::bad_alloc);<br>&nbsp;&nbsp;&nbsp; void operator delete(void *) throw();<br>提示：plain new在分配失败的情况下，抛出异常std::bad_alloc而不是返回NULL，因此通过判断返回值是否为NULL是徒劳的。<br>（2）nothrow new是不抛出异常的运算符new的形式。nothrow new在失败时，返回NULL。定义如下：<br>&nbsp;&nbsp;&nbsp; void * operator new(std::size_t,const std::nothrow_t&amp;) throw();<br>&nbsp;&nbsp;&nbsp; void operator delete(void*) throw();<br>（3）placement new意即&#8220;放置&#8221;，这种new允许在一块已经分配成功的内存上重新构造对象或对象数组。placement new不用担心内存分配失败，因为它根本不分配内存，它做的唯一一件事情就是调用对象的构造函数。定义如下：<br>&nbsp;&nbsp;&nbsp; void* operator new(size_t,void*);<br>&nbsp;&nbsp;&nbsp; void operator delete(void*,void*);<br>提示1：palcement new的主要用途就是反复使用一块较大的动态分配的内存来构造不同类型的对象或者他们的数组。<br>提示2：placement new构造起来的对象或其数组，要显示的调用他们的析构函数来销毁，千万不要使用delete。<br>char* p = new(nothrow) char[100];<br>long *q1 = new(p) long(100);<br>int *q2 = new(p) int[100/sizeof(int)];<br>
<img src ="http://www.cppblog.com/mmdengwo/aggbug/144257.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-14 22:38 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/14/144257.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>dll文件和lib文件的区别(如何利用VC创建DLL文件) </title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/14/144249.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Thu, 14 Apr 2011 14:17:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/14/144249.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/144249.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/14/144249.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/144249.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/144249.html</trackback:ping><description><![CDATA[<span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px; WIDOWS: 2; ORPHANS: 2"><span class=Apple-style-span style="FONT-SIZE: 13px; LINE-HEIGHT: 19px; FONT-FAMILY: Verdana, Geneva, Arial, Helvetica, sans-serif">&nbsp;
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>dll是在你的程序运行的时候才连接的文件，因此它是一种比较小的可执行文件格式，.dll还有其他的文件格式如.ocx等，所有的.dll文件都是可执行;</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>&nbsp;<wbr>&nbsp;<wbr><span class=Apple-converted-space>&nbsp;</span>lib是在你的程序编译连接的时候就连接的文件，因此你必须告知编译器连接的lib文件在那里。一般来说，与动态连接文件相对比，lib文件也被称为是静态连接库。当你把代码编译成这几种格式的文件时，在以后他们就不可能再被更改。如果你想使用lib文件，就必须：<br>1. 包含一个对应的头文件告知编译器lib文件里面的具体内容<br>2 .设置lib文件允许编译器去查找已经编译好的二进制代码</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>&nbsp;<wbr><span class=Apple-converted-space>&nbsp;</span>&nbsp;<wbr>如果你想从你的代码分离一个dll文件出来代替静态连接库，仍然需要一个lib文件。这个lib文件将被连接到程序告诉操作系统在运行的时候你想用到什么 dll文件，一般情况下，lib文件里有相应的dll文件的名字和一个指明dll输出函数入口的顺序表。如果不想用lib文件或者是没有lib文件，可以使用WIN32 API函数LoadLibrary、GetProcAddress。事实上，我们可以在Visual C++ IDE中以二进制形式打开lib文件，大多情况下会看到ASCII码格式的C++函数或一些重载操作的函数名字。</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>&nbsp;<wbr><span class=Apple-converted-space>&nbsp;</span>一般我们最主要的关于lib文件的麻烦就是出现unresolved symble 这类错误，这就是lib文件连接错误或者没有包含.c、.cpp文件到工程里，关键是如果在C++工程里用了C语言写的lib文件，就必需要这样包含：<br>extern "C"<br>{<br>#include "myheader.h"<br>}<br>这是因为C语言写的lib文件没有C++所必须的名字破坏，C函数不能被重载，因此连接器会出错。</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>在VC中不用MFC如何制作dll</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><strong><font color=#000000>方法一：使用export 和 import</font></strong></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>在VC中建立一个Console Application，建立2个文件：Dll.h 和 Dll.cpp</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>Dll.h<br>========================================================<br>#ifdef MYLIBAPI<br>#else<br>#define MYLIBAPI extern "C" _declspec (dllimport)<br>#end if</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>MYLIBAPI int Add (int iLeft, int iRight)<br>MYLIBAPI int Sub (int iLeft, int iRight)<br>========================================================</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>Dll.cpp<br>========================================================<br>#define MYLIBAPI extern "C" _declspec (dllexport)</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>#include "Dll.h"</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>int Add (int iLeft, int iRight)<br>{<br>return iLeft + iRight ;<br>}</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>int Sub (int iLeft, int iRight)<br>{<br>return iLeft - iRight ;<br>}<br>========================================================</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>保存文件。在Project-&gt;setting-&gt;link 最下面加上 &#8220;/dll&#8221;， "/"之前一定要与前一项有空格。然后编译，就可以在debug 或 release下面找到dll 和 lib 文件了使用的时候包含dll.h文件。</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000><strong>方法二：使用def文件</strong><br>建立一个console application, 建立2个文件dll.h 和 dll.cpp</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>Dll.h<br>========================================================<br>int Add (int iLeft, int iRight) ;<br>int Sub (int iLeft, int iRight) ;<br>========================================================</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>Dll.cpp<br>========================================================<br>#include "Dll.h"</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>int Add (int iLeft, int iRight)<br>{<br>return iLeft + iRight ;<br>}</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>int Sub (int iLeft, int iRight)<br>{<br>return iLeft - iRight ;<br>}<br>========================================================</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>然后再当前目录下面建立一个.def文件，文件名最好和要输出的dll名字一样，扩展名为.def, 里面写上：</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>LIBRARY dllname.dll<br>EXPORTS<br>Add @1<br>Add @2<br>然后将这个文件添加到工程中，在link中设置 /dll， 然后编译在debug或release中就可以找到dll和lib了<br>使用的时候加上dll.h文件。</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>===============================</font></p>
<p style="FONT-SIZE: 13px; MARGIN: 5px auto; TEXT-INDENT: 2em; LINE-HEIGHT: 1.4"><font color=#000000>补充一点：<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><span class=Apple-converted-space>&nbsp;</span>dll是个编译好的程序，调用时可以直接调用其中的函数，不参加工程的编译。而lib应该说是一个程序集，只是把一些相应的函数总结在一起，如果调用lib中的函数，在工程编译时,这些调用的函数都将参加编译。<br><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><span class=Apple-converted-space>&nbsp;</span>简单讲，静态库就是直接将需要的代码连接进可执行程序；动态库就是在需要调用其中的函数时，根据函数映射表找到该函数然后调入堆栈执行。<br>做成静态库可执行文件本身比较大，但不必附带动态库<br>做成动态库可执行文件本身比较小，但需要附带动态库<br>其它没有什么对于程序员而言很大的区别，有的Unix可能不支持动态库，所以只好用静态库。<br><br><strong>DLL与LIB的区别：</strong><br>1.DLL是一个完整程序，其已经经过链接，即不存在同名引用，且有导出表，与导入表lib是一个代码集(也叫函数集)他没有链接，所以lib有冗余，当两个lib相链接时地址会重新建立，当然还有其它相关的不同，用lib.exe就知道了；<br>2.在生成dll时，经常会生成一个.lib(导入与导出)，这个lib实际上不是真正的函数集，其每一个导出导入函数都是跳转指令，直接跳转到DLL中的位置，这个目的是外面的程序调用dll时自动跳转；<br>3.实际上最常用的lib是由lib.exe把*.obj生成的lib。</font></p>
</span></span>
<img src ="http://www.cppblog.com/mmdengwo/aggbug/144249.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-14 22:17 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/14/144249.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>段错误bug的调试 </title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/14/144248.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Thu, 14 Apr 2011 14:14:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/14/144248.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/144248.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/14/144248.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/144248.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/144248.html</trackback:ping><description><![CDATA[我们在用C/C++语言写程序的时侯，内存管理的绝大部分工作都是需要我们来做的。实际上，内存管理是一个比较繁琐的工作，无论你多高明，经验多丰富，难免会在此处犯些小错误，而通常这些错误又是那么的浅显而易于消除。但是手工&#8220;除虫&#8221;（debug），往往是效率低下且让人厌烦的，本文将就"段错误"这个内存访问越界的错误谈谈如何快速定位这些"段错误"的语句。<br>下面将就以下的一个存在段错误的程序介绍几种调试方法：<br>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align=center>
    <tbody>
        <tr>
            <td>&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp; dummy_function (void)<br>&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned char *ptr = 0x00;<br>&nbsp;&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *ptr = 0x00;<br>&nbsp;&nbsp;&nbsp;&nbsp; 5&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp; 6<br>&nbsp;&nbsp;&nbsp;&nbsp; 7&nbsp; int main (void)<br>&nbsp;&nbsp;&nbsp;&nbsp; 8&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; 9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dummy_function ();<br>&nbsp;&nbsp;&nbsp; 10<br>&nbsp;&nbsp;&nbsp; 11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>&nbsp;&nbsp;&nbsp; 12&nbsp; }<br></td>
        </tr>
    </tbody>
</table>
作为一个熟练的C/C++程序员，以上代码的bug应该是很清楚的，因为它尝试操作地址为0的内存区域，而这个内存区域通常是不可访问的禁区，当然就会出错了。我们尝试编译运行它:<br>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align=center>
    <tbody>
        <tr>
            <td>xiaosuo@gentux test $ ./a.out<br>段错误<br></td>
        </tr>
    </tbody>
</table>
果然不出所料，它出错并退出了。<br><span style="FONT-WEIGHT: bold">1.利用gdb逐步查找段错误:</span><br>这种方法也是被大众所熟知并广泛采用的方法，首先我们需要一个带有调试信息的可执行程序，所以我们加上&#8220;-g -rdynamic"的参数进行编译，然后用gdb调试运行这个新编译的程序,具体步骤如下:<br>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align=center>
    <tbody>
        <tr>
            <td>xiaosuo@gentux test $ gcc -g -rdynamic d.c<br>xiaosuo@gentux test $ gdb ./a.out<br>GNU gdb 6.5<br>Copyright (C) 2006 Free Software Foundation, Inc.<br>GDB is free software, covered by the GNU General Public License, and you are<br>welcome to change it and/or distribute copies of it under certain conditions.<br>Type "show copying" to see the conditions.<br>There is absolutely no warranty for GDB.&nbsp; Type "show warranty" for details.<br>This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".<br><br>(gdb) r<br>Starting program: /home/xiaosuo/test/a.out<br><br>Program received signal SIGSEGV, Segmentation fault.<br>0x08048524 in dummy_function () at d.c:4<br>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *ptr = 0x00;<br>(gdb)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br></td>
        </tr>
    </tbody>
</table>
哦？！好像不用一步步调试我们就找到了出错位置d.c文件的第4行，其实就是如此的简单。<br>从这里我们还发现进程是由于收到了SIGSEGV信号而结束的。通过进一步的查阅文档(man 7 signal)，我们知道SIGSEGV默认handler的动作是打印&#8221;段错误"的出错信息，并产生Core文件，由此我们又产生了方法二。<br><span style="FONT-WEIGHT: bold">2.分析Core文件：</span><br>Core文件是什么呢？<br>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align=center>
    <tbody>
        <tr>
            <td>The&nbsp; default action of certain signals is to cause a process to terminate and produce a core dump file, a disk file containing an image of the process's memory&nbsp; at the time of termination.&nbsp; A list of the signals which cause a process to dump core can be found in signal(7).</td>
        </tr>
    </tbody>
</table>
以上资料摘自man page(man 5 core)。不过奇怪了，我的系统上并没有找到core文件。后来，忆起为了渐少系统上的拉圾文件的数量（本人有些洁癖，这也是我喜欢Gentoo的原因之一），禁止了core文件的生成，查看了以下果真如此，将系统的core文件的大小限制在512K大小，再试:<br>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align=center>
    <tbody>
        <tr>
            <td>xiaosuo@gentux test $ ulimit -c<br>0<br>xiaosuo@gentux test $ ulimit -c 1000<br>xiaosuo@gentux test $ ulimit -c<br>1000<br>xiaosuo@gentux test $ ./a.out<br>段错误 (core dumped)<br>xiaosuo@gentux test $ ls<br>a.out&nbsp; core&nbsp; d.c&nbsp; f.c&nbsp; g.c&nbsp; pango.c&nbsp; test_iconv.c&nbsp; test_regex.c<br></td>
        </tr>
    </tbody>
</table>
core文件终于产生了，用gdb调试一下看看吧:<br>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align=center>
    <tbody>
        <tr>
            <td>xiaosuo@gentux test $ gdb ./a.out core<br>GNU gdb 6.5<br>Copyright (C) 2006 Free Software Foundation, Inc.<br>GDB is free software, covered by the GNU General Public License, and you are<br>welcome to change it and/or distribute copies of it under certain conditions.<br>Type "show copying" to see the conditions.<br>There is absolutely no warranty for GDB.&nbsp; Type "show warranty" for details.<br>This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".<br><br><br>warning: Can't read pathname for load map: 输入/输出错误.<br>Reading symbols from /lib/libc.so.6...done.<br>Loaded symbols for /lib/libc.so.6<br>Reading symbols from /lib/ld-linux.so.2...done.<br>Loaded symbols for /lib/ld-linux.so.2<br>Core was generated by `./a.out'.<br>Program terminated with signal 11, Segmentation fault.<br>#0&nbsp; 0x08048524 in dummy_function () at d.c:4<br>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *ptr = 0x00;<br></td>
        </tr>
    </tbody>
</table>
哇，好历害，还是一步就定位到了错误所在地，佩服一下Linux/Unix系统的此类设计。<br>接着考虑下去，以前用windows系统下的ie的时侯，有时打开某些网页，会出现&#8220;运行时错误&#8221;，这个时侯如果恰好你的机器上又装有windows的编译器的话，他会弹出来一个对话框，问你是否进行调试，如果你选择是，编译器将被打开，并进入调试状态，开始调试。<br>Linux下如何做到这些呢？我的大脑飞速地旋转着，有了，让它在SIGSEGV的handler中调用gdb，于是第三个方法又诞生了:<br><span style="FONT-WEIGHT: bold">3.段错误时启动调试:</span><br>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align=center>
    <tbody>
        <tr>
            <td>#include &lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;signal.h&gt;<br>#include &lt;string.h&gt;<br><br>void dump(int signo)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char buf[1024];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char cmd[1024];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FILE *fh;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; snprintf(buf, sizeof(buf), "/proc/%d/cmdline", getpid());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!(fh = fopen(buf, "r")))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!fgets(buf, sizeof(buf), fh))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fclose(fh);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(buf[strlen(buf) - 1] == '\n')<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf[strlen(buf) - 1] = '\0';<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; snprintf(cmd, sizeof(cmd), "gdb %s %d", buf, getpid());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; system(cmd);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(0);<br>}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void<br>dummy_function (void)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned char *ptr = 0x00;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *ptr = 0x00;<br>}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int<br>main (void)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; signal(SIGSEGV, &amp;dump);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dummy_function ();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>}<br></td>
        </tr>
    </tbody>
</table>
编译运行效果如下:<br>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align=center>
    <tbody>
        <tr>
            <td>xiaosuo@gentux test $ gcc -g -rdynamic f.c<br>xiaosuo@gentux test $ ./a.out<br>GNU gdb 6.5<br>Copyright (C) 2006 Free Software Foundation, Inc.<br>GDB is free software, covered by the GNU General Public License, and you are<br>welcome to change it and/or distribute copies of it under certain conditions.<br>Type "show copying" to see the conditions.<br>There is absolutely no warranty for GDB.&nbsp; Type "show warranty" for details.<br>This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".<br><br>Attaching to program: /home/xiaosuo/test/a.out, process 9563<br>Reading symbols from /lib/libc.so.6...done.<br>Loaded symbols for /lib/libc.so.6<br>Reading symbols from /lib/ld-linux.so.2...done.<br>Loaded symbols for /lib/ld-linux.so.2<br>0xffffe410 in __kernel_vsyscall ()<br>(gdb) bt<br>#0&nbsp; 0xffffe410 in __kernel_vsyscall ()<br>#1&nbsp; 0xb7ee4b53 in waitpid () from /lib/libc.so.6<br>#2&nbsp; 0xb7e925c9 in strtold_l () from /lib/libc.so.6<br>#3&nbsp; 0x08048830 in dump (signo=11) at f.c:22<br>#4&nbsp; &lt;signal handler called&gt;<br>#5&nbsp; 0x0804884c in dummy_function () at f.c:31<br>#6&nbsp; 0x08048886 in main () at f.c:38<br></td>
        </tr>
    </tbody>
</table>
怎么样？是不是依旧很酷？<br>以上方法都是在系统上有gdb的前提下进行的，如果没有呢？其实glibc为我们提供了此类能够dump栈内容的函数簇，详见/usr/include/execinfo.h（这些函数都没有提供man page，难怪我们找不到），另外你也可以通过<a href="http://www.gnu.org/software/libc/manual/html_node/Backtraces.html" target=_blank><u><font color=#0000ff>gnu的手册</font></u></a>进行学习。<br><span style="FONT-WEIGHT: bold">4.利用backtrace和objdump进行分析:</span><br>重写的代码如下:<br>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align=center>
    <tbody>
        <tr>
            <td>#include &lt;execinfo.h&gt;<br>#include &lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;signal.h&gt;<br><br>/* A dummy function to make the backtrace more interesting. */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void<br>dummy_function (void)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned char *ptr = 0x00;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *ptr = 0x00;<br>}<br><br>void dump(int signo)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void *array[10];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size_t size;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char **strings;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size_t i;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size = backtrace (array, 10);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strings = backtrace_symbols (array, size);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf ("Obtained %zd stack frames.\n", size);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (i = 0; i &lt; size; i++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf ("%s\n", strings[i]);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; free (strings);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(0);<br>}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int<br>main (void)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; signal(SIGSEGV, &amp;dump);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dummy_function ();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>}<br></td>
        </tr>
    </tbody>
</table>
编译运行结果如下：<br>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align=center>
    <tbody>
        <tr>
            <td>xiaosuo@gentux test $ gcc -g -rdynamic g.c<br>xiaosuo@gentux test $ ./a.out<br>Obtained 5 stack frames.<br>./a.out(dump+0x19) [0x80486c2]<br>[0xffffe420]<br>./a.out(main+0x35) [0x804876f]<br>/lib/libc.so.6(__libc_start_main+0xe6) [0xb7e02866]<br>./a.out [0x8048601]<br></td>
        </tr>
    </tbody>
</table>
这次你可能有些失望,似乎没能给出足够的信息来标示错误,不急,先看看能分析出来什么吧,用objdump反汇编程序,找到地址0x804876f对应的代码位置:<br>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align=center>
    <tbody>
        <tr>
            <td>xiaosuo@gentux test $ objdump -d a.out<br></td>
        </tr>
    </tbody>
</table>
<br>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align=center>
    <tbody>
        <tr>
            <td>&nbsp;8048765:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e8 02 fe ff ff&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; call&nbsp;&nbsp; 804856c &lt;signal@plt&gt;<br>&nbsp;804876a:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e8 25 ff ff ff&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; call&nbsp;&nbsp; 8048694 &lt;dummy_function&gt;<br>&nbsp;<span style="COLOR: rgb(255,1,2)">804876f</span>:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b8 00 00 00 00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp; $0x0,%eax<br>&nbsp;8048774:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; leave<br></td>
        </tr>
    </tbody>
</table>
我们还是找到了在哪个函数(dummy_function)中出错的,信息已然不是很完整,不过有总比没有好的啊!<br><span style="FONT-WEIGHT: bold">后记:</span><br>本文给出了分析"段错误"的几种方法,不要认为这是与孔乙己先生的"回"字四种写法一样的哦,因为每种方法都有其自身的适用范围和适用环境,请酌情使用,或遵医嘱。 
<img src ="http://www.cppblog.com/mmdengwo/aggbug/144248.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-14 22:14 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/14/144248.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++回调（CallBack）方案 </title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/14/144246.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Thu, 14 Apr 2011 14:12:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/14/144246.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/144246.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/14/144246.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/144246.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/144246.html</trackback:ping><description><![CDATA[<p>跟诸如Object Pascal和Ada等其它一些语言不同，C++语言并没有内在地提供一种将类的方法作为回调函数使用的方案。在C语言中，这种回调函数被称作算子（functor），在事件驱动类程序中普遍存在。主要问题基于这样一个事实：某个类的多个实例各自位于内存的不同位置。这就需要在回调的时候不仅需要一个函数指针，同时也需要一个指针指向某个实例本身（译者注：否则回调时便无法知道目前正在操作的是哪个对象，C++类的非静态方法包含一个默认的&#8220;参数&#8221;：this指针，就是起这种作用的）。所以，针对问题的定义，有一个很直观的解决方法就是使用模板和编译时的实例化及特化。</p>
<p>&nbsp;解决方案</p>
<p>&nbsp;这里的方案只支持一个模板参数，但如果一些能够如愿的话，随着更多的编译器完全实现C++标准，以后将会支持动态的模板参数，比如&#8220;&#8230;&#8221;形式的模板参数列表（参见《C++ Templates, The Complete Guide》），那时，我们就可以可以实现无需全部预定义的参数集合。（文中所有代码的注释为译者加，下同。）</p>
<p>&nbsp;template &lt; class Class, typename ReturnType, typename Parameter &gt;<br>&nbsp;class SingularCallBack<br>&nbsp;{<br>&nbsp;&nbsp; public: <br>//指向类成员函数的指针，用他来实现回调函数。<br>&nbsp;typedef ReturnType (Class::*Method)(Parameter);<br>&nbsp;&nbsp;&nbsp;&nbsp; //构造函数<br>&nbsp;&nbsp;&nbsp;&nbsp; SingularCallBack(Class* _class_instance, Method _method)<br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class_instance = _class_instance; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = _method;<br>&nbsp;&nbsp;&nbsp;&nbsp; };<br>&nbsp;//重载函数调用运算符()<br>&nbsp;&nbsp;&nbsp;&nbsp; ReturnType operator()(Parameter parameter)<br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (class_instance-&gt;*method)(parameter);<br>&nbsp;&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp;&nbsp; //与上面的()等价的函数，引入这个函数的原因见下文<br>&nbsp;&nbsp;&nbsp;&nbsp; ReturnType execute(Parameter parameter)<br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return operator()(parameter);<br>&nbsp;};<br>&nbsp;&nbsp;&nbsp; private:<br>&nbsp;&nbsp;&nbsp;&nbsp; Class*&nbsp; class_instance;<br>&nbsp;&nbsp;&nbsp;&nbsp; Method&nbsp; method;<br>&nbsp;};<br>&nbsp;模板的使用</p>
<p>&nbsp;模板（template）的使用非常方便，模板本身可被实例化为一个对象指针（object pointer）或者一个简单的类（class）。当作为对象指针使用时，C++有另外一个令人痛苦的限制：operator() 不可以在指针未被解引用的情况下调用，对于这个限制，一个简便的但却不怎么优雅的解决方法在一个模板内部增加一个execute方法（method），由这个方法从模板内部来调用operator()。除了这一点不爽之外，实例化SinglarCallBack为一个对象指针将可以使你拥有一个由回调组成的vector，或者任何其他类型的集合，这在事件驱动程序设计中是非常需要的。<br>&nbsp;假设以下两个类已经存在，而且我们想让methodB作为我们的回调方法，从代码中我们可以看到当传递一个class A类的参数并调用methodB时，methodB会调用A类的output方法，如果你能在stdout上看到"I am class A :D"，就说明回调成功了。<br>&nbsp;class A<br>&nbsp;{<br>&nbsp;&nbsp;&nbsp; public:<br>&nbsp;&nbsp;&nbsp; void output()<br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "I am class A :D" &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp;&nbsp; };<br>&nbsp;};<br>&nbsp;class B<br>&nbsp;{<br>&nbsp;&nbsp;&nbsp; public:<br>&nbsp;&nbsp;&nbsp;&nbsp; bool methodB(A a)<br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a.output();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;};<br>&nbsp;有两种方法可以从一个对象指针上调用一个回调方法，较原始的方法是解引用（dereference）一个对象指针并运行回调方法（即：operator()），第二个选择是运行execute方法。<br>&nbsp;//第一种方法：<br>&nbsp;A a;<br>&nbsp;B b;<br>&nbsp;SingularCallBack&lt; B,bool,A &gt;* cb;<br>&nbsp;cb = new SingularCallBack&lt; B,bool,A &gt;(&amp;b,&amp;B::methodB);<br>&nbsp;if((*cb)(a))<br>&nbsp;{<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "CallBack Fired Successfully!" &lt;&lt; std::endl;<br>&nbsp;}<br>&nbsp;else<br>&nbsp;{<br>&nbsp;&nbsp; std::cout &lt;&lt; "CallBack Fired Unsuccessfully!" &lt;&lt; std::endl;<br>&nbsp;}<br>&nbsp;//第二种方法： <br>&nbsp;A a;<br>&nbsp;B b;<br>&nbsp;SingularCallBack&lt; B,bool,A &gt;* cb; <br>&nbsp;cb = new SingularCallBack&lt; B,bool,A &gt;(&amp;b,&amp;B::methodB);<br>&nbsp;if(cb-&gt;execute(a))<br>&nbsp;{<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "CallBack Fired Successfully!" &lt;&lt; std::endl;<br>&nbsp;}<br>&nbsp;else<br>&nbsp;{<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "CallBack Fired Unsuccessfully!" &lt;&lt; std::endl;<br>&nbsp;}<br>&nbsp;下面的代码示范了怎样将一个模板实例化成一个普通的对象并使用之。<br>&nbsp;A a;<br>&nbsp;B b;<br>&nbsp;SingularCallBack&lt; B,bool,A &gt;cb(&amp;b,&amp;B::methodB);<br>&nbsp;{<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "CallBack Fired Successfully!" &lt;&lt; std::endl;<br>&nbsp;}<br>&nbsp;else<br>&nbsp;{<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "CallBack Fired Unsuccessfully!" &lt;&lt; std::endl;<br>&nbsp;}<br>&nbsp;更复杂的例子，一个回调模板可以像下面这样使用：<br>&nbsp;class AClass<br>&nbsp;{<br>&nbsp;&nbsp; public:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AClass(unsigned int _id): id(_id){}; <br>&nbsp;&nbsp;&nbsp;&nbsp; ~AClass(){};<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bool AMethod(std::string str) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "AClass[" &lt;&lt; id &lt;&lt; "]: " &lt;&lt; str &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp; private: <br>&nbsp;&nbsp;&nbsp;&nbsp; unsigned int id;<br>&nbsp;};<br>&nbsp;typedef SingularCallBack &lt; AClass, bool, std::string &gt; ACallBack; <br>&nbsp;int main()<br>&nbsp;{<br>&nbsp;&nbsp;&nbsp; std::vector &lt; ACallBack &gt; callback_list;<br>&nbsp;&nbsp;&nbsp; AClass a1(1);<br>&nbsp;&nbsp;&nbsp; AClass a2(2);<br>&nbsp;&nbsp;&nbsp; AClass a3(3);<br>&nbsp;&nbsp;&nbsp; callback_list.push_back(ACallBack(&amp;a1, &amp;AClass::AMethod));<br>&nbsp;&nbsp;&nbsp; callback_list.push_back(ACallBack(&amp;a2, &amp;AClass::AMethod));<br>&nbsp;&nbsp;&nbsp; callback_list.push_back(ACallBack(&amp;a3, &amp;AClass::AMethod));<br>&nbsp;&nbsp;&nbsp; for (unsigned int i = 0; i &lt; callback_list.size(); i++)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; callback_list[i]("abc");<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;<br>&nbsp;&nbsp; for (unsigned int i = 0; i &lt; callback_list.size(); i++) <br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; callback_list[i].execute("abc");<br>&nbsp;&nbsp; }<br>&nbsp;<br>&nbsp;&nbsp; return true; <br>}</p>
<p>下面这个例子比上面的又复杂一些，你可以混合从同一个公共基类（base class）上继承下来的不同的类到一个容器中，于是你就可以调用位于继承树的最底层的类的方法（most derived method）。（译者注，C++的多态机制）<br>class BaseClass<br>{<br>&nbsp;&nbsp; public:<br>&nbsp;&nbsp;&nbsp;&nbsp; virtual ~BaseClass(){};<br>&nbsp;&nbsp;&nbsp;&nbsp; virtual bool DerivedMethod(std::string str){ return true; };<br>};</p>
<p>class AClass : public BaseClass<br>{<br>&nbsp;&nbsp; public:<br>&nbsp;&nbsp;&nbsp;&nbsp; AClass(unsigned int _id): id(_id){};<br>&nbsp;&nbsp;&nbsp; ~AClass(){};<br>&nbsp;&nbsp;&nbsp;&nbsp; bool AMethod(std::string str)<br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "AClass[" &lt;&lt; id &lt;&lt; "]: " &lt;&lt; str &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br>&nbsp;&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; bool DerivedMethod(std::string str)<br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "Derived Method AClass[" &lt;&lt; id &lt;&lt; "]: " &lt;&lt; str &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br>&nbsp;&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp; private:<br>&nbsp;&nbsp;&nbsp; unsigned int id;<br>};</p>
<p>class BClass : public BaseClass<br>{<br>&nbsp;&nbsp; public:<br>&nbsp;&nbsp;&nbsp; BClass(unsigned int _id): id(_id){};<br>&nbsp;&nbsp;&nbsp; ~BClass(){};<br>&nbsp;&nbsp;&nbsp;&nbsp; bool BMethod(std::string str) <br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "BClass[" &lt;&lt; id &lt;&lt; "]: " &lt;&lt; str &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br>&nbsp;&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; bool DerivedMethod(std::string str) <br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "Derived Method BClass[" &lt;&lt; id &lt;&lt; "]: " &lt;&lt; str &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br>&nbsp;&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp; private:<br>&nbsp;&nbsp;&nbsp; unsigned int id;</p>
<p>};</p>
<p><br>typedef SingularCallBack &lt; BaseClass, bool, std::string &gt; BaseCallBack; </p>
<p>int main()<br>{<br>&nbsp;&nbsp; std::vector &lt; BaseCallBack &gt; callback_list;</p>
<p>&nbsp;&nbsp; AClass a(1); <br>&nbsp;&nbsp; BClass b(2);</p>
<p>&nbsp;&nbsp; callback_list.push_back(BaseCallBack(&amp;a, &amp;BaseClass::DerivedMethod)); <br>&nbsp;&nbsp; callback_list.push_back(BaseCallBack(&amp;b, &amp;BaseClass::DerivedMethod));</p>
<p>&nbsp;&nbsp; for (unsigned int i = 0; i &lt; callback_list.size(); i++) <br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; callback_list[i]("abc");<br>&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp; for (unsigned int i = 0; i &lt; callback_list.size(); i++) <br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; callback_list[i].execute("abc");<br>&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp; return true; <br>}</p>
<p>为简捷起见，与实例的验证（instance validation）相关的必要代码没有被包含进来，在实际的程序设计中，类实例的传递应该基于这样的结构：使用类似智能指针（smart pointer）的包装类。STL（标准模板库）提供了两个极好的选择：aotu_ptr以及它的后继：shared_ptr。Andrei Alexandrescu所著的《Modern C++ Design》一书也提供了一个面向策略设计（policy design oriented）的智能指针类。这三种方案中各有自己的优缺点，最终由用户自己来决定究竟那一种最适合他们的需要。<br></p>
<img src ="http://www.cppblog.com/mmdengwo/aggbug/144246.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-14 22:12 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/14/144246.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++拷贝构造函数的几个细节</title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143487.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Tue, 05 Apr 2011 15:44:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143487.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/143487.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143487.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/143487.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/143487.html</trackback:ping><description><![CDATA[<div class=postText>
<h3><font color=#002c99>C++拷贝构造函数的几个细节(转自：<a href="http://grantren.javaeye.com/blog/43289"><font color=#0066aa>http://grantren.javaeye.com/blog/43289</font></a>)</font><a href="http://grantren.javaeye.com/blog/43289"><u><font color=#810081></font></u></a></h3>
<div class=blog_content>
<p><font face=Arial>一 拷贝构造函数是<span class=hilite1>C</span>++最基础的概念之一，大家自认为对拷贝构造函数了解么？请大家先回答一下三个问题:</font></p>
<p><font face=Arial><strong>1.</strong> 以下函数哪个是拷贝构造函数,为什么?</font></p>
<div class=dp-highlighter>
<div class=bar></div>
<ol class=dp-cpp>
    <li class=alt><span><span>X::X(</span><span class=keyword><span class=hilite1>c</span>onst</span><span>&nbsp;X&amp;); &nbsp;&nbsp;</span></span>
    <li class=""><span>X::X(X); &nbsp;&nbsp;</span>
    <li class=alt><span>X::X(X&amp;,&nbsp;</span><span class=datatypes>int</span><span>&nbsp;a=1); &nbsp;&nbsp;</span>
    <li class=""><span>X::X(X&amp;,&nbsp;</span><span class=datatypes>int</span><span>&nbsp;a=1,&nbsp;b=2);&nbsp;&nbsp;</span> </li>
</ol>
</div>
<p>&nbsp;<font face=Arial><strong>2.</strong> 一个类中可以存在多于一个的拷贝构造函数吗?</font></p>
<p><font face=Arial><strong>3.</strong> 写出以下程序段的输出结果, 并说明为什么?</font><strong> </strong><strong></strong>如果你都能回答无误的话，那么你已经对<font face=Arial>拷贝构造函数有了相当的了解。</font></p>
<div class=dp-highlighter>
<div class=bar></div>
<ol class=dp-cpp>
    <li class=alt><span><span class=preprocessor>#in<span class=hilite1>c</span>lude&nbsp;<iostream></iostream> </span><span>&nbsp;&nbsp;</span></span>
    <li class=""><span></span><span class=preprocessor>#in<span class=hilite1>c</span>lude&nbsp;<string></string> </span><span>&nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;</span>
    <li class=""><span></span><span class=keyword>stru<span class=hilite1>c</span>t</span><span>&nbsp;X&nbsp;{ &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;</span><span class=keyword>template</span><span>&lt;</span><span class=keyword>typename</span><span>&nbsp;T&gt; &nbsp;&nbsp;</span>
    <li class=""><span>&nbsp;&nbsp;X(&nbsp;T&amp;&nbsp;)&nbsp;{&nbsp;std::<span class=hilite1>c</span>out&nbsp;&lt;&lt;&nbsp;</span><span class=string>"This&nbsp;is&nbsp;<span class=hilite1>c</span>tor."</span><span>&nbsp;&lt;&lt;&nbsp;std::endl;&nbsp;} &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;</span>
    <li class=""><span>&nbsp;&nbsp;</span><span class=keyword>template</span><span>&lt;</span><span class=keyword>typename</span><span>&nbsp;T&gt; &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;X&amp;&nbsp;operator=(&nbsp;T&amp;&nbsp;)&nbsp;{&nbsp;std::<span class=hilite1>c</span>out&nbsp;&lt;&lt;&nbsp;</span><span class=string>"This&nbsp;is&nbsp;<span class=hilite1>c</span>tor."</span><span>&nbsp;&lt;&lt;&nbsp;std::endl;&nbsp;} &nbsp;&nbsp;</span>
    <li class=""><span>}; &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;</span>
    <li class=""><span></span><span class=keyword>void</span><span>&nbsp;main()&nbsp;{ &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;X&nbsp;a(5); &nbsp;&nbsp;</span>
    <li class=""><span>&nbsp;&nbsp;X&nbsp;b(10.5); &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;X&nbsp;<span class=hilite1>c</span>&nbsp;=&nbsp;a; &nbsp;&nbsp;</span>
    <li class=""><span>&nbsp;&nbsp;<span class=hilite1>c</span>&nbsp;=&nbsp;b; &nbsp;&nbsp;</span>
    <li class=alt><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<p>&nbsp;</p>
<p><strong>解答如下：</strong></p>
<p><strong>1. </strong><font face=Arial>对于一个类X,如果一个构造函数的第一个参数是下列之一:<br>a) X&amp;<br>b) <span class=hilite1>c</span>onst X&amp;<br><span class=hilite1>c</span>) volatile X&amp;<br>d) <span class=hilite1>c</span>onst volatile X&amp;<br>且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数.&nbsp;</font></p>
<div class=dp-highlighter>
<div class=bar></div>
<ol class=dp-cpp>
    <li class=alt><strong><span><span>X::X(</span><span class=keyword><span class=hilite1>c</span>onst</span><span>&nbsp;X&amp;);&nbsp;&nbsp;</span><span class=comment>//是拷贝构造函数 </span><span>&nbsp;&nbsp;</span></span> </strong>
    <li class=""><strong><span>X::X(X&amp;,&nbsp;</span><span class=datatypes>int</span><span>=1);&nbsp;</span><span class=comment>//是拷贝构造函数</span><span>&nbsp;&nbsp;</span> </strong></li>
</ol>
</div>
<p>&nbsp;2.<font face=Arial>类中可以存在超过一个拷贝构造函数,</font>&nbsp;</p>
<div class=dp-highlighter>
<div class=bar></div>
<ol class=dp-cpp>
    <li class=alt><span><span class=keyword><span class=hilite1>c</span>lass</span><span><strong>&nbsp;X&nbsp;{&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</strong></span></span><strong> </strong>
    <li class=""><span></span><span class=keyword>publi<span class=hilite1>c</span></span><strong><span>:&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </strong>
    <li class=alt><strong><span>&nbsp;&nbsp;X(</span><span class=keyword><span class=hilite1>c</span>onst</span><span>&nbsp;X&amp;);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </strong>
    <li class=""><strong><span>&nbsp;&nbsp;X(X&amp;);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment>//&nbsp;OK </span><span>&nbsp;&nbsp;</span> </strong>
    <li class=alt><strong><span>};&nbsp;&nbsp;</span> </strong></li>
</ol>
</div>
<p>注意,如果一个类中只存在一个参数为X&amp;的拷贝构造函数,那么就不能使用<span class=hilite1>c</span>onst X或volatile X的对象实行拷贝初始化.</p>
<div class=dp-highlighter>
<div class=bar></div>
<ol class=dp-cpp>
    <li class=alt><span><span class=keyword><span class=hilite1>c</span>lass</span><span><strong>&nbsp;X&nbsp;{ &nbsp;&nbsp;</strong></span></span><strong> </strong>
    <li class=""><span></span><span class=keyword>publi<span class=hilite1>c</span></span><strong><span>: &nbsp;&nbsp;</span> </strong>
    <li class=alt><strong><span>&nbsp;&nbsp;X(); &nbsp;&nbsp;</span> </strong>
    <li class=""><strong><span>&nbsp;&nbsp;X(X&amp;); &nbsp;&nbsp;</span> </strong>
    <li class=alt><strong><span>}; &nbsp;&nbsp;</span> </strong>
    <li class=""><strong><span>&nbsp; &nbsp;&nbsp;</span> </strong>
    <li class=alt><span></span><span class=keyword><span class=hilite1>c</span>onst</span><strong><span>&nbsp;X&nbsp;<span class=hilite1>c</span>x; &nbsp;&nbsp;</span> </strong>
    <li class=""><strong><span>X&nbsp;x&nbsp;=&nbsp;<span class=hilite1>c</span>x;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment>//&nbsp;error </span><span>&nbsp;&nbsp;</span> </strong></li>
</ol>
</div>
<p>如果一个类中没有定义拷贝构造函数,那么编译器会自动产生一个默认的拷贝构造函数.<br>这个默认的参数可能为X::X(<span class=hilite1>c</span>onst X&amp;)或X::X(X&amp;),由编译器根据上下文决定选择哪一个.</p>
<font face=Arial><font face=Arial>
<p><font face=Arial>默认拷贝构造函数的行为如下:<br>&nbsp;默认的拷贝构造函数执行的顺序与其他用户定义的构造函数相同,执行先父类后子类的构造.<br>&nbsp;拷贝构造函数对类中每一个数据成员执行成员拷贝(memberwise <span class=hilite1>C</span>opy)的动作.<br>&nbsp;a)如果数据成员为某一个类的实例,那么调用此类的拷贝构造函数.<br>&nbsp;b)如果数据成员是一个数组,对数组的每一个执行按位拷贝. <br>&nbsp;<span class=hilite1>c</span>)如果数据成员是一个数量,如int,double,那么调用系统内建的赋值运算符对其进行赋值.</font></p>
<p><font face=Arial></font><strong>&nbsp;</strong></p>
<p><font face=Arial><strong>3.</strong>&nbsp; 拷贝构造函数不能由成员函数模版生成.</font>&nbsp;</p>
<div class=dp-highlighter>
<div class=bar></div>
<ol class=dp-cpp>
    <li class=alt><span><span class=keyword>stru<span class=hilite1>c</span>t</span><span><strong>&nbsp;X&nbsp;{ &nbsp;&nbsp;</strong></span></span><strong> </strong>
    <li class=""><strong><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword>template</span><span>&lt;</span><span class=keyword>typename</span><span>&nbsp;T&gt; &nbsp;&nbsp;</span> </strong>
    <li class=alt><strong><span>&nbsp;&nbsp;&nbsp;&nbsp;X(&nbsp;</span><span class=keyword><span class=hilite1>c</span>onst</span><span>&nbsp;T&amp;&nbsp;);&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment>//&nbsp;NOT&nbsp;<span class=hilite1>c</span>opy&nbsp;<span class=hilite1>c</span>tor,&nbsp;T&nbsp;<span class=hilite1>c</span>an't&nbsp;be&nbsp;X </span><span>&nbsp;&nbsp;</span> </strong>
    <li class=""><strong><span>&nbsp;&nbsp;</span> </strong>
    <li class=alt><strong><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword>template</span><span>&lt;</span><span class=keyword>typename</span><span>&nbsp;T&gt; &nbsp;&nbsp;</span> </strong>
    <li class=""><strong><span>&nbsp;&nbsp;&nbsp;&nbsp;operator=(&nbsp;</span><span class=keyword><span class=hilite1>c</span>onst</span><span>&nbsp;T&amp;&nbsp;);&nbsp;&nbsp;</span><span class=comment>//&nbsp;NOT&nbsp;<span class=hilite1>c</span>opy&nbsp;ass't,&nbsp;T&nbsp;<span class=hilite1>c</span>an't&nbsp;be&nbsp;X </span><span>&nbsp;&nbsp;</span> </strong>
    <li class=alt><strong><span>}; &nbsp;&nbsp;</span> </strong>
    <li class=""><strong><span>&nbsp;&nbsp;</span> </strong></li>
</ol>
</div>
<p>原因很简单, 成员函数模版并不改变语言的规则,而语言的规则说,如果程序需要一个拷贝构造函数而你没有声明它,那么编译器会为你自动生成一个. 所以成员函数模版并不会阻止编译器生成拷贝构造函数, 赋值运算符重载也遵循同样的规则.(参见Effe<span class=hilite1>c</span>tive <span class=hilite1>C</span>++ 3edition, Item45)<br></p>
</font></font></div>
<br>二 针对上面作者的讨论，理解更深了，但是下面我还是会给出一个一般的标准的实现和注意事项：<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">stdafx.h</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>#include&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">stdio.h</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">iostream</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;Test1&nbsp;<br><img id=Codehighlighter1_93_365_Open_Image onclick="this.style.display='none'; Codehighlighter1_93_365_Open_Text.style.display='none'; Codehighlighter1_93_365_Closed_Image.style.display='inline'; Codehighlighter1_93_365_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_93_365_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_93_365_Closed_Text.style.display='none'; Codehighlighter1_93_365_Open_Image.style.display='inline'; Codehighlighter1_93_365_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_93_365_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_93_365_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_107_109_Open_Image onclick="this.style.display='none'; Codehighlighter1_107_109_Open_Text.style.display='none'; Codehighlighter1_107_109_Closed_Image.style.display='inline'; Codehighlighter1_107_109_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_107_109_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_107_109_Closed_Text.style.display='none'; Codehighlighter1_107_109_Open_Image.style.display='inline'; Codehighlighter1_107_109_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Test1()&nbsp;</span><span id=Codehighlighter1_107_109_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_107_109_Open_Text><span style="COLOR: #000000">{&nbsp;}</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_128_138_Open_Image onclick="this.style.display='none'; Codehighlighter1_128_138_Open_Text.style.display='none'; Codehighlighter1_128_138_Closed_Image.style.display='inline'; Codehighlighter1_128_138_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_128_138_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_128_138_Closed_Text.style.display='none'; Codehighlighter1_128_138_Open_Image.style.display='inline'; Codehighlighter1_128_138_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Test1(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i)&nbsp;</span><span id=Codehighlighter1_128_138_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_128_138_Open_Text><span style="COLOR: #000000">{&nbsp;id&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;i;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Test1(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Test1</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;test)<br><img id=Codehighlighter1_173_201_Open_Image onclick="this.style.display='none'; Codehighlighter1_173_201_Open_Text.style.display='none'; Codehighlighter1_173_201_Closed_Image.style.display='inline'; Codehighlighter1_173_201_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_173_201_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_173_201_Closed_Text.style.display='none'; Codehighlighter1_173_201_Open_Image.style.display='inline'; Codehighlighter1_173_201_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_173_201_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_173_201_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;test.id;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Test1</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Test1</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;test)<br><img id=Codehighlighter1_249_351_Open_Image onclick="this.style.display='none'; Codehighlighter1_249_351_Open_Text.style.display='none'; Codehighlighter1_249_351_Closed_Image.style.display='inline'; Codehighlighter1_249_351_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_249_351_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_249_351_Closed_Text.style.display='none'; Codehighlighter1_249_351_Open_Image.style.display='inline'; Codehighlighter1_249_351_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_249_351_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_249_351_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">test)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;test.id;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;id;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Test2<br><img id=Codehighlighter1_381_1170_Open_Image onclick="this.style.display='none'; Codehighlighter1_381_1170_Open_Text.style.display='none'; Codehighlighter1_381_1170_Closed_Image.style.display='inline'; Codehighlighter1_381_1170_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_381_1170_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_381_1170_Closed_Text.style.display='none'; Codehighlighter1_381_1170_Open_Image.style.display='inline'; Codehighlighter1_381_1170_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_381_1170_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_381_1170_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img id=Codehighlighter1_402_419_Open_Image onclick="this.style.display='none'; Codehighlighter1_402_419_Open_Text.style.display='none'; Codehighlighter1_402_419_Closed_Image.style.display='inline'; Codehighlighter1_402_419_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_402_419_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_402_419_Closed_Text.style.display='none'; Codehighlighter1_402_419_Open_Image.style.display='inline'; Codehighlighter1_402_419_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Test2()</span><span id=Codehighlighter1_402_419_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_402_419_Open_Text><span style="COLOR: #000000">{&nbsp;m_pChar&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;NULL;}</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_444_462_Open_Image onclick="this.style.display='none'; Codehighlighter1_444_462_Open_Text.style.display='none'; Codehighlighter1_444_462_Closed_Image.style.display='inline'; Codehighlighter1_444_462_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_444_462_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_444_462_Closed_Text.style.display='none'; Codehighlighter1_444_462_Open_Image.style.display='inline'; Codehighlighter1_444_462_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Test2(</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">pChar)&nbsp;</span><span id=Codehighlighter1_444_462_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_444_462_Open_Text><span style="COLOR: #000000">{&nbsp;m_pChar&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;pChar;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Test2(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;num)&nbsp;<br><img id=Codehighlighter1_488_626_Open_Image onclick="this.style.display='none'; Codehighlighter1_488_626_Open_Text.style.display='none'; Codehighlighter1_488_626_Closed_Image.style.display='inline'; Codehighlighter1_488_626_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_488_626_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_488_626_Closed_Text.style.display='none'; Codehighlighter1_488_626_Open_Image.style.display='inline'; Codehighlighter1_488_626_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_488_626_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_488_626_Open_Text><span style="COLOR: #000000">{&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_pChar&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">[num];<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(</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;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;i</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;num;&nbsp;</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">i)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_pChar[i]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">'</span><span style="COLOR: #000000">a</span><span style="COLOR: #000000">'</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_pChar[num</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">'</span><span style="COLOR: #000000">\0</span><span style="COLOR: #000000">'</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Test2(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Test2</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;test)<br><img id=Codehighlighter1_661_839_Open_Image onclick="this.style.display='none'; Codehighlighter1_661_839_Open_Text.style.display='none'; Codehighlighter1_661_839_Closed_Image.style.display='inline'; Codehighlighter1_661_839_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_661_839_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_661_839_Closed_Text.style.display='none'; Codehighlighter1_661_839_Open_Image.style.display='inline'; Codehighlighter1_661_839_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_661_839_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_661_839_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">pCharT&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;m_pChar;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_pChar&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">[strlen(test.m_pChar)];<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strcpy(m_pChar,&nbsp;test.m_pChar);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">pCharT)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;[]pCharT;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Test2</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Test2</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;test)<br><img id=Codehighlighter1_887_1140_Open_Image onclick="this.style.display='none'; Codehighlighter1_887_1140_Open_Text.style.display='none'; Codehighlighter1_887_1140_Closed_Image.style.display='inline'; Codehighlighter1_887_1140_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_887_1140_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_887_1140_Closed_Text.style.display='none'; Codehighlighter1_887_1140_Open_Image.style.display='inline'; Codehighlighter1_887_1140_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_887_1140_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_887_1140_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">test)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">pCharT&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;m_pChar;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_pChar&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">[strlen(test.m_pChar)];<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strcpy(m_pChar,&nbsp;test.m_pChar);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">pCharT)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;[]pCharT;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">m_pChar;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;main(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;argc,&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;argv[])<br><img id=Codehighlighter1_1207_1530_Open_Image onclick="this.style.display='none'; Codehighlighter1_1207_1530_Open_Text.style.display='none'; Codehighlighter1_1207_1530_Closed_Image.style.display='inline'; Codehighlighter1_1207_1530_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_1207_1530_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1207_1530_Closed_Text.style.display='none'; Codehighlighter1_1207_1530_Open_Image.style.display='inline'; Codehighlighter1_1207_1530_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_1207_1530_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1207_1530_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Test1&nbsp;ts(</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;Test1()</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Test1</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;p_ts&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">ts;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Test1&nbsp;ts2(ts);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">Test(const&nbsp;Test1&amp;&nbsp;test)</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Test1&nbsp;ts3&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;ts;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">Test(const&nbsp;Test1&amp;&nbsp;test)</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;Test1&nbsp;ts4;&nbsp;ts4&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;ts;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">Test1&amp;&nbsp;operator&nbsp;=&nbsp;(const&nbsp;Test1&amp;&nbsp;test)</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Test2&nbsp;t(</span><span style="COLOR: #000000">5</span><span style="COLOR: #000000">);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Test2&nbsp;t2(t);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Test2&nbsp;t3&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;t2;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Test2&nbsp;t4;&nbsp;t4&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;t;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<br><br></div>
<img src ="http://www.cppblog.com/mmdengwo/aggbug/143487.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-05 23:44 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/05/143487.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>explicit关键字</title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143460.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Tue, 05 Apr 2011 10:00:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143460.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/143460.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143460.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/143460.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/143460.html</trackback:ping><description><![CDATA[<span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 13px; LINE-HEIGHT: 19px; FONT-FAMILY: Verdana, Geneva, Arial, Helvetica, sans-serif">《<font color=#551a8b>ANSI/</font><font color=#cc0033>ISO C++</font><font color=#551a8b><span class=Apple-converted-space>&nbsp;</span>Professional Programmer's Handbook<span class=Apple-converted-space>&nbsp;</span></font>》是这样说的<br><br>explicit Constructors<br>A constructor that takes a single argument is, by default, an implicit conversion operator, which converts its argument to<br>an object of its class (see also Chapter 3, "Operator Overloading"). Examine the following concrete example:<br>class string<br>{<br>private:<br>int size;<br>int capacity;<br>char *buff;<br>public:<br>string();<br>string(int size); // constructor and implicit conversion operator<br>string(const char *); // constructor and implicit conversion operator<br>~string();<br>};<br>Class string has three constructors: a default constructor, a constructor that takes int, and a constructor that<br>constructs a string from const char *. The second constructor is used to create an empty string object with an<br>initial preallocated buffer at the specified size. However, in the case of class string, the automatic conversion is<br>dubious. Converting an int into a string object doesn't make sense, although this is exactly what this constructor does.<br><br>Consider the following:<br>int main()<br>{<br>string s = "hello"; //OK, convert a C-string into a string object<br>int ns = 0;<br>s = 1; // 1 oops, programmer intended to write ns = 1,<br>}<br>In the expression s= 1;, the programmer simply mistyped the name of the variable ns, typing s instead. Normally,<br>the compiler detects the incompatible types and issues an error message. However, before ruling it out, the compiler first<br>searches for a user-defined conversion that allows this expression; indeed, it finds the constructor that takes int.<br>Consequently, the compiler interprets the expression s= 1; as if the programmer had written<br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 13px; LINE-HEIGHT: 19px; FONT-FAMILY: Verdana, Geneva, Arial, Helvetica, sans-serif">s = string(1);<br>You might encounter a similar problem when calling a function that takes a string argument. The following example<br>can either be a cryptic coding style or simply a programmer's typographical error. However, due to the implicit<br>conversion constructor of class string, it will pass unnoticed:<br>int f(string s);<br>int main()<br>{<br>f(1); // without a an explicit constructor,<br>//this call is expanded into: f ( string(1) );<br>//was that intentional or merely a programmer's typo?<br>}<br>'In order to avoid such implicit conversions, a constructor that takes one argument needs to be declared explicit:<br>class string<br>{<br>//...<br>public:<br>explicit string(int size); // block implicit conversion<br>string(const char *); //implicit conversion<br>~string();<br>};<br>An explicit constructor does not behave as an implicit conversion operator, which enables the compiler to catch the<br>typographical error this time:<br>int main()<br>{<br>string s = "hello"; //OK, convert a C-string into a string object<br>int ns = 0;<br>s = 1; // compile time error ; this time the compiler catches the typo<br>}<br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 13px; LINE-HEIGHT: 19px; FONT-FAMILY: Verdana, Geneva, Arial, Helvetica, sans-serif">Why aren't all constructors automatically declared explicit? Under some conditions, the automatic type conversion is<br>useful and well behaved. A good example of this is the third constructor of string:<br>string(const char *);<br><br>The implicit type conversion of const char * to a string object enables its users to write the following:<br>string s;<br>s = "Hello";<br>The compiler implicitly transforms this into<br>string s;<br>//pseudo C++ code:<br>s = string ("Hello"); //create a temporary and assign it to s<br>On the other hand, if you declare this constructor explicit, you have to use explicit type conversion:<br>class string<br>{<br>//...<br>public:<br>explicit string(const char *);<br>};<br>int main()<br>{<br>string s;<br>s = string("Hello"); //explicit conversion now required<br>return 0;<br>}<br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 13px; LINE-HEIGHT: 19px; FONT-FAMILY: Verdana, Geneva, Arial, Helvetica, sans-serif">Extensive amounts of legacy C++ code rely on the implicit conversion of constructors. The C++ Standardization<br>committee was aware of that. In order to not make existing code break, the implicit conversion was retained. However, a<br>new keyword, explicit, was introduced to the languageto enable the programmer to block the implicit conversion<br>when it is undesirable. As a rule, a constructor that can be invoked with a single argument needs to be declared<br>explicit. When the implicit type conversion is intentional and well behaved, the constructor can be used as an<br>implicit conversion operator.</span></span></span></span></span></span></span></span>
<img src ="http://www.cppblog.com/mmdengwo/aggbug/143460.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-05 18:00 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/05/143460.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>volatile——编写多线程程序的好帮手</title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143455.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Tue, 05 Apr 2011 09:41:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143455.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/143455.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143455.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/143455.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/143455.html</trackback:ping><description><![CDATA[<p align=left><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">http://www.c-view.org/journal/006/gp_aa.htm<span class=Apple-converted-space>&nbsp;</span><br><br><br>并不是我故意想弄糟你的心情，但是在这期专栏里，我们将讨论多线程编程这一话题。正如上一期Generic里所说的，编写异常安全（exception-safe）的程序是非常困难的，但是和编写多线程程序比起来，那简直就是儿戏。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>多线程的程序是出了名的难编写、难验证、难调试、难维护，这通常是件苦差事。不正确的多线程程序可能可以运行很多年也不出一点错，直到满足某些临界的条件时，才出现意想不到的奇怪错误。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>不用说，编写多线程程序的程序员需要使用可能得到的所有帮助。这期专栏将专注于讨论竞争条件（race&nbsp;conditions）——这通常是多线程程序中各种麻烦的根源——深入了解它并提供一些工具来防止竞争。令人惊异的是，我们将让编译器尽其所能来帮助你做这些事。<br><br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">仅仅一个不起眼的关键字<span class=Apple-converted-space>&nbsp;</span><br>尽管C和C++标准对于线程都明显的&#8220;保持沉默&#8221;，但它们以volatile关键字的形式，确实为多线程保留了一点特权。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>就象大家更熟悉的const一样，volatile是一个类型修饰符（type&nbsp;modifier）。它是被设计用来修饰被不同线程访问和修改的变量。如果没有volatile，基本上会导致这样的结果：要么无法编写多线程程序，要么编译器失去大量优化的机会。下面我们来一个个说明。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>考虑下面的代码：&nbsp;<span class=Apple-converted-space>&nbsp;</span><br></p>
<div align=left>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;1</span><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Gadget<br></span><span style="COLOR: #008080">&nbsp;2</span><span style="COLOR: #000000"><img id=Codehighlighter1_13_240_Open_Image onclick="this.style.display='none'; Codehighlighter1_13_240_Open_Text.style.display='none'; Codehighlighter1_13_240_Closed_Image.style.display='inline'; Codehighlighter1_13_240_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_13_240_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_13_240_Closed_Text.style.display='none'; Codehighlighter1_13_240_Open_Image.style.display='inline'; Codehighlighter1_13_240_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_13_240_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_13_240_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;3</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br></span><span style="COLOR: #008080">&nbsp;4</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;Wait()<br></span><span style="COLOR: #008080">&nbsp;5</span><span style="COLOR: #000000"><img id=Codehighlighter1_43_149_Open_Image onclick="this.style.display='none'; Codehighlighter1_43_149_Open_Text.style.display='none'; Codehighlighter1_43_149_Closed_Image.style.display='inline'; Codehighlighter1_43_149_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_43_149_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_43_149_Closed_Text.style.display='none'; Codehighlighter1_43_149_Open_Image.style.display='inline'; Codehighlighter1_43_149_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_43_149_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_43_149_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;6</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">flag_)<br></span><span style="COLOR: #008080">&nbsp;7</span><span style="COLOR: #000000"><img id=Codehighlighter1_76_143_Open_Image onclick="this.style.display='none'; Codehighlighter1_76_143_Open_Text.style.display='none'; Codehighlighter1_76_143_Closed_Image.style.display='inline'; Codehighlighter1_76_143_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_76_143_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_76_143_Closed_Text.style.display='none'; Codehighlighter1_76_143_Open_Image.style.display='inline'; Codehighlighter1_76_143_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_76_143_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_76_143_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;8</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sleep(</span><span style="COLOR: #000000">1000</span><span style="COLOR: #000000">);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;sleeps&nbsp;for&nbsp;1000&nbsp;milliseconds</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;9</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">10</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">11</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;Wakeup()<br></span><span style="COLOR: #008080">12</span><span style="COLOR: #000000"><img id=Codehighlighter1_173_203_Open_Image onclick="this.style.display='none'; Codehighlighter1_173_203_Open_Text.style.display='none'; Codehighlighter1_173_203_Closed_Image.style.display='inline'; Codehighlighter1_173_203_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_173_203_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_173_203_Closed_Text.style.display='none'; Codehighlighter1_173_203_Open_Image.style.display='inline'; Codehighlighter1_173_203_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_173_203_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_173_203_Open_Text><span style="COLOR: #000000">{&nbsp;<br></span><span style="COLOR: #008080">13</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;flag_&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></span><span style="COLOR: #008080">14</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">15</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.cppblog.com/Images/dot.gif"><br></span><span style="COLOR: #008080">16</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:&nbsp;<br></span><span style="COLOR: #008080">17</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">bool</span><span style="COLOR: #000000">&nbsp;flag_;&nbsp;<br></span><span style="COLOR: #008080">18</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">19</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">20</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
</div>
<p align=left><br>&nbsp;<span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px"><br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">上面代码中Gadget::Wait的目的是每过一秒钟去检查一下flag_成员变量，当flag_被另一个线程设为true时，该函数才会返回。至少这是程序作<br>者的意图，然而，这个Wait函数是错误的.假设编译器发现Sleep(1000)是调用一个外部的库函数，它不会改变成员变量flag_，那么编译器就可<br>以断定它可以把flag_缓存在寄存器中，以后可以访问该寄存器来代替访问较慢的主板上的内存。这对于单线程代码来说是一个很好的优化，但<br>是在现在这种情况下，它破坏了程序的正确性：当你调用了某个Gadget的Wait函数后，即使另一个线程调用了Wakeup，Wait还是会一直循环下去。<br>这是因为flag_的改变没有反映到缓存它的寄存器中去。编译器的优化未免有点太&#8230;&#8230;乐观了。<br></span></span><br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">在大多数情况下，把变量缓存在寄存器中是一个非常有价值的优化方法，如果不用的话很可惜。C和C++给你提供了显式禁用这种缓存优化的机会。<br>如果你声明变量是使用了volatile修饰符，编译器就不会把这个变量缓存在寄存器里——每次访问都将去存取变量在内存中的实际位置。这样你<br>要对Gadget的Wait/Wakeup做的修改就是给flag_加上正确的修饰：&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>class&nbsp;Gadget<span class=Apple-converted-space>&nbsp;</span><br>{<span class=Apple-converted-space>&nbsp;</span><br>public:<span class=Apple-converted-space>&nbsp;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;...&nbsp;as&nbsp;above&nbsp;...<span class=Apple-converted-space>&nbsp;</span><br>private:<span class=Apple-converted-space>&nbsp;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;volatile&nbsp;bool&nbsp;flag_;<span class=Apple-converted-space>&nbsp;</span><br>};<span class=Apple-converted-space>&nbsp;<br><br></span><br><br>大多数关于volatile的原理和用法的解释就到此为止，并且建议你用volatile修饰在多个线程中使用的原生类型变量。然而，你可以用volatile<br>做更多的事，因为它是神奇的C++类型系统的一部分。<br><br><br><br><br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">把volatile用于自定义类型<span class=Apple-converted-space>&nbsp;</span><br>volatile修饰不仅可以用于原生类型，也可以用于自定义类型。这时候，volatile修饰方式类似于const（你也可以对一个类型同时使用const<br>和volatile）.<br><br>与const不同，volatile的作用对于原生类型和自定义类型是有区别的。就是说，原生类型有volatile修饰时，仍然支持它们的各种操作（加、<br>乘、赋值等等），然而对于class来说，就不是这样。举例来说，你可以把一个非volatile的int的值赋给一个volatile的int，但是你不能把一<br>个非volatile的对象赋给一个volatile对象。&nbsp;<br><br>让我们举个例子来说明自定义类型的volatile是怎么工作的。&nbsp;<span class=Apple-converted-space>&nbsp;<br></span></span></span></span></span></span></span><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px"><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px"><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">class&nbsp;Gadget</p>
{<br>public: <br>&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;Foo()&nbsp;volatile; <br>&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;Bar();<br>&nbsp;&nbsp;&nbsp;&nbsp;... <br>private:<br>&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;name_; <br>&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;state_; <br>};</span></span><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">
<pre>...
<br>Gadget&nbsp;regularGadget;
<br>volatile&nbsp;Gadget&nbsp;volatileGadget;
<br>
<br></pre>
<p><br><br>如果你认为volatile对于对象来说没有什么作用的话，那你可要大吃一惊了。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br>volatileGadget.Foo();&nbsp;//&nbsp;ok,&nbsp;volatile&nbsp;fun&nbsp;called&nbsp;for<span class=Apple-converted-space>&nbsp;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;volatile&nbsp;object<span class=Apple-converted-space>&nbsp;</span><br>regularGadget.Foo();&nbsp;&nbsp;//&nbsp;ok,&nbsp;volatile&nbsp;fun&nbsp;called&nbsp;for<span class=Apple-converted-space>&nbsp;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;non-volatile&nbsp;object<span class=Apple-converted-space>&nbsp;</span><br>volatileGadget.Bar();&nbsp;//&nbsp;error!&nbsp;Non-volatile&nbsp;function&nbsp;called&nbsp;for<span class=Apple-converted-space>&nbsp;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;volatile&nbsp;object!<span class=Apple-converted-space>&nbsp;</span><br><br>从没有volatile修饰的类型到相应的volatile类型的转换是很平常的。但是，就象const一样，你不能反过来把volatile类型转换为非volatile类型。你必须用类型转换运算符：&nbsp;<span class=Apple-converted-space>&nbsp;</span><br>Gadget&amp;&nbsp;ref&nbsp;=&nbsp;const_cast&lt;Gadget&amp;&gt;;(volatileGadget);<span class=Apple-converted-space>&nbsp;</span><br>ref.Bar();&nbsp;//&nbsp;ok<span class=Apple-converted-space>&nbsp;</span><br><br>一个有volatile修饰的类只允许访问其接口的一个子集，这个子集由类的实现者来控制。用户只有用const_cast才可以访问这个类型的全部接口。而且，象const一样，类的volatile属性会传递给它的成员（例如，volatileGadget.name_和volatileGadget.state_也是volatile变量）。<br><br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">volatile，临界区和竞争条件<span class=Apple-converted-space>&nbsp;</span><br>多线程程序中最简单也是最常用的同步机制要算是mutex（互斥对象）了。一个mutex只提供两个基本操作：Acquire和Release。一旦某个线程调用了Acquire，其他线程再调用Acquire时就会被阻塞。当这个线程调用Release后，刚才阻塞在Acquire里的线程中，会有一个且仅有一个被唤醒。换句话说，对于一个给定的mutex，只有一个线程可以在Acquire和Release调用之间获取处理器时间。在Acquire和Release调用之间执行的代码叫做临界区（critical&nbsp;section）。（Windows的用语可能会引起一点混乱，因为Windows把mutex本身叫做临界区，而Windows的mutex实际上指进程间的mutex。如果把它们分别叫作线程mutex和进程mutex可能会好些。）&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>Mutex是用来避免数据出现竞争条件。根据定义，所谓竞争条件就是这样一种情况：多个线程对数据产生的作用要依赖于线程的调度顺序的。当两个线程竞相访问同一数据时，就会发生竞争条件。因为一个线程可以在任意一个时刻打断其他线程，数据可能会被破坏或者被错误地解释。因此，对数据的修改操作，以及有些情况下的访问操作，必须用临界区保护起来。在面向对象的编程中，这通常意味着你在一个类的成员变量中保存一个mutex，然后在你访问这个类的状态时使用这个mutex。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>多线程编程高手看了上面两个段落，可能已经在打哈欠了，但是它们的目的只是提供一个准备练习，我们现在要和volatile联系起来了。我们将把C++的类型和线程的语义作一个对比。<br><br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">在一个临界区以外，任意线程会在任何时间打断别的线程；这是不受控制的，所以被多个线程访问的变量容易被改得面目全非。这和volatile的原意[1]是一致的——所以需要用volatile来防止编译器无意地缓存这样的变量。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>在由一个mutex限定的临界区里，只有一个线程可以进入。因此，在临界区中执行的代码有和单线程程序有相同的语义。被控制的变量不会再被意外改变——你可以去掉volatile修饰。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>简而言之，线程间共享的数据在临界区之外是volatile的，而在临界区之内则不是。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>你通过对一个mutex加锁来进入一个临界区，然后你用const_cast去掉某个类型的volatile修饰，如果我们能成功地把这两个操作放到一起，那么我们就在C++类型系统和应用程序的线程语义建立起联系。这样我们可以让编译器来帮我们检测竞争条件。<br><br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">LockingPtr<span class=Apple-converted-space>&nbsp;</span><br>我们需要有一个工具来做mutex的获取和const_cast两个操作。让我们来设计一个LockingPtr类，你需要用一个volatile的对象obj和一个mutex对象mtx来初始化它。在LockingPtr对象的生命期中，它会保证mtx处于被获取状态，而且也提供对去掉volatile修饰的obj的访问。对obj的访问类似于smart&nbsp;pointer，是通过operator-&gt;;和operator*来进行的。const_cast是在LockingPtr内部进行。这个转化在语义上是正确的，因为LockingPtr在其生存期中始终拥有mutex。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>首先，我们来定义和LockingPtr一起工作的Mutex类的框架：&nbsp;<span class=Apple-converted-space>&nbsp;<br></span><br>class&nbsp;Mutex<br><br>{<br><br>public:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;Acquire();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;Release();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;...&nbsp;&nbsp;&nbsp;&nbsp;<br><br>};<br></p>
<p></span></span><br class=Apple-interchange-newline></span></span></span></span></span></span></span></span><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">为了使用LockingPtr，你需要用操作系统提供的数据结构和底层函数来实现Mutex。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br>LockingPtr是一个模板，用被控制变量的类型作为模板参数。例如，如果你希望控制一个Widget，你就要这样写LockingPtr&nbsp;&lt;Widget&gt;;。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>LockingPtr的定义很简单，它只是实现了一个单纯的smart&nbsp;pointer。它关注的焦点只是在于把const_cast和临界区操作放在一起。&nbsp;<span class=Apple-converted-space>&nbsp;<br></span><br>template&nbsp;&lt;typename&nbsp;T&gt;;<br><br>class&nbsp;LockingPtr&nbsp;<br>{<br><br>public:<br><br>&nbsp;&nbsp;&nbsp;//&nbsp;Constructors/destructors<br><br>&nbsp;&nbsp;&nbsp;LockingPtr(volatile&nbsp;T&amp;&nbsp;obj,&nbsp;Mutex&amp;&nbsp;mtx)<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;pObj_(const_cast&lt;T*&gt;;(&amp;obj)),<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pMtx_(&amp;mtx)<br><br>&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;mtx.Lock();&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;~LockingPtr()<br><br>&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;pMtx_-&gt;;Unlock();&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;//&nbsp;Pointer&nbsp;behavior<br><br>&nbsp;&nbsp;&nbsp;T&amp;&nbsp;operator*()<br><br>&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;*pObj_;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;T*&nbsp;operator-&gt;;()<br><br>&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;return&nbsp;pObj_;&nbsp;&nbsp;&nbsp;}<br><br>private:<br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">&nbsp;&nbsp;&nbsp;T*&nbsp;pObj_;<br><br>&nbsp;&nbsp;&nbsp;Mutex*&nbsp;pMtx_;<br><br>&nbsp;&nbsp;&nbsp;LockingPtr(const&nbsp;LockingPtr&amp;);<br><br>&nbsp;&nbsp;&nbsp;LockingPtr&amp;&nbsp;operator=(const&nbsp;LockingPtr&amp;);<br><br>};</p>
<p><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">尽管很简单，LockingPtr对于编写正确的多线程代码非常有用。你应该把线程间共享的对象声明为volatile，但是永远不要对它们使用<br>const_cast——你应该始终是用LockingPtr的自动对象（automatic&nbsp;objects）。让我们举例来说明。<br><br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">比如说你有两个线程需要共享一个vector&lt;char&gt;;对象：&nbsp;<br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px"></p>
<pre>class&nbsp;SyncBuf&nbsp;{
<br>public:
<br>&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;Thread1();
<br>&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;Thread2();
<br>private:
<br>&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;vector&lt;char&gt;;&nbsp;BufT;
<br>&nbsp;&nbsp;&nbsp;&nbsp;volatile&nbsp;BufT&nbsp;buffer_;
<br>&nbsp;&nbsp;&nbsp;&nbsp;Mutex&nbsp;mtx_;&nbsp;//&nbsp;controls&nbsp;access&nbsp;to&nbsp;buffer_
<br>};</pre>
<p></span></span><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">在一个线程的函数里，你只需要简单地使用一个LockingPtr&lt;BufT&gt;;对象来获取对buffer_成员变量的受控访问：<br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px"></p>
<pre>void&nbsp;SyncBuf::Thread1()&nbsp;{
<br>&nbsp;&nbsp;&nbsp;&nbsp;LockingPtr&lt;BufT&gt;;&nbsp;lpBuf(buffer_,&nbsp;mtx_);
<br>&nbsp;&nbsp;&nbsp;&nbsp;BufT::iterator&nbsp;i&nbsp;=&nbsp;lpBuf-&gt;;begin();
<br>&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(;&nbsp;i&nbsp;!=&nbsp;lpBuf-&gt;;end();&nbsp;++i)&nbsp;{
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...&nbsp;use&nbsp;*i&nbsp;...
<br>&nbsp;&nbsp;&nbsp;&nbsp;}
<br>}</pre>
<p></span></span><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">这样的代码很容易编写，也很容易理解——每当你需要使用buffer_时，你必须创建一个LockingPtr&lt;BufT&gt;;来指向它。当你这样做了以后，你就<br>可以访问vector的全部接口。<br><br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">这个方法的好处是，如果你犯了错误，编译器会指出它：&nbsp;<span class=Apple-converted-space>&nbsp;</span><br></p>
<pre><br>void&nbsp;SyncBuf::Thread2()&nbsp;{
<br>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Error!&nbsp;Cannot&nbsp;access&nbsp;'begin'&nbsp;for&nbsp;a&nbsp;volatile&nbsp;object
<br>&nbsp;&nbsp;&nbsp;&nbsp;BufT::iterator&nbsp;i&nbsp;=&nbsp;buffer_.begin();
<br>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Error!&nbsp;Cannot&nbsp;access&nbsp;'end'&nbsp;for&nbsp;a&nbsp;volatile&nbsp;object
<br>&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(;&nbsp;i&nbsp;!=&nbsp;lpBuf-&gt;;end();&nbsp;++i)&nbsp;{
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...&nbsp;use&nbsp;*i&nbsp;...
<br>&nbsp;&nbsp;&nbsp;&nbsp;}
<br>}</pre>
<p></span></span><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">你不能访问buffer_的任何函数，除非你进行了const_cast或者用LockingPtr。这两者的区别是LockingPtr提供了一个有规则的方法来对一个volatile变量进行const_cast。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br>LockingPtr有非常好的表达力。如果你只需要调用一个函数，你可以创建一个无名的临时LockingPtr对象，然后直接使用它：&nbsp;<span class=Apple-converted-space>&nbsp;<br></span><br>unsigned&nbsp;int&nbsp;SyncBuf::Size()&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;LockingPtr&lt;BufT&gt;;(buffer_,&nbsp;mtx_)-&gt;;size();<br><br>}<br></p>
<p></span></span><br class=Apple-interchange-newline></span></span></span></span></span></span></span></span></span></span></span></span></span></span><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">回到原生类型<span class=Apple-converted-space>&nbsp;</span><br>我们已经看到了volatile对于保护对象免于不受控的访问是多么出色，并且看到了LockingPtr是怎么提供了一个简单有效的办法来编写线程安全的代码。现在让我们回到原生类型，volatile对它们的作用方式是不同的。&nbsp;<br><br>让我们来考虑一个多个线程共享一个int变量的例子。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br></p>
<pre><br>class&nbsp;Counter
<br>{
<br>public:
<br>&nbsp;&nbsp;&nbsp;&nbsp;...
<br>&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;Increment()&nbsp;{&nbsp;++ctr_;&nbsp;}
<br>&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;Decrement()&nbsp;{&nbsp;--ctr_;&nbsp;}
<br>private:
<br>&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;ctr_;
<br>};
</pre>
<p></span></span><br class=Apple-interchange-newline><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">如果Increment和Decrement是在不同的线程里被调用的，上面的代码片断里就有bug。首先，ctr_必须是volatile的。其次，即使是一个看上去是原子的操作，比如++ctr_，实际上也分为三个阶段。内存本身是没有运算功能的，当对一个变量进行增量操作时，处理器会：&nbsp;<span class=Apple-converted-space>&nbsp;</span><br>把变量读入寄存器&nbsp;<span class=Apple-converted-space>&nbsp;</span><br>对寄存器里的值加1&nbsp;<span class=Apple-converted-space>&nbsp;</span><br>把结果写回内存&nbsp;<span class=Apple-converted-space>&nbsp;</span><br>这个三步操作称为RMW（Read-Modify-Write）。在一个RMW操作的Modify阶段，大多数处理器都会释放内存总线，以使其他处理器能够访问内存。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br>如果在这个时候另一个处理器对同一个变量也进行RMW操作，我们就遇到了一个竞争条件：第二次写入会覆盖掉第一次的值。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>为了防止这样的事发生，你又要用到LockingPtr：&nbsp;<span class=Apple-converted-space>&nbsp;</span><br></span></span><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px"></p>
<pre>class&nbsp;Counter
<br>{
<br>public:
<br>&nbsp;&nbsp;&nbsp;&nbsp;...
<br>&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;Increment()&nbsp;{&nbsp;++*LockingPtr&lt;int&gt;;(ctr_,&nbsp;mtx_);&nbsp;}
<br>&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;Decrement()&nbsp;{&nbsp;--*LockingPtr&lt;int&gt;;(ctr_,&nbsp;mtx_);&nbsp;}
<br>private:
<br>&nbsp;&nbsp;&nbsp;&nbsp;volatile&nbsp;int&nbsp;ctr_;
<br>&nbsp;&nbsp;&nbsp;&nbsp;Mutex&nbsp;mtx_;
<br>};</pre>
<p></span></span><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">现在这段代码正确了，但是和SyncBuf相比，这段代码的质量要差一些。为什么？因为对于Counter，编译器不会在你错误地直接访问ctr_（没有对它加锁）时产生警告。虽然ctr_是volatile的，但是编译器还是可以编译++ctr_，尽管产生的代码绝对是不正确的。编译器不再是你的盟友了，你只有自己留意竞争条件。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br>那么你该怎么做呢？很简单，你可以用一个高层的结构来包装原生类型的数据，然后对那个结构使用volatile。这有点自相矛盾，直接用volatile修饰原生类型是一个不好的用法，尽管这是volatile最初期望的用法！<br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">到现在为止，我们讨论了具有volatile数据成员的类；现在让我们来考虑设计这样的类，它会作为更大的对象的一部分并且在线程间共享。这里，volatile的成员函数可以帮很大的忙。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>在设计类的时候，你只对那些线程安全的成员函数加volatile修饰。你必须假定外面的代码会在任何地方任何时间调用volatile成员函数。不要忘记：volatile相当于自由的多线程代码，并且没有临界区；非volatile相当于单线程的环境或者在临界区内。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>比如说，你定义了一个Widget类，它用两个方法实现了同一个操作——一个线程安全的方法和一个快速的不受保护的方法。<br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px"></p>
<pre>class&nbsp;Widget
<br>{
<br>public:
<br>&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;Operation()&nbsp;volatile;
<br>&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;Operation();
<br>&nbsp;&nbsp;&nbsp;&nbsp;...
<br>private:
<br>&nbsp;&nbsp;&nbsp;&nbsp;Mutex&nbsp;mtx_;
<br>};
</pre>
<p></span></span><br class=Apple-interchange-newline></span></span></span></span><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">注意这里的重载（overloading）用法。现在Widget的用户可以用一致的语法调用Operation，对于volatile对象可以得到线程安全性，对于普通对象可以得到速度。用户必须注意把共享的Widget对象定义为volatile。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br>在实现volatile成员函数时，第一个操作通常是用LockingPtr对this进行加锁，然后其余工作可以交给非volatile的同名函数做：&nbsp;<br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px"></p>
<pre>void&nbsp;Widget::Operation()&nbsp;volatile
<br>{
<br>&nbsp;&nbsp;&nbsp;&nbsp;LockingPtr&lt;Widget&gt;;&nbsp;lpThis(*this,&nbsp;mtx_);
<br>&nbsp;&nbsp;&nbsp;&nbsp;lpThis-&gt;;Operation();&nbsp;//&nbsp;invokes&nbsp;the&nbsp;non-volatile&nbsp;function
<br>}</pre>
<p></span></span><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">小结<span class=Apple-converted-space>&nbsp;</span><br>在编写对线程程序的时候，使用volatile将对你十分有益。你必须坚持下面的规则：&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>把所有共享对象声明为volatile&nbsp;<span class=Apple-converted-space>&nbsp;</span><br>不要对原生类型直接使用volatile&nbsp;<span class=Apple-converted-space>&nbsp;</span><br>定义共享类时，用volatile成员函数来表示它的线程安全性。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br>如果你这么做了，而且用了简单的通用组件LockingPtr，你就可以写出线程安全的代码，并且大大减少对竞争条件的担心，因为编译器会替你操心，并且勤勤恳恳地为你指出哪里错了。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>在我参与的几个项目中，使用volatile和LockingPtr产生了很大效果。代码十分整洁，也容易理解。我记得遇到过一些死锁的情况，但是相对于竞争条件，我宁愿对付死锁的情况，因为它们调试起来容易多了。那些项目实际上根本没有碰到过有关竞争条件的问题。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>致谢<span class=Apple-converted-space>&nbsp;</span><br>非常感谢James&nbsp;Kanze和Sorin&nbsp;Jianu提供了很有洞察力的意见。<br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">致谢<span class=Apple-converted-space>&nbsp;</span><br>非常感谢James&nbsp;Kanze和Sorin&nbsp;Jianu提供了很有洞察力的意见。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>附：滥用volatile的本质？[2]<span class=Apple-converted-space>&nbsp;</span><br>在上一期的专栏《Generic&lt;Programming&gt;;:&nbsp;volatile&nbsp;—&nbsp;Multithreaded&nbsp;Programmer's&nbsp;Best&nbsp;Friend》发表以后，我收到很多反馈意见。就像是注定的一样，大部分称赞都是私人信件，而抱怨都发到USENET新闻组comp.lang.c++.moderated和&nbsp;comp.programming.threads里去了。随后引起了很长很激烈的讨论，如果你对这个主题有兴趣，你可以去看看这个讨论，它的标题是&#8220;volatile,&nbsp;was:&nbsp;memory&nbsp;visibility&nbsp;between&nbsp;threads.&#8221;。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>我知道我从这个讨论中学到了很多东西。比如说，文章开头的Widget的例子不太切题。长话短说，在很多系统（比如POSIX兼容的系统）中，volatile修饰是不需要的，而在另一些系统中，即使加了volatile也没有用，程序还是不正确。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>关于volatile&nbsp;correctness，最重要的一个问题是它依赖于类似POSIX的mutex，如果在多处理器系统上，光靠mutex就不够了——你必须用memory&nbsp;barriers。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>另一个更哲理性的问题是：严格来说通过类型转换把变量的volatile属性去掉是不合法的，即使volatile属性是你自己为了volatile&nbsp;correctness而加上去的。正如Anthony&nbsp;Williams指出的，可以想象一个系统可能把volatile数据放在一个不同于非volatile数据的存储区中，在这种情况下，进行地址变换会有不确定的行为。&nbsp;<br><br>另一个批评是volatile&nbsp;correctness虽然可以在一个较低层次上解决竞争条件，但是不能正确的检测出高层的、逻辑的竞争条件。例如，你有一个mt_vector模版类，用来模拟std::vector，成员函数经过正确的线程同步修正。考虑这段代码：&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>volatile&nbsp;mt_vector&lt;int&gt;;&nbsp;vec;<span class=Apple-converted-space>&nbsp;</span><br>&#8230;<span class=Apple-converted-space>&nbsp;</span><br>if&nbsp;(!vec.empty())&nbsp;{<span class=Apple-converted-space>&nbsp;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;vec.pop_back();<span class=Apple-converted-space>&nbsp;</span><br>}<span class=Apple-converted-space>&nbsp;</span><br><br>这段代码的目的是删除vector里的最后一个元素，如果它存在的话。在单线程环境里，他工作地很好。然而如果你把它用在多线程程序里，这段代码还是有可能抛出异常，尽管empty和pop_back都有正确的线程同步行为。虽然底层的数据（vec）的一致性有保证，但是高层操作的结果还是不确定的。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br>无论如何，经过辩论之后，我还是保持我的建议，在有类POSIX的mutex的系统上，volatile&nbsp;correctness还是检测竞争条件的一个有价值的工具。但是如果你在一个支持内存访问重新排序的多处理器系统上，你首先需要仔细阅读你的编译器的文档。你必须知己知彼。<br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">最后，Kenneth&nbsp;Chiu提到了一篇非常有趣的文章http://theory.stanford.edu/~freunds/race.ps，猜猜题目是什么？&#8220;Type-Based&nbsp;Race&nbsp;Detection&nbsp;for&nbsp;Java&#8221;。这篇文章讲了怎么对Java的类型系统作一点小小的补充，从而让编译器和程序员一起在编译时检测竞争条件。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>Eppur&nbsp;si&nbsp;muove.[3]&nbsp;<br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px">译注<span class=Apple-converted-space>&nbsp;</span><br>[1]&nbsp;Volatile原意为易变的，反复无常的。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>[2]&nbsp;这是Andrei在他的下一篇Generic&lt;Programming&gt;;里对本文作的补充说明。&nbsp;<span class=Apple-converted-space>&nbsp;</span><br><br>[3]&nbsp;这是伽利略在被迫放弃他一直信仰的哥白尼的地动说时所说过的一句辩解的话，意思是&#8220;它（地球）毕竟仍在运动&#8221;。</span></span></span></span></span></span></span></span></span></span><br class=Apple-interchange-newline></span></span></span></span></p>
<img src ="http://www.cppblog.com/mmdengwo/aggbug/143455.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-05 17:41 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/05/143455.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>