﻿<?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>IT博客-Martin Ding's Garden Plot-随笔分类-C &amp; C++</title><link>http://www.cnitblog.com/martin/category/2433.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 27 Sep 2011 22:39:45 GMT</lastBuildDate><pubDate>Tue, 27 Sep 2011 22:39:45 GMT</pubDate><ttl>60</ttl><item><title>[转载]boost tokenizer</title><link>http://www.cnitblog.com/martin/archive/2006/08/25/15925.html</link><dc:creator>Martin</dc:creator><author>Martin</author><pubDate>Fri, 25 Aug 2006 03:11:00 GMT</pubDate><guid>http://www.cnitblog.com/martin/archive/2006/08/25/15925.html</guid><wfw:comment>http://www.cnitblog.com/martin/comments/15925.html</wfw:comment><comments>http://www.cnitblog.com/martin/archive/2006/08/25/15925.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/martin/comments/commentRss/15925.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/martin/services/trackbacks/15925.html</trackback:ping><description><![CDATA[
		<div class="posttitle">(原载：<a href="http://www.cppblog.com/zuroc/">http://www.cppblog.com/zuroc/</a>)</div>
		<p>tokenizer - Break of a string or other character sequence into a series of tokens, from John Bandela<br />tokenizer - 分解字串,提取内容.作者: John Bandela</p>
		<p>例一:<br />// simple_example_1.cpp<br />#include&lt;iostream&gt;<br />#include&lt;boost/tokenizer.hpp&gt;<br />#include&lt;string&gt;</p>
		<p>int main(){<br />   using namespace std;<br />   using namespace boost;<br />   string s = "This is,  a test";<br />   tokenizer&lt;&gt; tok(s);<br />   for(tokenizer&lt;&gt;::iterator beg=tok.begin(); beg!=tok.end();++beg){<br />       cout &lt;&lt; *beg &lt;&lt; "\n";<br />   }<br />}</p>
		<p>输出<br />This<br />is<br />a<br />test</p>
		<p>tokenizer默认将单词以空格和标点为边界分开.</p>
		<p>例二:<br />#include&lt;iostream&gt;<br />#include&lt;boost/tokenizer.hpp&gt;<br />#include&lt;string&gt;</p>
		<p>int main(){<br />   using namespace std;<br />   using namespace boost;<br />   string s = "Field 1,\"putting quotes around fields, allows commas\",Field 3";<br />   tokenizer&lt;escaped_list_separator&lt;char&gt; &gt; tok(s);<br />   for(tokenizer&lt;escaped_list_separator&lt;char&gt; &gt;::iterator beg=tok.begin(); beg!=tok.end();++beg){<br />       cout &lt;&lt; *beg &lt;&lt; "\n";<br />   }<br />}<br />输出<br />Field 1<br />putting quotes around fields, allows commas<br />Field 3</p>
		<p>双引号之间可以有标点.</p>
		<p>
				<br />例三:<br />// simple_example_3.cpp<br />#include&lt;iostream&gt;<br />#include&lt;boost/tokenizer.hpp&gt;<br />#include&lt;string&gt;</p>
		<p>int main(){<br />   using namespace std;<br />   using namespace boost;<br />   string s = "12252001";<br />   int offsets[] = {2,2,4};<br />   offset_separator f(offsets, offsets+3);<br />   tokenizer&lt;offset_separator&gt; tok(s,f);<br />   for(tokenizer&lt;offset_separator&gt;::iterator beg=tok.begin(); beg!=tok.end();++beg){<br />       cout &lt;&lt; *beg &lt;&lt; "\n";<br />   }<br />}</p>
		<p>把12252001分解为<br />12<br />25<br />2001</p>
		<p>例4:<br />// char_sep_example_1.cpp<br />#include &lt;iostream&gt;<br />#include &lt;boost/tokenizer.hpp&gt;<br />#include &lt;string&gt;</p>
		<p>int main()<br />{<br />  std::string str = ";!!;Hello|world||-foo--bar;yow;baz|";<br />  typedef boost::tokenizer&lt;boost::char_separator&lt;char&gt; &gt;<br />    tokenizer;<br />  boost::char_separator&lt;char&gt; sep("-;|");<br />  tokenizer tokens(str, sep);<br />  for (tokenizer::iterator tok_iter = tokens.begin();<br />       tok_iter != tokens.end(); ++tok_iter)<br />    std::cout &lt;&lt; "&lt;" &lt;&lt; *tok_iter &lt;&lt; "&gt; ";<br />  std::cout &lt;&lt; "\n";<br />  return EXIT_SUCCESS;<br />}</p>
		<p>输出<br />&lt;!!&gt; &lt;Hello&gt; &lt;world&gt; &lt;foo&gt; &lt;bar&gt; &lt;yow&gt; &lt;baz&gt;<br />自定义分隔的标点</p>
		<p>例5:<br />    // char_sep_example_2.cpp<br />    #include &lt;iostream&gt;<br />    #include &lt;boost/tokenizer.hpp&gt;<br />    #include &lt;string&gt;</p>
		<p>    int main()<br />    {<br />        std::string str = ";;Hello|world||-foo--bar;yow;baz|";<br />        typedef boost::tokenizer&lt;boost::char_separator&lt;char&gt; &gt;<br />            tokenizer;<br />        boost::char_separator&lt;char&gt; sep("-;", "|", boost::keep_empty_tokens);<br />        tokenizer tokens(str, sep);<br />        for (tokenizer::iterator tok_iter = tokens.begin();<br />             tok_iter != tokens.end(); ++tok_iter)<br />          std::cout &lt;&lt; "&lt;" &lt;&lt; *tok_iter &lt;&lt; "&gt; ";<br />        std::cout &lt;&lt; "\n";<br />        return EXIT_SUCCESS;<br />    }</p>
		<p>The output is:</p>
		<p>   
&lt;&gt; &lt;&gt; &lt;Hello&gt; &lt;|&gt; &lt;world&gt; &lt;|&gt;
&lt;&gt; &lt;|&gt; &lt;&gt; &lt;foo&gt; &lt;&gt; &lt;bar&gt;
&lt;yow&gt; &lt;baz&gt; &lt;|&gt; &lt;&gt;<br />去除-; , 保留|但将它看作是分隔符,当两个分隔符相邻的时候会自动加空格</p>
		<p>例6:<br />    // char_sep_example_3.cpp<br />    #include &lt;iostream&gt;<br />    #include &lt;boost/tokenizer.hpp&gt;<br />    #include &lt;string&gt;</p>
		<p>    int main()<br />    {<br />       std::string str = "This is,  a test";<br />       typedef boost::tokenizer&lt;boost::char_separator&lt;char&gt; &gt; Tok;<br />       boost::char_separator&lt;char&gt; sep; // default constructed<br />       Tok tok(str, sep);<br />       for(Tok::iterator tok_iter = tok.begin(); tok_iter != tok.end(); ++tok_iter)<br />         std::cout &lt;&lt; "&lt;" &lt;&lt; *tok_iter &lt;&lt; "&gt; ";<br />       std::cout &lt;&lt; "\n";<br />       return EXIT_SUCCESS;<br />    }</p>
		<p>The output is:</p>
		<p>    &lt;This&gt; &lt;is&gt; &lt;,&gt; &lt;a&gt; &lt;test&gt;<br />保留标点但将它看作分隔符</p>
<img src ="http://www.cnitblog.com/martin/aggbug/15925.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/martin/" target="_blank">Martin</a> 2006-08-25 11:11 <a href="http://www.cnitblog.com/martin/archive/2006/08/25/15925.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]在C++中使用cpuid指令获得CPU信息</title><link>http://www.cnitblog.com/martin/archive/2006/05/19/10816.html</link><dc:creator>Martin</dc:creator><author>Martin</author><pubDate>Fri, 19 May 2006 08:32:00 GMT</pubDate><guid>http://www.cnitblog.com/martin/archive/2006/05/19/10816.html</guid><wfw:comment>http://www.cnitblog.com/martin/comments/10816.html</wfw:comment><comments>http://www.cnitblog.com/martin/archive/2006/05/19/10816.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/martin/comments/commentRss/10816.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/martin/services/trackbacks/10816.html</trackback:ping><description><![CDATA[
		<p>From: http://freeman.cnblogs.com/archive/2005/08/30/226128.html<br /></p>
		<p>
				<br />
		</p>
		<p>1、什么是cpuid指令</p>
		<p>    CPUID指令是intel IA32架构下获得CPU信息的汇编指令，可以得到CPU类型，型号，制造商信息，商标信息，序列号，缓存等一系列CPU相关的东西。</p>
		<p>2、cpuid指令的使用</p>
		<p>    cpuid使用eax作为输入参数，eax，ebx，ecx，edx作为输出参数，举个例子:</p>
		<p> __asm<br /> {<br />  mov eax, 1<br />  cpuid<br />  ...<br /> }</p>
		<p>    以上代码以1为输入参数，执行cpuid后，所有寄存器的值都被返回值填充。针对不同的输入参数eax的值，输出参数的意义都不相同。<br />   
为了更好的在C++中使用cpuid指令，可以使用类对指令进行封装，在类中定义一个专门的函数负责cpuid的执行，他需要一个输入参数。还需要定义四
个成员变量存储cpuid执行以后返回来的值。由于这四个寄存器都是32位长的，可以使用unsinged long 类型变量存储。</p>
		<p> typedef unsigned long DWORD</p>
		<p> class CPUID<br /> {<br /> public:<br />  ...<br /> private:<br />  void Executecpuid(DWORD eax); // 用来实现cpuid</p>
		<p>  DWORD m_eax;   // 存储返回的eax<br />  DWORD m_ebx;   // 存储返回的ebx<br />  DWORD m_ecx;   // 存储返回的ecx<br />  DWORD m_edx;   // 存储返回的edx</p>
		<p>  ...<br /> }</p>
		<p> void CPUID::Executecpuid(DWORD veax)<br /> {<br />  // 因为嵌入式的汇编代码不能识别 类成员变量<br />  // 所以定义四个临时变量作为过渡<br />  DWORD deax;<br />  DWORD debx;<br />  DWORD decx;<br />  DWORD dedx;</p>
		<p>  __asm<br />  {<br />   mov eax, veax ;将输入参数移入eax<br />   cpuid  ;执行cpuid<br />   mov deax, eax ;以下四行代码把寄存器中的变量存入临时变量<br />   mov debx, ebx<br />   mov decx, ecx<br />   mov dedx, edx<br />  }</p>
		<p>  m_eax = deax; // 把临时变量中的内容放入类成员变量<br />  m_ebx = debx;<br />  m_ecx = decx;<br />  m_edx = dedx;<br /> }</p>
		<p>    这样就可以通过直接调用Executecupid()函数的方式来执行cpuid指令了，返回值存在类成员变量m_eax, m_ebx, m_ecx和m_edx中。</p>
		<p>3、获得CPU的制造商信息(Vender ID String)</p>
		<p>    把eax = 0作为输入参数，可以得到CPU的制造商信息。<br />    cpuid指令执行以后，会返回一个12字符的制造商信息，前四个字符的ASC码按低位到高位放在ebx，中间四个放在edx，最后四个字符放在ecx。比如说，对于intel的cpu，会返回一个“GenuineIntel”的字符串，返回值的存储格式为:</p>
		<p>           31      23      15      07      00<br />        EBX| u (75)| n (6E)| e (65)| G (47)<br />        EDX| I (49)| e (65)| n (6E)| i (69)<br />        ECX| l (6C)| e (65)| t (74)| n (6E)</p>
		<p>    因此可以这样实现他：</p>
		<p> string CPUID::GetVID()<br /> {<br />  char cVID[13];   // 字符串，用来存储制造商信息<br />  memset(cVID, 0, 13);  // 把数组清0<br />  Executecpuid(0);  // 执行cpuid指令，使用输入参数 eax = 0<br />  memcpy(cVID, &amp;m_ebx, 4); // 复制前四个字符到数组<br />  memcpy(cVID+4, &amp;m_edx, 4); // 复制中间四个字符到数组<br />  memcpy(cVID+8, &amp;m_ecx, 4); // 复制最后四个字符到数组</p>
		<p>  return string(cVID);  // 以string的形式返回<br /> }</p>
		<p>4、获得CPU商标信息(Brand String)</p>
		<p>   
在我的电脑上点击右键，选择属性，可以在窗口的下面看到一条CPU的信息，这就是CPU的商标字符串。CPU的商标字符串也是通过cpuid得到的。由于
商标的字符串很长(48个字符)，所以不能在一次cpuid指令执行时全部得到，所以intel把它分成了3个操作，eax的输入参数分别是
0x80000002,0x80000003,0x80000004，每次返回的16个字符，按照从低位到高位的顺序依次放在eax, ebx,
ecx, edx。因此，可以用循环的方式，每次执行完以后保存结果，然后执行下一次cpuid。</p>
		<p> string CPUID::GetBrand()<br /> {<br />  const DWORD BRANDID = 0x80000002;  // 从0x80000002开始，到0x80000004结束<br />  char cBrand[49];    // 用来存储商标字符串，48个字符<br />  memset(cBrand, 0, 49);    // 初始化为0</p>
		<p>  for (DWORD i = 0; i &lt; 3; i++)   // 依次执行3个指令<br />  {<br />   Executecpuid(BRANDID + i);   <br />   memcpy(cBrand + i*16, &amp;m_eax, 16); // 每次执行结束后，保存四个寄存器里的asc码到数组<br />  }      // 由于在内存中，m_eax, m_ebx, m_ecx, m_edx是连续排列<br />        // 所以可以直接以内存copy的方式进行保存<br />  return string(cBrand);  // 以string的形式返回<br /> }</p>
		<p>5、检测CPU特性(CPU feature)</p>
		<p>    我98年初买第一台电脑的时候，CPU能支持MMX就很了不起了。现在的intel
CPU，台式机的好点的都支持Hyper-Threading了，移动的要支持Speed
Sted。这些都是CPU的特性。CPU的特性可以通过cpuid获得，参数是eax =
1，返回值放在edx和ecx，通过验证edx或者ecx的某一个bit，可以获得CPU的一个特性是否被支持。比如说，edx的bit
32代表是否支持MMX，edx的bit 28代表是否支持Hyper-Threading，ecx的bit 7代表是否支持speed
sted。下面就是获得CPU特性的例子：</p>
		<p> bool CPUID::IsHyperThreading()  // 判断是否支持hyper-threading<br /> {<br />  Executecpuid(1);  // 执行cpuid指令，使用输入参数 eax = 1</p>
		<p>  return m_edx &amp; (1&lt;&lt;28);  // 返回edx的bit 28<br /> }</p>
		<p> bool CPUID::IsEST()   // 判断是否支持speed step<br /> {<br />  Executecpuid(1);  // 执行cpuid指令，使用输入参数 eax = 1</p>
		<p>  return m_ecx &amp; (1&lt;&lt;7);  // 返回ecx的bit 7<br /> }</p>
		<p> bool CPUID::IsMMX()   // 判断是否支持MMX<br /> {<br />  Executecpuid(1);  // 执行cpuid指令，使用输入参数 eax = 1</p>
		<p>  return m_edx &amp; (1&lt;&lt;23);  // 返回edx的bit 23<br /> }</p>
		<p>    CPU的特性还有很多，这只是平时我们听到比较多的三个，更多的特性请参考intel的资料。</p>
		<p>6、获得CPU的缓存(cache)<br />    <br />    缓存，就是CACHE，已经成为判断CPU性能的一项大指标。缓存信息包括：第几级缓存(level)，缓存大小(size)，通道数(way)，吞吐量(line size)。因此可以使用一个结构体来存储缓存信息。</p>
		<p> struct CacheInfo<br /> {<br />  int level;    // 第几级缓存<br />  int size;    // 缓存大小，单位KB<br />  int way;    // 通道数<br />  int linesize;    // 吞吐量</p>
		<p>  CacheInfo()    // 构造函数<br />  {<br />   level = 0;<br />   size = 0;<br />   way = 0;<br />   linesize = 0;<br />  }</p>
		<p>  CacheInfo(int clevel, int csize, int cway, int clinesize)  // 构造函数<br />  {<br />   level = clevel;<br />   size = csize;<br />   way = cway;<br />   linesize = clinesize;<br />  }<br /> };<br />    <br />   
缓存信息可以通过eax = 2的cpuid来得到（得到的不光有cache信息，还有其他的一些信息），返回值在eax(高24位), ebx,
ecx和edx，总共15个BYTE的信息，每个BYTE的值不同，代表的意义也不同，所以需要用一个哈希表存储各种不同BYTE的定义，可以定义一个
map类型的类成员存储这些资料。我把资料上和缓存有关的信息存储如下：</p>
		<p> m_cache[0x06] =  CacheInfo(1, 8, 4, 32);<br /> m_cache[0x08] =  CacheInfo(1, 16, 4, 32);<br /> m_cache[0x0a] =  CacheInfo(1, 8, 2, 32);<br /> m_cache[0x0c] =  CacheInfo(1, 16, 4, 32);<br /> m_cache[0x2c] =  CacheInfo(1, 32, 8, 64);<br /> m_cache[0x30] =  CacheInfo(1, 32, 8, 64);<br /> m_cache[0x60] =  CacheInfo(1, 16, 8, 64);<br /> m_cache[0x66] =  CacheInfo(1, 8, 4, 64);<br /> m_cache[0x67] =  CacheInfo(1, 16, 4, 64);<br /> m_cache[0x68] =  CacheInfo(1, 32, 4, 64);</p>
		<p> m_cache[0x39] =  CacheInfo(2, 128, 4, 64);<br /> m_cache[0x3b] =  CacheInfo(2, 128, 2, 64);<br /> m_cache[0x3c] =  CacheInfo(2, 256, 4, 64);<br /> m_cache[0x41] =  CacheInfo(2, 128, 4, 32);<br /> m_cache[0x42] =  CacheInfo(2, 256, 4, 32);<br /> m_cache[0x43] =  CacheInfo(2, 512, 4, 32);<br /> m_cache[0x44] =  CacheInfo(2, 1024, 4, 32);<br /> m_cache[0x45] =  CacheInfo(2, 2048, 4, 32);<br /> m_cache[0x79] =  CacheInfo(2, 128, 8, 64);<br /> m_cache[0x7a] =  CacheInfo(2, 256, 8, 64);<br /> m_cache[0x7b] =  CacheInfo(2, 512, 8, 64);<br /> m_cache[0x7c] =  CacheInfo(2, 1024, 8, 64);<br /> m_cache[0x82] =  CacheInfo(2, 256, 8, 32);<br /> m_cache[0x83] =  CacheInfo(2, 512, 8, 32);<br /> m_cache[0x84] =  CacheInfo(2, 1024, 8, 32);<br /> m_cache[0x85] =  CacheInfo(2, 2048, 8, 32);<br /> m_cache[0x86] =  CacheInfo(2, 512, 4, 64);<br /> m_cache[0x87] =  CacheInfo(2, 1024, 8, 64);</p>
		<p> m_cache[0x22] =  CacheInfo(3, 512, 4, 64);<br /> m_cache[0x23] =  CacheInfo(3, 1024, 8, 64);<br /> m_cache[0x25] =  CacheInfo(3, 2048, 8, 64);<br /> m_cache[0x29] =  CacheInfo(3, 4096, 8, 64);</p>
		<p>    m_cache是类成员，定义如下：</p>
		<p> map&lt;int, CacheInfo&gt; m_cache; // Cache information table</p>
		<p>    在得到返回值以后，只需要遍历每一个BYTE的值，找到在m_cache中存在的元素，就可以得到cache信息了。代码如下：</p>
		<p> typedef unsigned char BYTE;</p>
		<p> DWORD CPUID::GetCacheInfo(CacheInfo&amp; L1, CacheInfo&amp; L2, CacheInfo&amp; L3)<br /> {<br />  BYTE cValues[16];      // 存储返回的16个byte值<br />  DWORD result = 0;      // 记录发现的缓存数量<br />  Executecpuid(2);      // 执行cpuid，参数为eax = 2<br />  memcpy(cValues, &amp;m_eax, 16);     // 把m_eax, m_ebx, m_ecx和m_edx存储到cValue</p>
		<p>  for (int i = 1; i &lt; 16; i++)     // 开始遍历，注意eax的第一个byte没有意义，需要跳过<br />  {<br />   if (m_cache.find(cValues[i]) != m_cache.end())  // 从表中查找此信息是否代表缓存<br />   {<br />    switch (m_cache[cValues[i]].level)  // 对号入座，保存缓存信息<br />    {<br />    case 1:  // L1 cache<br />     L1 = m_cache[cValues[i]];<br />     break;<br />    case 2:  // L2 cache<br />     L2 = m_cache[cValues[i]];<br />     break;<br />    case 3:  // L3 cache<br />     L3 = m_cache[cValues[i]];<br />     break;<br />    default:<br />     break;<br />    }<br />    result++;<br />   }<br />  <br />  }</p>
		<p>  return result;<br /> }</p>
		<p>    <br />7、获得CPU的序列号</p>
		<p>    序列号无处不在！！CPU的序列号用一个96bit的串表示，格式是连续的6个WORD值：XXXX-XXXX-XXXX-XXX-XXXX-XXXX。WORD是16个bit长的数据，可以用unsigned short模拟：</p>
		<p> typedef unsigned short WORD;</p>
		<p>    获得序列号需要两个步骤，首先用eax = 1做参数，返回的eax中存储序列号的高两个WORD。用eax = 3做参数，返回ecx和edx按从低位到高位的顺序存储前4个WORD。实现如下：</p>
		<p> bool CPUID::GetSerialNumber(SerialNumber&amp; serial)<br /> {<br />  Executecpuid(1); // 执行cpuid，参数为 eax = 1<br />  bool isSupport = m_edx &amp; (1&lt;&lt;18); // edx是否为1代表CPU是否存在序列号<br />  if (false == isSupport) // 不支持，返回false<br />  {<br />   return false;<br />  }<br />  memcpy(&amp;serial.nibble[4], &amp;m_eax, 4); // eax为最高位的两个WORD</p>
		<p>  Executecpuid(3); // 执行cpuid，参数为 eax = 3<br />  memcpy(&amp;serial.nibble[0], &amp;m_ecx, 8); // ecx 和 edx为低位的4个WORD</p>
		<p>  return true;<br /> }</p>
		<p>8、后记</p>
		<p>   
CPUID还能获得很多信息，以上实现的都是最常见的。完整的代码和有关cpuid的资料我会用附件的形式附在文章结尾。昨天代码写完后拿给朋友看，朋友
骂我使用了太多的memcpy()函数进行赤裸裸的内存操作...其实我这么做的目的是提高程序的性能，减少代码量，但是可读性就降了下来，不喜欢这种风
格的朋友可以自己改一下。还有，因为CPUID类只是提供了很多的接口，没有存储数据的功能，所以类以Singleton的方式设计，使用方法可以参考我
代码中的test2.cpp文件。</p>
<img src ="http://www.cnitblog.com/martin/aggbug/10816.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/martin/" target="_blank">Martin</a> 2006-05-19 16:32 <a href="http://www.cnitblog.com/martin/archive/2006/05/19/10816.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]C++ sizeof 使用规则及陷阱分析</title><link>http://www.cnitblog.com/martin/archive/2006/05/19/10815.html</link><dc:creator>Martin</dc:creator><author>Martin</author><pubDate>Fri, 19 May 2006 08:28:00 GMT</pubDate><guid>http://www.cnitblog.com/martin/archive/2006/05/19/10815.html</guid><wfw:comment>http://www.cnitblog.com/martin/comments/10815.html</wfw:comment><comments>http://www.cnitblog.com/martin/archive/2006/05/19/10815.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cnitblog.com/martin/comments/commentRss/10815.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/martin/services/trackbacks/10815.html</trackback:ping><description><![CDATA[
		<div class="postbody">
				<p>From: http://freeman.cnblogs.com/articles/sizeof.html<br /></p>
				<p>
						<br />
				</p>
				<p>前言<br /><br />    50米的网站<a href="http://www.50mi.net/">http://www.50mi.net</a>开张了，所以50米邀请我给他写点什么。说实在的，作为一个资深的潜水员，我还真没动笔写过什么东西，所以绞尽脑汁也没想起来能写什么的。不过鉴于50米喜欢在小孩子面前臭屁的，就写一篇群里讨论很多的sizeof问题吧。<br /><br />1、什么是sizeof</p>
				<p>    首先看一下sizeof在msdn上的定义：</p>
				<p>    The sizeof keyword gives the amount of storage, in bytes,
associated with a variable or a type (including aggregate types). This
keyword returns a value of type size_t.</p>
				<p>   
看到return这个字眼，是不是想到了函数？错了，sizeof不是一个函数，你见过给一个函数传参数，而不加括号的吗？sizeof可以，所以
sizeof不是函数。网上有人说sizeof是一元操作符，但是我并不这么认为，因为sizeof更像一个特殊的宏，它是在编译阶段求值的。举个例子：<br /> <br /> cout&lt;&lt;sizeof(int)&lt;&lt;endl; // 32位机上int长度为4<br /> cout&lt;&lt;sizeof(1==2)&lt;&lt;endl; // == 操作符返回bool类型，相当于 cout&lt;&lt;sizeof(bool)&lt;&lt;endl;</p>
				<p>    在编译阶段已经被翻译为：</p>
				<p> cout&lt;&lt;4&lt;&lt;endl;<br /> cout&lt;&lt;1&lt;&lt;endl;</p>
				<p>    这里有个陷阱，看下面的程序：</p>
				<p> int a = 0;<br /> cout&lt;&lt;sizeof(a=3)&lt;&lt;endl;<br /> cout&lt;&lt;a&lt;&lt;endl;</p>
				<p>   
输出为什么是4，0而不是期望中的4，3？？？就在于sizeof在编译阶段处理的特性。由于sizeof不能被编译成机器码，所以sizeof作用范围
内，也就是()里面的内容也不能被编译，而是被替换成类型。=操作符返回左操作数的类型，所以a=3相当于int，而代码也被替换为：</p>
				<p> int a = 0;<br /> cout&lt;&lt;4&lt;&lt;endl;<br /> cout&lt;&lt;a&lt;&lt;endl;</p>
				<p>    所以，sizeof是不可能支持链式表达式的，这也是和一元操作符不一样的地方。</p>
				<p>    结论：不要把sizeof当成函数，也不要看作一元操作符，把他当成一个特殊的编译预处理。</p>
				<p>2、sizeof的用法</p>
				<p>    sizeof有两种用法：<br />  <br />    （1）sizeof(object)<br />    也就是对对象使用sizeof，也可以写成sizeof object 的形式。例如：</p>
				<p>    （2）sizeof(typename)<br />    也就是对类型使用sizeof，注意这种情况下写成sizeof typename是非法的。下面举几个例子说明一下：</p>
				<p> <br /> int i = 2;<br /> cout&lt;&lt;sizeof(i)&lt;&lt;endl; // sizeof(object)的用法，合理<br /> cout&lt;&lt;sizeof i&lt;&lt;endl; // sizeof object的用法，合理<br /> cout&lt;&lt;sizeof 2&lt;&lt;endl; // 2被解析成int类型的object, sizeof object的用法，合理<br /> cout&lt;&lt;sizeof(2)&lt;&lt;endl; // 2被解析成int类型的object, sizeof(object)的用法，合理<br /> cout&lt;&lt;sizeof(int)&lt;&lt;endl;// sizeof(typename)的用法，合理<br /> cout&lt;&lt;sizeof int&lt;&lt;endl; // 错误！对于操作符，一定要加()</p>
				<p>    可以看出，加()是永远正确的选择。</p>
				<p>    结论：不论sizeof要对谁取值，最好都加上()。</p>
				<p>
						<br />3、数据类型的sizeof</p>
				<p>（1）C++固有数据类型</p>
				<p>    32位C++中的基本数据类型，也就char,short int(short),int,long int(long),float,double, long double<br />大小分别是：1，2，4，4，4，8, 10。</p>
				<p>    考虑下面的代码：</p>
				<p> cout&lt;&lt;sizeof(unsigned int) == sizeof(int)&lt;&lt;endl; // 相等，输出 1</p>
				<p>    unsigned影响的只是最高位bit的意义，数据长度不会被改变的。</p>
				<p>    结论：unsigned不能影响sizeof的取值。</p>
				<p>（2）自定义数据类型</p>
				<p>    typedef可以用来定义C++自定义类型。考虑下面的问题：</p>
				<p> typedef short WORD;<br /> typedef long DWORD;<br /> cout&lt;&lt;(sizeof(short) == sizeof(WORD))&lt;&lt;endl; // 相等，输出1<br /> cout&lt;&lt;(sizeof(long) == sizeof(DWORD))&lt;&lt;endl; // 相等，输出1</p>
				<p>    结论：自定义类型的sizeof取值等同于它的类型原形。</p>
				<p>（3）函数类型</p>
				<p>    考虑下面的问题：</p>
				<p> int f1(){return 0;};<br /> double f2(){return 0.0;}<br /> void f3(){}</p>
				<p> cout&lt;&lt;sizeof(f1())&lt;&lt;endl; // f1()返回值为int，因此被认为是int<br /> cout&lt;&lt;sizeof(f2())&lt;&lt;endl; // f2()返回值为double，因此被认为是double<br /> cout&lt;&lt;sizeof(f3())&lt;&lt;endl; // 错误！无法对void类型使用sizeof<br /> cout&lt;&lt;sizeof(f1)&lt;&lt;endl;  // 错误！无法对函数指针使用sizeof   <br /> cout&lt;&lt;sizeof*f2&lt;&lt;endl;  // *f2，和f2()等价，因为可以看作object，所以括号不是必要的。被认为是double</p>
				<p>    结论：对函数使用sizeof，在编译阶段会被函数返回值的类型取代，</p>
				<p>4、指针问题</p>
				<p>    考虑下面问题：<br /> <br /> cout&lt;&lt;sizeof(string*)&lt;&lt;endl; // 4<br /> cout&lt;&lt;sizeof(int*)&lt;&lt;endl; // 4<br /> cout&lt;&lt;sizof(char****)&lt;&lt;endl; // 4</p>
				<p>    可以看到，不管是什么类型的指针，大小都是4的，因为指针就是32位的物理地址。</p>
				<p>    结论：只要是指针，大小就是4。（64位机上要变成8也不一定）。</p>
				<p>   
顺便唧唧歪歪几句，C++中的指针表示实际内存的地址。和C不一样的是，C++中取消了模式之分，也就是不再有small,middle,big,取而代
之的是统一的flat。flat模式采用32位实地址寻址，而不再是c中的 segment:offset模式。举个例子，假如有一个指向地址
f000:8888的指针，如果是C类型则是8888(16位,
只存储位移，省略段)，far类型的C指针是f0008888(32位，高位保留段地址，地位保留位移),C++类型的指针是f8888(32位，相当于
段地址*16 + 位移，但寻址范围要更大)。</p>
				<p>5、数组问题</p>
				<p>    考虑下面问题：</p>
				<p> char a[] = "abcdef";<br /> int b[20] = {3, 4};<br /> char c[2][3] = {"aa", "bb"};<br /> </p>
				<p> cout&lt;&lt;sizeof(a)&lt;&lt;endl; // 7<br /> cout&lt;&lt;sizeof(b)&lt;&lt;endl; // 20 <font color="#ff0000">(Wrong here, should be 20*4)</font><br /> cout&lt;&lt;sizeof(c)&lt;&lt;endl; // 6<br /> </p>
				<p>    数组a的大小在定义时未指定，编译时给它分配的空间是按照初始化的值确定的，也就是7。c是多维数组，占用的空间大小是各维数的乘积，也就是6。可以看出，数组的大小就是他在编译时被分配的空间，也就是各维数的乘积*数组元素的大小。</p>
				<p>    结论：数组的大小是各维数的乘积*数组元素的大小。</p>
				<p>    这里有一个陷阱：</p>
				<p> int *d = new int[10];</p>
				<p> cout&lt;&lt;sizeof(d)&lt;&lt;endl; // 4</p>
				<p>    d是我们常说的动态数组，但是他实质上还是一个指针，所以sizeof(d)的值是4。</p>
				<p>    再考虑下面的问题：</p>
				<p> double* (*a)[3][6];<br /> <br /> cout&lt;&lt;sizeof(a)&lt;&lt;endl;  // 4<br /> cout&lt;&lt;sizeof(*a)&lt;&lt;endl;  // 72<br /> cout&lt;&lt;sizeof(**a)&lt;&lt;endl; // 24<br /> cout&lt;&lt;sizeof(***a)&lt;&lt;endl; // 4<br /> cout&lt;&lt;sizeof(****a)&lt;&lt;endl; // 8</p>
				<p>    a是一个很奇怪的定义，他表示一个指向 double*[3][6]类型数组的指针。既然是指针，所以sizeof(a)就是4。</p>
				<p>   
既然a是执行double*[3][6]类型的指针，*a就表示一个double*[3][6]的多维数组类型，因此sizeof(*a)=
3*6*sizeof(double*)=72。同样的，**a表示一个double*[6]类型的数组，所以sizeof(**a)=6*sizeof
(double*)=24。***a就表示其中的一个元素，也就是double*了，所以sizeof(***a)=4。至于****a，就是一个
double了，所以sizeof(****a)=sizeof(double)=8。</p>
				<p>
						<br />6、向函数传递数组的问题。</p>
				<p>    考虑下面的问题：<br />#include &lt;iostream&gt;<br />using namespace std;</p>
				<p>int Sum(int i[])<br />{<br /> int sumofi = 0;<br /> for (int j = 0; j &lt; sizeof(i)/sizeof(int); j++) //实际上，sizeof(i) = 4<br /> {<br />  sumofi += i[j];<br /> }<br /> return sumofi;<br />}</p>
				<p>int main()<br />{<br /> int allAges[6] = {21, 22, 22, 19, 34, 12};<br /> cout&lt;&lt;Sum(allAges)&lt;&lt;endl;<br /> system("pause");<br /> return 0;<br />}</p>
				<p>    Sum的本意是用sizeof得到数组的大小，然后求和。但是实际上，传入自函数Sum的，只是一个int 类型的指针，所以sizeof(i)=4，而不是24，所以会产生错误的结果。解决这个问题的方法使是用指针或者引用。</p>
				<p>    使用指针的情况：<br />int Sum(int (*i)[6])<br />{<br /> int sumofi = 0;<br /> for (int j = 0; j &lt; sizeof(*i)/sizeof(int); j++) //sizeof(*i) = 24<br /> {<br />  sumofi += (*i)[j];<br /> }<br /> return sumofi;<br />}</p>
				<p>int main()<br />{<br /> int allAges[] = {21, 22, 22, 19, 34, 12};<br /> cout&lt;&lt;Sum(&amp;allAges)&lt;&lt;endl;<br /> system("pause");<br /> return 0;<br />}<br />   
在这个Sum里，i是一个指向i[6]类型的指针，注意，这里不能用int Sum(int
(*i)[])声明函数，而是必须指明要传入的数组的大小，不然sizeof(*i)无法计算。但是在这种情况下，再通过sizeof来计算数组大小已经
没有意义了，因为此时大小是指定为6的。<br />使用引用的情况和指针相似：</p>
				<p>int Sum(int (&amp;i)[6])<br />{<br /> int sumofi = 0;<br /> for (int j = 0; j &lt; sizeof(i)/sizeof(int); j++)<br /> {<br />  sumofi += i[j];<br /> }<br /> return sumofi;<br />}</p>
				<p>int main()<br />{<br /> int allAges[] = {21, 22, 22, 19, 34, 12};<br /> cout&lt;&lt;Sum(allAges)&lt;&lt;endl;<br /> system("pause");<br /> return 0;<br />}<br />    这种情况下sizeof的计算同样无意义，所以用数组做参数，而且需要遍历的时候，函数应该有一个参数来说明数组的大小，而数组的大小在数组定义的作用域内通过sizeof求值。因此上面的函数正确形式应该是：<br />#include &lt;iostream&gt;<br />using namespace std;</p>
				<p>int Sum(int *i, unsigned int n)<br />{<br /> int sumofi = 0;<br /> for (int j = 0; j &lt; n; j++)<br /> {<br />  sumofi += i[j];<br /> }<br /> return sumofi;<br />}</p>
				<p>int main()<br />{<br /> int allAges[] = {21, 22, 22, 19, 34, 12};<br /> cout&lt;&lt;Sum(i, sizeof(allAges)/sizeof(int))&lt;&lt;endl;<br /> system("pause");<br /> return 0;<br />}</p>
				<p>7、字符串的sizeof和strlen</p>
				<p>    考虑下面的问题：</p>
				<p> char a[] = "abcdef";<br /> char b[20] = "abcdef";<br /> string s = "abcdef";</p>
				<p> cout&lt;&lt;strlen(a)&lt;&lt;endl;  // 6，字符串长度<br /> cout&lt;&lt;sizeof(a)&lt;&lt;endl;  // 7，字符串容量<br /> cout&lt;&lt;strlen(b)&lt;&lt;endl;  // 6，字符串长度<br /> cout&lt;&lt;strlen(b)&lt;&lt;endl;  // 20，字符串容量<br /> cout&lt;&lt;sizeof(s)&lt;&lt;endl;  // 12, 这里不代表字符串的长度，而是string类的大小<br /> cout&lt;&lt;strlen(s)&lt;&lt;endl;  // 错误！s不是一个字符指针。</p>
				<p> a[1] = '\0';<br /> cout&lt;&lt;strlen(a)&lt;&lt;endl;  // 1<br /> cout&lt;&lt;sizeof(a)&lt;&lt;endl;  // 7，sizeof是恒定的</p>
				<p>
						<br />   
strlen是寻找从指定地址开始，到出现的第一个0之间的字符个数，他是在运行阶段执行的，而sizeof是得到数据的大小，在这里是得到字符串的容
量。所以对同一个对象而言，sizeof的值是恒定的。string是C++类型的字符串，他是一个类，所以sizeof(s)表示的并不是字符串的长
度，而是类string的大小。strlen(s)根本就是错误的，因为strlen的参数是一个字符指针，如果想用strlen得到s字符串的长度，应
该使用sizeof(s.c_str())，因为string的成员函数c_str()返回的是字符串的首地址。实际上，string类提供了自己的成员
函数来得到字符串的容量和长度，分别是Capacity()和Length()。string封装了常用了字符串操作，所以在C++开发过程中，最好使用
string代替C类型的字符串。</p>
				<p>
						<br />8、从union的sizeof问题看cpu的对界</p>
				<p>    考虑下面问题：（默认对齐方式）</p>
				<p> union u<br /> {<br />  double a;<br />  int b;<br /> };</p>
				<p> union u2<br /> {<br />  char a[13];<br />  int b;<br /> };</p>
				<p> union u3<br /> {<br />  char a[13];<br />  char b;<br /> };</p>
				<p> cout&lt;&lt;sizeof(u)&lt;&lt;endl;  // 8<br /> cout&lt;&lt;sizeof(u2)&lt;&lt;endl;  // 16<br /> cout&lt;&lt;sizeof(u3)&lt;&lt;endl;  // 13</p>
				<p>   
都知道union的大小取决于它所有的成员中，占用空间最大的一个成员的大小。所以对于u来说，大小就是最大的double类型成员a了，所以
sizeof(u)=sizeof(double)=8。但是对于u2和u3，最大的空间都是char[13]类型的数组，为什么u3的大小是13，而
u2是16呢？关键在于u2中的成员int
b。由于int类型成员的存在，使u2的对齐方式变成4，也就是说，u2的大小必须在4的对界上，所以占用的空间变成了16（最接近13的对界）。</p>
				<p>    结论：复合数据类型，如union，struct，class的对齐方式为成员中对齐方式最大的成员的对齐方式。</p>
				<p>   
顺便提一下CPU对界问题，32的C++采用8位对界来提高运行速度，所以编译器会尽量把数据放在它的对界上以提高内存命中率。对界是可以更改的，使用
#pragma
pack(x)宏可以改变编译器的对界方式，默认是8。C++固有类型的对界取编译器对界方式与自身大小中较小的一个。例如，指定编译器按2对界，int
类型的大小是4，则int的对界为2和4中较小的2。在默认的对界方式下，因为几乎所有的数据类型都不大于默认的对界方式8（除了long
double），所以所有的固有类型的对界方式可以认为就是类型自身的大小。更改一下上面的程序：</p>
				<p> #pragma pack(2)<br /> union u2<br /> {<br />  char a[13];<br />  int b;<br /> };</p>
				<p> union u3<br /> {<br />  char a[13];<br />  char b;<br /> };<br /> #pragma pack(8)</p>
				<p> cout&lt;&lt;sizeof(u2)&lt;&lt;endl;  // 14<br /> cout&lt;&lt;sizeof(u3)&lt;&lt;endl;  // 13</p>
				<p>    由于手动更改对界方式为2，所以int的对界也变成了2，u2的对界取成员中最大的对界，也是2了，所以此时sizeof(u2)=14。</p>
				<p>    结论：C++固有类型的对界取编译器对界方式与自身大小中较小的一个。</p>
				<p>
						<br />9、struct的sizeof问题</p>
				<p>    因为对齐问题使结构体的sizeof变得比较复杂，看下面的例子：(默认对齐方式下)</p>
				<p> struct s1<br /> {<br />  char a;<br />  double b;<br />  int c;<br />  char d; <br /> };</p>
				<p> struct s2<br /> {<br />  char a;<br />  char b;<br />  int c;<br />  double d;<br /> };</p>
				<p> cout&lt;&lt;sizeof(s1)&lt;&lt;endl; // 24<br /> cout&lt;&lt;sizeof(s2)&lt;&lt;endl; // 16</p>
				<p>   
同样是两个char类型，一个int类型，一个double类型，但是因为对界问题，导致他们的大小不同。计算结构体大小可以采用元素摆放法，我举例子说
明一下：首先，CPU判断结构体的对界，根据上一节的结论，s1和s2的对界都取最大的元素类型，也就是double类型的对界8。然后开始摆放每个元
素。<br />   
对于s1，首先把a放到8的对界，假定是0，此时下一个空闲的地址是1，但是下一个元素d是double类型，要放到8的对界上，离1最接近的地址是8
了，所以d被放在了8，此时下一个空闲地址变成了16，下一个元素c的对界是4，16可以满足，所以c放在了16，此时下一个空闲地址变成了20，下一个
元素d需要对界1，也正好落在对界上，所以d放在了20，结构体在地址21处结束。由于s1的大小需要是8的倍数，所以21-23的空间被保留，s1的大
小变成了24。<br />   
对于s2，首先把a放到8的对界，假定是0，此时下一个空闲地址是1，下一个元素的对界也是1，所以b摆放在1，下一个空闲地址变成了2；下一个元素c的
对界是4，所以取离2最近的地址4摆放c，下一个空闲地址变成了8，下一个元素d的对界是8，所以d摆放在8，所有元素摆放完毕，结构体在15处结束，占
用总空间为16，正好是8的倍数。</p>
				<p>    这里有个陷阱，对于结构体中的结构体成员，不要认为它的对齐方式就是他的大小，看下面的例子：</p>
				<p> struct s1<br /> {<br />  char a[8];<br /> };</p>
				<p> struct s2<br /> {<br />  double d;<br /> };</p>
				<p> struct s3<br /> {<br />  s1 s;<br />  char a;<br /> };</p>
				<p> struct s4<br /> {<br />  s2 s;<br />  char a; <br /> };</p>
				<p> cout&lt;&lt;sizeof(s1)&lt;&lt;endl; // 8<br /> cout&lt;&lt;sizeof(s2)&lt;&lt;endl; // 8<br /> cout&lt;&lt;sizeof(s3)&lt;&lt;endl; // 9<br /> cout&lt;&lt;sizeof(s4)&lt;&lt;endl; // 16;</p>
				<p>    s1和s2大小虽然都是8，但是s1的对齐方式是1，s2是8（double），所以在s3和s4中才有这样的差异。</p>
				<p>    所以，在自己定义结构体的时候，如果空间紧张的话，最好考虑对齐因素来排列结构体里的元素。</p>
				<p>10、不要让double干扰你的位域</p>
				<p>    在结构体和类中，可以使用位域来规定某个成员所能占用的空间，所以使用位域能在一定程度上节省结构体占用的空间。不过考虑下面的代码：</p>
				<p> struct s1<br /> {<br />  int i: 8;<br />  int j: 4;<br />  double b;<br />  int a:3;<br /> };</p>
				<p> struct s2<br /> {<br />  int i;<br />  int j;<br />  double b;<br />  int a;<br /> };</p>
				<p> struct s3<br /> {<br />  int i;<br />  int j;<br />  int a;<br />  double b;<br /> };</p>
				<p> struct s4<br /> {<br />  int i: 8;<br />  int j: 4;<br />  int a:3;<br />  double b;<br /> };</p>
				<p> cout&lt;&lt;sizeof(s1)&lt;&lt;endl;  // 24<br /> cout&lt;&lt;sizeof(s2)&lt;&lt;endl;  // 24<br /> cout&lt;&lt;sizeof(s3)&lt;&lt;endl;  // 24<br /> cout&lt;&lt;sizeof(s4)&lt;&lt;endl;  // 16</p>
				<p>    可以看到，有double存在会干涉到位域（sizeof的算法参考上一节），所以使用位域的的时候，最好把float类型和double类型放在程序的开始或者最后。</p>
				<p>    第一次写东西，发现自己的表达能力太差了，知道的东西讲不出来，讲出来的东西别人也看不懂，呵呵。另外，C99标准的sizeof已经可以工作在运行时了，打算最近找个支持C99的编译器研究一下。</p>
		</div>
<img src ="http://www.cnitblog.com/martin/aggbug/10815.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/martin/" target="_blank">Martin</a> 2006-05-19 16:28 <a href="http://www.cnitblog.com/martin/archive/2006/05/19/10815.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转][译著]在模板方法中的一些"反常"用法</title><link>http://www.cnitblog.com/martin/archive/2006/05/19/10790.html</link><dc:creator>Martin</dc:creator><author>Martin</author><pubDate>Fri, 19 May 2006 04:18:00 GMT</pubDate><guid>http://www.cnitblog.com/martin/archive/2006/05/19/10790.html</guid><wfw:comment>http://www.cnitblog.com/martin/comments/10790.html</wfw:comment><comments>http://www.cnitblog.com/martin/archive/2006/05/19/10790.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/martin/comments/commentRss/10790.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/martin/services/trackbacks/10790.html</trackback:ping><description><![CDATA[
		<font size="2">
				<font color="#800080" size="3">
						<strong>[译著]在模板方法中的一些"反常"用法</strong>
				</font>
				<br />
				<br />
				<font color="#ffa500">作者: Jerry Cat<br />时间: 2006/05/19<br />链接: </font>
				<a href="http://www.cppblog.com/jerysun0818/archive/2006/05/19/7393.html">
						<font color="#ffa500">http://www.cppblog.com/jerysun0818/archive/2006/05/19/7393.html</font>
				</a>
				<font size="2">
						<a href="http://www.cppblog.com/jerysun0818/archive/2006/05/16/7232.html">
						</a>
						<br />
				</font>
				<br />－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br />I. Virtually Yours -- Template Method模式 <br />我在研究Wendy写的一个类。那是她为这个项目写的一个抽象基类，而我的工作就是从中派生出一个具象类(concrete class)。这个类的public部分是这样的： </font>
		<p>
				<font size="2">class Mountie {<br />public:<br />    void read( std::istream &amp; );<br />    void write( std::ostream &amp; ) const;<br />    virtual ~Mountie();</font>
		</p>
		<p>
				<font size="2">很正常，virtual destructor表明这个类打算被继承。那么再看看其protected部分： </font>
		</p>
		<p>
				<font size="2">protected:<br />    virtual void do_read( std::istream &amp; );<br />    virtual void do_write( std::ostream &amp; ) const;</font>
		</p>
		<p>
				<font size="2">也
不过就是一会儿的功夫，我识破了Wendy的把戏：她在使用template
method模式。public成员函数read和write是非虚拟的，它们肯定是调用protected部分do_read/do_write虚拟成
员函数来完成实际的工作。啊，我简直为自己的进步而飘飘然了！哈，Wendy，这回你可难不住我，还有什么招数？尽管放马过来...
突然，笑容在我脸上凝固，因为我看到了其private部分： </font>
		</p>
		<p>
				<font size="2">private:<br />    virtual std::string classID() const = 0;</font>
		</p>
		<p>
				<font size="2">这是什么？一个private纯虚函数，能工作么？我站了起来， </font>
		</p>
		<p>
				<font size="2">“Wendy，你的Mountie类好像不能工作耶，它有一个private virtual function。” </font>
		</p>
		<p>
				<font size="2">“你试过了？”她连头都不抬。 </font>
		</p>
		<p>
				<font size="2">“嗯，那倒是没有啦，可是想想也不行啊？我的派生类怎么能override你的private函数呢？” 我嘟囔着。 </font>
		</p>
		<p>
				<font size="2">“嗬，你倒是很确定啊！”Wendy的声音很轻柔，“你怎么老是这也不行，那也不行的，这几个月跟着我你就没学到什么东西吗？小菜鸟。” </font>
		</p>
		<p>
				<font size="2">真是可恶啊... </font>
		</p>
		<p>
				<font size="2">“小菜鸟，你全都忘了，访问控制级别跟一个函数是不是虚拟的根本没关系。判断一个函数是动态绑定还是静态绑定是函数调用解析的最后一个步骤。好好读读标准的3.4和5.2.2节吧。” </font>
		</p>
		<p>
				<font size="2">我完全处于下风，只好采取干扰战术。“好吧，就算你说的不错，我也还是不明白，何必把它设为private？” </font>
		</p>
		<p>
				<font size="2">“我且问你，倘若你不想让一个类中的成员函数被其他的类调用，应当如何处理？” </font>
		</p>
		<p>
				<font size="2">“当然是把它设置为private的，” 我回答道。 </font>
		</p>
		<p>
				<font size="2">“那么你去看看我的Mountie类实现，特别是write()函数的实现。” </font>
		</p>
		<p>
				<font size="2">我正巴不得逃开Wendy那刺人的目光，便转过头去在我的屏幕上搜索，很快，我找到了： </font>
		</p>
		<p>
				<font size="2">void Mountie::write(std::ostream &amp;Dudley) const<br />{<br />    Dudley &lt;&lt; classID() &lt;&lt; std::endl;<br />    do_write(Dudley);<br />}</font>
		</p>
		<p>
				<font size="2">嗨，最近卡通片真是看得太多了，居然犯这样的低级失误。还是老是承认吧：“好了，我明白了。classID()是一个实现细节，用来在保存对象时指示具象类的类型，派生类必须覆盖它，所以必须是纯虚的。但是既然是实现细节，就应该设为private的。” </font>
		</p>
		<p>
				<font size="2">“这还差不多，小菜鸟。”大虾点了点头，“现在给我解释一下为什么do_read()和do_write()是protected的？” </font>
		</p>
		<p>
				<font size="2">这个问题并不难，我组织了一下就回答：“因为派生类对象需要调用这两个函数的实现来读写其中的基类对象。” </font>
		</p>
		<p>
				<font size="2">“很好很好，”大虾差不多满意了，“不过，你再解释解释为什么我不把它们设为public的？” </font>
		</p>
		<p>
				<font size="2">现在我感觉好多了：“因为调用它们的时候必须以一种特定的方式进行。比如do_write()函数，必须先把类型信息写入，再把对象信息写入，这样读取的时候，负责生成对象的模块首先能够知道要读出来的对象是什么类型的，然后才能正确地从流中读取对象信息。” </font>
		</p>
		<p>
				<font size="2">“聪明啊，我的小菜鸟！”Wendy停顿了一下，“就跟学习外国口语一样，学习C++也不光是掌握语法而已，还必须要掌握大量的惯用法。” </font>
		</p>
		<p>
				<font size="2">“是啊是啊，我正打算读Coplien的书...” </font>
		</p>
		<p>
				<font size="2">[译者注：就是James Coplien 1992年的经典著作Advanced C++ Programming Style and Idioms] </font>
		</p>
		<p>
				<font size="2">大
虾挥了挥她的手，“冷静，小菜鸟，我不是指先知Coplien的那本书，我是指某种结构背后隐含的惯用法。比如一个类有virtual
destructor，相当于告诉你说：‘嗨，我是一个多态基类，来继承我吧！’
而如果一个类的destructor不是虚拟的，则相当于是在说：‘我不能作为多态基类，看在老天的份上，别继承我。’” </font>
		</p>
		<p>
				<font size="2">“同
样的，virtual函数的访问控制级别也具有隐含的意义。一个protected virtual
function告诉你：‘你写的派生类应该，哦，可是说是必须调用我的实现。’而一个private virtual
function是在说：‘派生类可以覆盖，也可以不覆盖我，随你的便。但是你不可以调用我的实现。’” </font>
		</p>
		<p>
				<font size="2">我点点头，告诉她我懂了，然后追问道：“那么public virtual function呢？” </font>
		</p>
		<p>
				<font size="2">“尽可能不要使用public virtual function。”她拿起一支笔写下了以下代码： </font>
		</p>
		<p>
				<font size="2">class HardToExtend <br />{<br />public:<br />  virtual void f();<br />};<br /> void HardToExtend::f() <br />{ <br /> // Perform a specific action <br />}</font>
		</p>
		<p>
				<font size="2">“假设你发布了这个类。在写第二版时，需求有所变化，你必须改用Template Method。可是这根本不可能，你知道为什么？” </font>
		</p>
		<p>
				<font size="2">“呃，这个...，不知道。” </font>
		</p>
		<p>
				<font size="2">“由两种可能的办法。其一，将f()的实现代码转移到一个新的函数中，然后将f()本身设为non-virtual的： </font>
		</p>
		<p>
				<font size="2">class HardToExtend<br />{<br />// possibly protected<br />    virtual void do_f();<br />public:<br />    void f();<br />};<br />void HardToExtend::f()<br />{<br />    // pre-processing<br />    do_f();<br />    // post-processing<br />}<br />void HardToExtend::do_f()<br />{<br />    // Perform a specific action<br />}</font>
		</p>
		<p>
				<font size="2">然
而你原来写的派生类都是企图override函数f()而不是do_f()的，你必须改变所有的派生类实现，只要你错过了一个类，你的类层次就会染上先知
Meyers所说的‘精神分裂的行径’。” [译者注：参见Scott Meyers，Effective C++, Item
37，绝对不要重新定义继承而来的非虚拟函数] </font>
		</p>
		<p>
				<font size="2">“另一种办法是将f()移到private区域，引入一个新的non-virtual函数：” </font>
		</p>
		<p>
				<font size="2">class HardToExtend<br />{<br />// possibly protected<br />    virtual void f();<br />public:<br />    void call_f();<br />};</font>
		</p>
		<p>
				<font size="2">“这会导致无数令人头痛的问题。首先，所有的客户都企图调用f()而不是call_f()，现在它们的代码都不能编译了。更有甚者，大部分派生类都回把f()放在public区域中，这样直接使用派生类的用户可以访问到你本来想保护的细节。” </font>
		</p>
		<p>
				<font size="2">“对待虚函数要象对待数据成员一样，把它们设为private的，直到设计上要求使用更宽松的访问控制再来调整。要知道由private入public易，由public入private难啊！” </font>
		</p>
		<p>
				<font size="2">[译
者注：这篇文章所表达的思想具有一定的颠覆性，因为我们太容易在基类中设置public virtual
function了，Java中甚至专门为这种做法建立了interface机制，现在竟然说这不好！一时间真是接受不了。但是仔细体会作者的意思，他并
不是一般地反对public virtual function，只是在template
method大背景下给出上述原则。虽然这个原则在一般的设计中也是值得考虑的，但是主要的应用领域还是在template
method模式中。当然，template method是一种非常有用和常用的模式，因此也决定了本文提出的原则具有广泛的意义。] </font>
		</p>
<img src ="http://www.cnitblog.com/martin/aggbug/10790.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/martin/" target="_blank">Martin</a> 2006-05-19 12:18 <a href="http://www.cnitblog.com/martin/archive/2006/05/19/10790.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]C++ Style and Technique FAQ （中文版）</title><link>http://www.cnitblog.com/martin/archive/2006/04/28/9876.html</link><dc:creator>Martin</dc:creator><author>Martin</author><pubDate>Fri, 28 Apr 2006 10:39:00 GMT</pubDate><guid>http://www.cnitblog.com/martin/archive/2006/04/28/9876.html</guid><wfw:comment>http://www.cnitblog.com/martin/comments/9876.html</wfw:comment><comments>http://www.cnitblog.com/martin/archive/2006/04/28/9876.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/martin/comments/commentRss/9876.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/martin/services/trackbacks/9876.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: C++ Style and Technique FAQ （中文版）																		Bjarne Stroustrup 著， 紫云英 译																 						[注: 本访谈录之译文经Stroustrup博士授权。如要转载，请和我联系： 										zmelody@sohu.com				...&nbsp;&nbsp;<a href='http://www.cnitblog.com/martin/archive/2006/04/28/9876.html'>阅读全文</a><img src ="http://www.cnitblog.com/martin/aggbug/9876.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/martin/" target="_blank">Martin</a> 2006-04-28 18:39 <a href="http://www.cnitblog.com/martin/archive/2006/04/28/9876.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]字节长度，字节对齐以及类，对象的长度</title><link>http://www.cnitblog.com/martin/archive/2006/04/28/9873.html</link><dc:creator>Martin</dc:creator><author>Martin</author><pubDate>Fri, 28 Apr 2006 10:24:00 GMT</pubDate><guid>http://www.cnitblog.com/martin/archive/2006/04/28/9873.html</guid><wfw:comment>http://www.cnitblog.com/martin/comments/9873.html</wfw:comment><comments>http://www.cnitblog.com/martin/archive/2006/04/28/9873.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/martin/comments/commentRss/9873.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/martin/services/trackbacks/9873.html</trackback:ping><description><![CDATA[
		<p>From: http://www.cppblog.com/mzty/archive/2005/10/24/832.html<br /></p>
		<p>
				<br />
		</p>
		<p>#include  <iostream.h><br />main()<br />{<br /><br />    cout&lt;&lt;"sizeof('$')="&lt;<sizeof('$')>&lt;<endl;><br />    cout&lt;&lt;"sizeof(1)="&lt;<sizeof(1)>&lt;<endl;><br />    cout&lt;&lt;"sizeof(1.5)="&lt;<sizeof(1.5)>&lt;<endl;><br />    cout&lt;&lt;"sizeof(\"Good!\")="&lt;<sizeof("good!")>&lt;<endl;>&lt; P&gt; 
</endl;></sizeof("good!")></endl;></sizeof(1.5)></endl;></sizeof(1)></endl;></sizeof('$')></iostream.h></p>
		<p>  <br />    int i=100;<br />    char c='A';<br />    float x=3.1416; <br />    double p=0.1;<br />    cout&lt;&lt;"sizeof(i)="&lt;<sizeof(i)>&lt;<endl;><br />    cout&lt;&lt;"sizeof(c)="&lt;<sizeof(c)>&lt;<endl;><br />    cout&lt;&lt;"sizeof(x)="&lt;<sizeof(x)>&lt;<endl;><br />    cout&lt;&lt;"sizeof(p)="&lt;<sizeof(p)>&lt;<endl;>&lt; P&gt; 
</endl;></sizeof(p)></endl;></sizeof(x)></endl;></sizeof(c)></endl;></sizeof(i)></p>
		<p>  <br />    cout&lt;&lt;"sizeof(x+1.732)="&lt;<sizeof(x+1.732)>&lt;<endl;>&lt; P&gt; 
</endl;></sizeof(x+1.732)></p>
		<p>
				<br />    cout&lt;&lt;"sizeof(char)="&lt;<sizeof(char)>&lt;<endl;><br />    cout&lt;&lt;"sizeof(int)="&lt;<sizeof(int)>&lt;<endl;><br />    cout&lt;&lt;"sizeof(float)="&lt;<sizeof(float)>&lt;<endl;><br />    cout&lt;&lt;"sizeof(double)="&lt;<sizeof(double)>&lt;<endl;><br /><br /><br />    char str[]="This is a test.";<br />    int a[10]; <br />    double xy[10];<br />    cout&lt;&lt;"sizeof(str)="&lt;<sizeof(str)>&lt;<endl;><br />    cout&lt;&lt;"sizeof(a)="&lt;<sizeof(a)>&lt;<endl;><br />    cout&lt;&lt;"sizeof(xy)="&lt;<sizeof(xy)>&lt;<endl;>&lt; P&gt; 
</endl;></sizeof(xy)></endl;></sizeof(a)></endl;></sizeof(str)></endl;></sizeof(double)></endl;></sizeof(float)></endl;></sizeof(int)></endl;></sizeof(char)></p>
		<p>
				<br />    struct st {<br />        short num;<br />        float math_grade;<br />        float Chinese_grade;<br />        float sum_grade;<br />    };<br />    st student1;<br />    cout&lt;&lt;"sizeof(st)="&lt;<sizeof(st)>&lt;<endl;><br />    cout&lt;&lt;"sizeof(student1)="&lt;<sizeof(student1)>&lt;<endl;><br />}</endl;></sizeof(student1)></endl;></sizeof(st)></p>
		<p>----------------------------the result are:-------------------------------------<br />sizeof('$')=1<br />sizeof(1)=4<br />sizeof(1.5)=8<br />sizeof("Good!")=6<br />sizeof(i)=4<br />sizeof(c)=1<br />sizeof(x)=4<br />sizeof(p)=8<br />sizeof(x+1.732)=8<br />sizeof(char)=1<br />sizeof(int)=4<br />sizeof(float)=4<br />sizeof(double)=8<br />sizeof(str)=16<br />sizeof(a)=40<br />sizeof(xy)=80<br />sizeof(st)=16<br />sizeof(student1)=16<br />Press any key to continue<br />-------------------------------------------------------------------------<br />//  #pragma pack( )  <br />//  mulbayes  <br />//  unicode<br />-------------------------------------------------------------------------</p>
		<p>
				<br />
				<br />为了能使CPU对变量进行高效快速的访问，变量的起始地址应该具有某些特性，<br />即所谓的“对齐”。例如对于4字节的int类型变量，其起始地址应位于4字节边界上，<br />即起始地址能够被4整除。变量的对齐规则如下（32位系统）：</p>
		<p>
				<br />Type<br />Alignment</p>
		<p>char<br />在字节边界上对齐</p>
		<p>short (16-bit)<br />在双字节边界上对齐</p>
		<p>int and long (32-bit)<br />在4字节边界上对齐</p>
		<p>float<br />在4字节边界上对齐</p>
		<p>double<br />在8字节边界上对齐</p>
		<p> </p>
		<p>structures<br />单独考虑结构体的个成员，它们在不同的字节边界上对齐。<br />其中最大的字节边界数就是该结构的字节边界数。<br />MSDN原话：Largest alignment requirement of any member<br />理解结构体的对齐方式有点挠头，如果结构体中有结构体成员，<br />那么这是一个递归的过程。<br />对齐方式影响结构体成员在结构体中的偏移设编译器设定的最大对齐字节边界数为n，<br />对于结构体中的某一成员item，它相对于结构首地址的实际字节对齐数目X应该满足<br />以下规则：</p>
		<p>X = min（n， sizeof（item））</p>
		<p>例如，对于结构体 struct {char a; int b} T;</p>
		<p>当位于32位系统，n=8时：<br />a的偏移为0，<br />b的偏移为4，中间填充了3个字节, b的X为4；</p>
		<p>当位于32位系统，n=2时：<br />a的偏移为0，<br />b的偏移为2，中间填充了1个字节，b的X为2；</p>
		<p>结构体的sizeof<br />设结构体的最后一个成员为LastItem，其相对于结构体首地址的<br />偏移为offset（LastItem），其大小为sizeof(LastItem)，结构体的字节对齐数为N，<br />则：结构体的sizeof 为： 若offset（LastItem）＋ sizeof(LastItem)能够被N整除，<br />那么就是offset（LastItem）＋ sizeof(LastItem)，否则，在后面填充，<br />直到能够被N整除。</p>
		<p>例如：32位系统，n=8，<br />结构体 struct {char a; char b;} T;<br />struct {char a; int b；} T1;<br />struct {char a; int b； char c;} T2;<br />sizeof(T) == 2; N = 1 没有填充<br />sizeof(T) == 8; N = 4 中间填充了3字节<br />sizeof(T2)==12； N = 4 中间，结尾各填充了3字节</p>
		<p>注意：</p>
		<p>1） 对于空结构体，sizeof ＝＝ 1；因为必须保证结构体的每一个实例在内存中都<br />有独一无二的地址。</p>
		<p>2） 结构体的静态成员不对结构体的大小产生影响，因为静态变量的存储位置与<br />结构体的实例地址无关。</p>
		<p>例如：</p>
		<p>struct {static int I;} T; struct {char a; static int I;} T1;<br />sizeof(T) == 1; sizeof(T1) == 1;</p>
		<p>3) 某些编译器支持扩展指令设置变量或结构的对齐方式，如VC，<br />  详见MSDN（alignment of structures）</p>
		<p> </p>
		<p>并不是要求#pragma pack(8)，就一定是每个成员都是8字节对齐<br />而是指一组成员要按照8字节对齐。<br />struct s1<br />{<br />   short a;   // 2字节<br />   long b;    // 4字节<br />}；<br />整个s1小于8字节，因此s1就是8字节。</p>
		<p>struct s2<br />{<br />   char c;    // 1字节<br />   s1 d;      // 8字节<br />   __int64 e; // 8字节<br />};<br />整个s2小于12字节，但是由于#pragma pack(8)的限定，12不能与8字节对齐，因此s2就是24字节，c占用8字节<br />－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br /><br /><br />类或对象的长度：<br />   非虚函数相当与全局，不在类里。<br />   静态也是全局，不在类里。<br />   但是const要分配空间。</p>
		<p>
				<br />非静态变量，虚函数链表（如果类中有虚函数的话） -----------分配空间</p>
<img src ="http://www.cnitblog.com/martin/aggbug/9873.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/martin/" target="_blank">Martin</a> 2006-04-28 18:24 <a href="http://www.cnitblog.com/martin/archive/2006/04/28/9873.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]只在多态基类中声明虚析构函数</title><link>http://www.cnitblog.com/martin/archive/2006/04/28/9864.html</link><dc:creator>Martin</dc:creator><author>Martin</author><pubDate>Fri, 28 Apr 2006 09:22:00 GMT</pubDate><guid>http://www.cnitblog.com/martin/archive/2006/04/28/9864.html</guid><wfw:comment>http://www.cnitblog.com/martin/comments/9864.html</wfw:comment><comments>http://www.cnitblog.com/martin/archive/2006/04/28/9864.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/martin/comments/commentRss/9864.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/martin/services/trackbacks/9864.html</trackback:ping><description><![CDATA[
		<span style="font-size: 10pt; font-family: 宋体;">
				<div>
						<span style="font-size: 10pt; font-family: 宋体;">From: http://www.cppblog.com/nacci/archive/2005/11/10/1046.html<br /><br />关于virtual desctructor的详细讨论。同样来自于《Effective C++》3rd Edition。</span>
						<br />
				</div>
				<hr />
		</span>
		<p style="margin: 7.8pt 0cm;">
				<span style="font-size: 10pt; font-family: 宋体;">跟踪时间是很平常的任务，所以开发一个名为</span>
				<span style="font-size: 10pt;" lang="EN-US">
						<font face="Times New Roman">TimeKeeper</font>
				</span>
				<span style="font-size: 10pt; font-family: 宋体;">的基类，并让不同的派生类来实现不同的计时方法是很合理的事情：</span>
				<span style="font-size: 10pt;" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">class</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US"> TimeKeeper {<o:p></o:p></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">public</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">:<o:p></o:p></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<font face="Lucida Console">
								<span style="">    </span>TimeKeeper();<o:p></o:p></font>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<font face="Lucida Console">
								<span style="">    </span>~TimeKeeper();<o:p></o:p></font>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<font face="Lucida Console">
								<span style="">    </span>...<o:p></o:p></font>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">};<o:p></o:p></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p> </o:p>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">class</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US"> AtomicClock: <font face="Lucida Console"><span style="color: blue;">public</span> TimeKeeper { ... };<o:p></o:p></font></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">class</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US"> WaterClock: <font face="Lucida Console"><span style="color: blue;">public</span> TimeKeeper { ... };<o:p></o:p></font></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">class</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US"> WristWatch: <font face="Lucida Console"><span style="color: blue;">public</span> TimeKeeper{ ... };<o:p></o:p></font></span>
		</p>
		<p style="margin: 7.8pt 0cm;">
				<span style="font-size: 10pt; font-family: 宋体;">很多用户都希望直接用这些类来计数，而对于他们究竟是如何实现的并不关心。于是一个我们可以用一个</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">Factory function</span>
				<span style="font-size: 10pt; font-family: 宋体;">——创建一个派生类对象并返回一个基类指针的函数——返回一个指向</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">TimeKeeper</span>
				<span style="font-size: 10pt; font-family: 宋体;">的指针。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">TimeKeeper* getTimeKeeper(); <span style="color: green;"><font face="Lucida Console">// returns a pointer to a dynamically <o:p></o:p></font></span></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<font face="Lucida Console">
								<span style="">                         </span>
								<span style=""> </span>
								<span style="">    </span>
								<span style="color: green;">// allocated object of a class derived<o:p></o:p></span>
						</font>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<font face="Lucida Console">
								<span style="">                         </span>
								<span style=""> </span>
								<span style="">    </span>
								<span style="color: green;">// from TimeKeeper<o:p></o:p></span>
						</font>
				</span>
		</p>
		<p style="margin: 7.8pt 0cm;">
				<span style="font-size: 10pt; font-family: 宋体;">通常，</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">factory function</span>
				<span style="font-size: 10pt; font-family: 宋体;">返回的对象都是创建在堆上的，当用户使用完计数器的时候把对象析构掉是很重要的：</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">TimeKeeper *ptk = getTimeKeeper(); <span style="color: green;"><font face="Lucida Console">// get dynamically allocated object<o:p></o:p></font></span></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<font face="Lucida Console">
								<span style="">                            </span>
								<span style="">   </span>
								<span style="">     </span>
								<span style="color: green;">// from TimeKeeper hierarchy<o:p></o:p></span>
						</font>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">...<font face="Lucida Console"><span style="">                         </span><span style="">   </span><span style="">     </span><span style="color: green;">// use it<o:p></o:p></span></font></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">delete</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US"> ptk;<font face="Lucida Console"><span style="">                     </span><span style="">   </span><span style="color: green;">// release it to avoid resource leak<o:p></o:p></span></font></span>
		</p>
		<p style="margin: 7.8pt 0cm;">
				<span style="font-size: 10pt; font-family: 宋体;">但是，依赖用户来执行删除是错误的重要来源。条款</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">18</span>
				<span style="font-size: 10pt; font-family: 宋体;">介绍了如何修改</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">Factory function</span>
				<span style="font-size: 10pt; font-family: 宋体;">的接口来避免这些常见的用户错误，但是，这些目前都是次要的，因为在上面的代码中还存在更为严重的问题：即使客户执行的正确的动作，你还是无法预期你的程序能够正确执行。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 7.8pt 0cm;">
				<span style="font-size: 10pt; font-family: 宋体;">问题在于</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">getTimeKeeper</span>
				<span style="font-size: 10pt; font-family: 宋体;">返回了一个派生类对象（例如</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">:AutoicClock</span>
				<span style="font-size: 10pt; font-family: 宋体;">），但是这个对象却通过基类的指针来删除（一个指向</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">Timekeeper</span>
				<span style="font-size: 10pt; font-family: 宋体;">的指针），并且这个基类没有虚析构函数。这种组合是制造灾难的良方，因为</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">C++</span>
				<span style="font-size: 10pt; font-family: 宋体;">规定：用不带有虚析构函数的基类的指针来删除一个派生类，其结果是未定的。通常在运行时发生的情况是这个对象的派生类部分没有被析构。如果</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">getTimeKeeper</span>
				<span style="font-size: 10pt; font-family: 宋体;">返回一个指向</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">AtomicClock</span>
				<span style="font-size: 10pt; font-family: 宋体;">对象的指针，那么</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">AtomicClock</span>
				<span style="font-size: 10pt; font-family: 宋体;">中派生类的部分（例如在</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">AtomicClock</span>
				<span style="font-size: 10pt; font-family: 宋体;">中声明的数据成员）将不会被正确的析构，实际上</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">AtomicClock</span>
				<span style="font-size: 10pt; font-family: 宋体;">的析构函数都根本不会被调用。但是，基类的部分，却会被正确的清除，这就造就了一个“畸形”的</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">partially destroyed object</span>
				<span style="font-size: 10pt; font-family: 宋体;">。这是一个非常棒的泄漏资源、破坏数据的方法，它会让你在调试器上花费大量的精力。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 7.8pt 0cm;">
				<span style="font-size: 10pt; font-family: 宋体;">解决这个问题的方法很简单，给派生类加上一个虚析构函数。这样派生类对象就会如你所愿，被正确的清除：</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">class</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US"> TimeKeeper {<o:p></o:p></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">public</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">:<o:p></o:p></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<font face="Lucida Console">
								<span style="">    </span>TimeKeeper();<o:p></o:p></font>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<font face="Lucida Console">
								<span style="">    </span>
								<span style="color: blue;">virtual</span> ~TimeKeeper();<o:p></o:p></font>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<font face="Lucida Console">
								<span style="">    </span>...<o:p></o:p></font>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">};<o:p></o:p></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p> </o:p>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">TimeKeeper *ptk = getTimeKeeper();<span style="color: green;"><o:p></o:p></span></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">...<font face="Lucida Console"><span style="">                         </span><span style="">   <br /></span></font></span>
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">delete</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US"> ptk;<font face="Lucida Console"><span style="">                 </span><span style="color: green;">// now behaves correctlhy<o:p></o:p></span></font></span>
		</p>
		<p style="margin: 7.8pt 0cm;">
				<span style="font-size: 10pt; font-family: 宋体;">像</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">TimeKeeper</span>
				<span style="font-size: 10pt; font-family: 宋体;">这样的基类，除了析构函数外，通常会包含其它的虚函数。因为虚函数的目标就是让派生类来订制基类的实现。例如，</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">getCurrentTime</span>
				<span style="font-size: 10pt; font-family: 宋体;">，在不同的派生类中就会有不同的实现（注：其实</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">getTimeKeeper</span>
				<span style="font-size: 10pt; font-family: 宋体;">也可以是一个虚函数）。任何一个拥有虚函数的类都应该包含一个虚析构函数。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 7.8pt 0cm;">
				<span style="font-size: 10pt; font-family: 宋体;">如果一个类没有虚函数呢，这也就意味着这个类并不是被当作基类来使用的。当遇到这种情况的时候，声明一个虚析构函数往往不是一个好主意。考虑一个用来表示二维空间中的某点的类：</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">class</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US"> Point {<font face="Lucida Console"><span style=""></span><span style="color: green;">// a 2D point<o:p></o:p></span></font></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">public</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">:<o:p></o:p></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<font face="Lucida Console">
								<span style="">    </span>Point(<span style="color: blue;">int</span> xCoord, <span style="color: blue;">int</span> yCoord);<o:p></o:p></font>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<font face="Lucida Console">
								<span style="">    </span>~Point();<o:p></o:p></font>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p> </o:p>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">private</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">:<o:p></o:p></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<font face="Lucida Console">
								<span style="">    </span>
								<span style="color: blue;">int</span> x, y;<br /></font>
				</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">};<o:p></o:p></span>
		</p>
		<p style="margin: 7.8pt 0cm;">
				<span style="font-size: 10pt; font-family: 宋体;">如果一个</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">int</span>
				<span style="font-size: 10pt; font-family: 宋体;">占</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">32 bits</span>
				<span style="font-size: 10pt; font-family: 宋体;">，这样的一个</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">Point</span>
				<span style="font-size: 10pt; font-family: 宋体;">可以被放到一个</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">64</span>
				<span style="font-size: 10pt; font-family: 宋体;">位寄存器中。另外，这样的一个</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">Point</span>
				<span style="font-size: 10pt; font-family: 宋体;">对象还可以被当作是一个整体被其它的语言使用，例如</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">C</span>
				<span style="font-size: 10pt; font-family: 宋体;">或</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">FORTRAN</span>
				<span style="font-size: 10pt; font-family: 宋体;">。但是，如果</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">Point</span>
				<span style="font-size: 10pt; font-family: 宋体;">的析构函数是虚拟的，故事就完全不一样了。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 7.8pt 0cm;">
				<span style="font-size: 10pt; font-family: 宋体;">虚函数的实现需要对象承载某些额外信息，这些信息用来在运行时对虚函数的调用进行正确的转发。这个额外的信息使通过一个</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">vtpr</span>
				<span style="font-size: 10pt; font-family: 宋体;">来实现的。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">Vptr</span>
				<span style="font-size: 10pt; font-family: 宋体;">指向一个存放函数指针（</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">vtbl</span>
				<span style="font-size: 10pt; font-family: 宋体;">）的数组，每一个具有虚函数的类都有一个对应的</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">vtbl</span>
				<span style="font-size: 10pt; font-family: 宋体;">。当一个对象的虚函数被调用的时候，该对象的</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">vtpr</span>
				<span style="font-size: 10pt; font-family: 宋体;">和</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">vtbl</span>
				<span style="font-size: 10pt; font-family: 宋体;">组合来完成定位正确的函数调用的工作。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 7.8pt 0cm;">
				<span style="font-size: 10pt; font-family: 宋体;">这里，虚函数如何实现的并不重要。重要的是如果</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">Point</span>
				<span style="font-size: 10pt; font-family: 宋体;">包含了一个虚函数，对象将会长胖。在一个</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">32 bits</span>
				<span style="font-size: 10pt; font-family: 宋体;">的机器上，它将会从</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">64 bits</span>
				<span style="font-size: 10pt; font-family: 宋体;">长到</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">96 bits</span>
				<span style="font-size: 10pt; font-family: 宋体;">；在</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">64 bit</span>
				<span style="font-size: 10pt; font-family: 宋体;">的机器上，它将会从</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">64 bits</span>
				<span style="font-size: 10pt; font-family: 宋体;">长到</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">128 bits</span>
				<span style="font-size: 10pt; font-family: 宋体;">。这个额外的</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">vtpr</span>
				<span style="font-size: 10pt; font-family: 宋体;">的存在让对象的体积增长了</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">50%~100%</span>
				<span style="font-size: 10pt; font-family: 宋体;">。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">Point</span>
				<span style="font-size: 10pt; font-family: 宋体;">对象也不再能够放到一个</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">64-bit</span>
				<span style="font-size: 10pt; font-family: 宋体;">寄存器中了。另外，</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">Point</span>
				<span style="font-size: 10pt; font-family: 宋体;">对象也不再和</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">C</span>
				<span style="font-size: 10pt; font-family: 宋体;">语言的保持兼容，因为</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">C</span>
				<span style="font-size: 10pt; font-family: 宋体;">语言中没有</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">vrpr</span>
				<span style="font-size: 10pt; font-family: 宋体;">机制。结果是，你要想使用该</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">Point</span>
				<span style="font-size: 10pt; font-family: 宋体;">对象，除非自己来实现</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">vtpr</span>
				<span style="font-size: 10pt; font-family: 宋体;">和</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">vtpl</span>
				<span style="font-size: 10pt; font-family: 宋体;">机制，而这样做，往往又会降低你的代码的可移植性。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 7.8pt 0cm;">
				<span style="font-size: 10pt; font-family: 宋体;">也就是说，把所有的析构函数都不加思索的声明为虚拟的和从不把它们声明为虚拟的一样，都是不明智的行为。实际上，很多人得除了这样的结论：当且仅当一个类有至少一个虚函数的时候，才把析构函数声明为虚拟的。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 7.8pt 0cm;">
				<span style="font-size: 10pt; font-family: 宋体;">实际上，即使你的类中没有虚函数，你还是有可能被非虚析构函数的问题咬上一口。例如</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">std::string</span>
				<span style="font-size: 10pt; font-family: 宋体;">就没有虚函数，但是一些被误导的程序员有时会把它当作基类来使用：</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">class</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US"> SpecialString: <font face="Lucida Console"><span style="color: blue;">public</span> std::string { <o:p></o:p></font></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-indent: 190pt; text-align: left;" align="left">
				<span style="font-size: 10pt; color: green; font-family: 'Lucida Console';" lang="EN-US">// bad idea! std::string has a<o:p></o:p></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<font face="Lucida Console">
								<span style="">    </span>...<span style="">                      </span><span style="">    </span><span style="color: green;">// non-virtual destructor<br /></span></font>
				</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">}<o:p></o:p></span>
		</p>
		<p style="margin: 7.8pt 0cm;">
				<span style="font-size: 10pt; font-family: 宋体;">乍一看，这可能没什么问题，但是一旦你把一个指向</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">SpecialString</span>
				<span style="font-size: 10pt; font-family: 宋体;">的指针转换成一个</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">string</span>
				<span style="font-size: 10pt; font-family: 宋体;">，并用这个指针来删除</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">SpecialString</span>
				<span style="font-size: 10pt; font-family: 宋体;">对象的时候，你马上就被带进了未定义行为的深潭。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">SpecialString *pss = <font face="Lucida Console"><span style="color: blue;">new</span> SpecialString(<span style="color: maroon;">"Impending Doom"</span>);<o:p></o:p></font></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">std::string *ps;<o:p></o:p></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">...<o:p></o:p></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">ps = pss;<font face="Lucida Console"><span style="">  </span><span style="color: green;">// SpecialString* --&gt; std::string*<o:p></o:p></span></font></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; color: green; font-family: 'Lucida Console';" lang="EN-US">
						<o:p> </o:p>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">delete</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US"> ps;<font face="Lucida Console"><span style="">  </span><span style="color: green;">// undefined! In practice, *ps's Specialstring resources<o:p></o:p></span></font></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<font face="Lucida Console">
								<span style="">           </span>
								<span style=""> </span>
								<span style="color: green;">// will be leaked, because the SpecialString destructor won't<span style="">        </span>// be called<o:p></o:p></span>
						</font>
				</span>
		</p>
		<p style="margin: 7.8pt 0cm; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 宋体;">同样的结果还会出现在其它没有虚析构函数的类中，例如所有的</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">STL</span>
				<span style="font-size: 10pt; font-family: 宋体;">容器类型（例如：</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">vector, list, set, tr1::unordered_map</span>
				<span style="font-size: 10pt; font-family: 宋体;">等等）。如果你曾经对于从一个标准容器或其它带有非虚析构函数的类继承，那么彻底打消这个想法。（不幸的是</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">C++</span>
				<span style="font-size: 10pt; font-family: 宋体;">没有提供像</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">C#(sealed)</span>
				<span style="font-size: 10pt; font-family: 宋体;">和</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">Java(final)</span>
				<span style="font-size: 10pt; font-family: 宋体;">类似的拒绝继承的语言机制）</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 7.8pt 0cm; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 宋体;">有时候，把析构函数设定为</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">pure virtual</span>
				<span style="font-size: 10pt; font-family: 宋体;">是非常方便的。一个</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">pure virtual</span>
				<span style="font-size: 10pt; font-family: 宋体;">函数可以让一个类成为抽象类。有时，你可能需要让你的类成为一个</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">abstract class</span>
				<span style="font-size: 10pt; font-family: 宋体;">，但是你一时又找不到合适的纯虚函数。怎么办呢？因为一个抽象类往往是要被作为基类的，而一个基类往往又应该有一个虚析构函数。这样一来：声明一个</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">pure virtual destructor</span>
				<span style="font-size: 10pt; font-family: 宋体;">就是一个不错的主意。一箭双雕。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">class</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US"> AWOV {<font face="Lucida Console"><span style="">  </span><span style="color: green;">// AWOV = "Abstract w/o Virtuals"<o:p></o:p></span></font></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; color: blue; font-family: 'Lucida Console';" lang="EN-US">public</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">:<o:p></o:p></span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<font face="Lucida Console">
								<span style="">    </span>
								<span style="color: blue;">virtual</span> ~AWOV() = 0; <span style="color: green;">// declare pure virtual destructor<o:p></o:p></span></font>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">};<o:p></o:p></span>
		</p>
		<p style="margin: 7.8pt 0cm; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 宋体;">这个类有一个纯虚函数，因此这是以个抽象基类，并且这个类有一个虚析构函数，这也使你远离了析构函数的问题，唯一要注意的，就是一定要为纯虚析构函数提供一份实现。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 7.8pt 0cm; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 宋体;">虚析构函数的工作方式是从最深的派生类的析构函数依次调用其基类的析构函数，编译器会生成生成一个从派生类到基类的</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">~AWOV</span>
				<span style="font-size: 10pt; font-family: 宋体;">的调用。如果你没有提供析构函数的实现，链接器就会抱怨错误。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 7.8pt 0cm; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 宋体;">所以，你只应该把多态基类的析构函数声明为虚拟的。只有你想通过基类接口来操作派生类的时候，一个基类才是多态的。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">TimeKeeper</span>
				<span style="font-size: 10pt; font-family: 宋体;">就是一个多态基类，因为我们需要用一个</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">TimeKeeper*</span>
				<span style="font-size: 10pt; font-family: 宋体;">来操作</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">AtomicClock</span>
				<span style="font-size: 10pt; font-family: 宋体;">和</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">WaterClock</span>
				<span style="font-size: 10pt; font-family: 宋体;">对象。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 7.8pt 0cm; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: 宋体;">另外，并不是所有的基类都要按照多态的方式来设计和使用。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">Std::string</span>
				<span style="font-size: 10pt; font-family: 宋体;">和</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">STL</span>
				<span style="font-size: 10pt; font-family: 宋体;">中的容器类型就都不具备多态性。一些类被设计成基类，但是却不应该按照多态的方式来使用，例如</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">input_iterator_tag</span>
				<span style="font-size: 10pt; font-family: 宋体;">就是一个例子，你并不需要用基类接口来操纵派生类。结果是，他们也不需要虚拟析构函数。</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt; text-align: left;" align="left">
				<span style="font-size: 14pt; font-family: 宋体;">时时刻刻让自己记住</span>
				<span style="font-size: 14pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt; text-align: left;" align="left">
				<span style="font-size: 10pt; font-family: Wingdings;" lang="EN-US">
						<span style="">
								<font face="Wingdings">l</font>
								<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">         </span>
						</span>
				</span>
				<span style="font-size: 10pt; font-family: 宋体;">应该为多态基类声明虚拟析构函数。如果一个类有一个虚函数，那么它也应该有一个虚析构函数</span>
				<span style="font-size: 10pt; font-family: 'Lucida Console';" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<span style="font-size: 10pt; font-family: Wingdings;" lang="EN-US">
				<span style="">
						<font face="Wingdings">l</font>
						<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">         </span>
				</span>
		</span>
		<span style="font-size: 10pt; font-family: 宋体;">如果一个类不是被设计为基类或者它们并不是按照多态的方式来使用的，不要为它们声明虚析构函数</span>
<img src ="http://www.cnitblog.com/martin/aggbug/9864.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/martin/" target="_blank">Martin</a> 2006-04-28 17:22 <a href="http://www.cnitblog.com/martin/archive/2006/04/28/9864.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]C++的类型萃取技术</title><link>http://www.cnitblog.com/martin/archive/2006/04/28/9863.html</link><dc:creator>Martin</dc:creator><author>Martin</author><pubDate>Fri, 28 Apr 2006 09:19:00 GMT</pubDate><guid>http://www.cnitblog.com/martin/archive/2006/04/28/9863.html</guid><wfw:comment>http://www.cnitblog.com/martin/comments/9863.html</wfw:comment><comments>http://www.cnitblog.com/martin/archive/2006/04/28/9863.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/martin/comments/commentRss/9863.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/martin/services/trackbacks/9863.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: From: http://www.cppblog.com/nacci/archive/2005/11/03/911.html																										自从C++中引入了template后，以泛型技术为中心的设计得到了长足的进步。STL就是这个阶段杰出的产物。STL的目标就是要把数据和算法分开，分别对其进行设计，之后通过一种名为iterato...&nbsp;&nbsp;<a href='http://www.cnitblog.com/martin/archive/2006/04/28/9863.html'>阅读全文</a><img src ="http://www.cnitblog.com/martin/aggbug/9863.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/martin/" target="_blank">Martin</a> 2006-04-28 17:19 <a href="http://www.cnitblog.com/martin/archive/2006/04/28/9863.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]一劳永逸：关于C/C++中指针、数组与函数复合定义形式的直观解释</title><link>http://www.cnitblog.com/martin/archive/2006/04/26/9704.html</link><dc:creator>Martin</dc:creator><author>Martin</author><pubDate>Wed, 26 Apr 2006 10:35:00 GMT</pubDate><guid>http://www.cnitblog.com/martin/archive/2006/04/26/9704.html</guid><wfw:comment>http://www.cnitblog.com/martin/comments/9704.html</wfw:comment><comments>http://www.cnitblog.com/martin/archive/2006/04/26/9704.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/martin/comments/commentRss/9704.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/martin/services/trackbacks/9704.html</trackback:ping><description><![CDATA[From: http://neoragex2002.cnblogs.com/archive/2005/11/06/269974.html<br /><br /><br />今天又捧起久违的<a href="http://cm.bell-labs.com/cm/cs/cbook/">K&amp;R C</a>拜读了一遍。其实有点东西在6年前就想写，借着今天这个机会，终于把它写出来了。 <br /><br />初看一眼标题中的变量定义感觉是不是很抓狂？：）一直以来，C语言中关于指针、数据和函数的复合定义都是一个难点，其实，理解它也是有规律可循的。然而，即便是国内在讲解指针方面久负盛名的<a href="http://www.china-pub.com/computers/common/info.asp?id=1689">“谭本”</a>也没有将这一规律性说清楚，K&amp;R C虽然提到了一点，却始终没有捅破这层窗户纸，也许是K&amp;R觉得以“直观方式”解释太阳春白雪了点吧：）在Blog上面说说这种不值一提的dd倒正合适。 <br /><br />其实，理解C语言中复合定义的关键在于对变量声明语句中各修饰符结合律的把握，我们可以将它们的结合规律简单归纳如下： <br /><br /><font face="宋体">(1) 函数修饰符 <font color="#0000ff">( )</font> 从左至右 <br />(2) 数组修饰符 <font color="#0000ff">[ ]</font> 从左至右 <br />(3) 指针修饰符  <font color="#0000ff">* </font> 从右至左</font><br /><br />其中，(1)与(2)的修饰优先级是相同的，而(3)比前两者的优先级都低，而且是写在左边的。下面我们给出3个直观的例子来说明如何借助结合律来理解复合变量声明，为了简单点，函数修饰符一律使用无形参的签名形式。 <br /><br /><font face="宋体"><b>示例1. </b><font color="#0000ff">char</font> (*(*x[3])())[5]</font><br />这是什么意思？别急，跟着走一遭咱就知道是什么了。根据结合律，我们可以依次写出与x结合的修饰符： <br /><br /><font face="宋体"><font color="#0000ff">x</font> -1-&gt; <font color="#0000ff">[3]</font> -2-&gt; <font color="#0000ff">*</font> -3-&gt; <font color="#0000ff">()</font> -4-&gt; <font color="#0000ff">*</font> -5-&gt; <font color="#0000ff">[5]</font> -6-&gt; <font color="#0000ff">char</font><br /></font><br />然后我们再来从左至右地对上述过程进行解释： <br /><br />1说明：x是一个一维数组，数组中元素个数为3 <br />2说明：上述数组中每一个元素都是一个指针 <br />3说明：这些指针都是函数的指针，该函数的签名为( ) <br />4说明：上面的函数所返回的值是一个指针 <br />5说明：上面的指针所指向的是一个一维数组，其元素个数为5 <br />6说明：上面的数组中的每一个元素均是一个字符 <br /><br />不
知大家在上面的规范化步骤描述中看出端倪来了没有？：）这个声明的含义是：x是一个由3个指向函数A的指针所组成的一维数组，函数A返回指向一个元素个数
为5的字符数组的指针。其实，以结合律来解析复合声明的方式是一种“由近及远”的方式：首先尝试着去说清楚离变量“近”的修饰符的含义，然后再对“远处”
的修饰符进行依次说明，从抽象到具体，从顶到底，层层细化。 <br /><br />实际上，我比较反感这种一步到位的复合方式，它不仅把变量定义和类型声明混为一谈，而且也不能直观地体现出类型的含义，更糟糕的是，这不符合典型的“积木化”的程序思维，我更倾向于采用<font color="#0000ff" face="宋体">typedef</font>，以一种“由远及近”的方式来逐步定义变量的形态,即先定义若干基本类型，然后再在其基础上将其扩充成复杂类型，最后利用复杂类型定义变量。例如，上述的例子，如果要我来定义，我觉得如此定义比较恰当： <br /><br /><font face="宋体"><font color="#0000ff">typedef char</font><strong>ArrayOfChar</strong>[5]; <br /><font color="#0000ff">typedef</font> ArrayOfChar* <strong>PointerOfArrayOfChar</strong>; <br /><font color="#0000ff">typedef</font> PointerOfArrayOfChar (*<strong>PointerOfFunc</strong>)() <br /><font color="#0000ff">typedef</font> PointerOfFunc <strong>ArrayOfPointerOfFunc</strong>[3] <br /><strong>ArrayOfPointerOfFunc</strong> pfa;</font><br /><br />这种“堆积木”的方式实际上和那个复合声明是等价的，其看似繁冗，但对于程序员而言却很直观，所以平心而论，我比较推荐这种积木化声明方式，而不推荐以复合声明直接一步到位。 <br /><br /><font face="宋体"><b>示例2.</b><font color="#0000ff">char</font> (**x[3])()[5]</font><br />根据结合律，将上述声明改写如下： <br /><br /><font face="宋体"><font color="#0000ff">x</font> -1-&gt; <font color="#0000ff">[3]</font> -2-&gt; <font color="#0000ff">*</font> -3-&gt; <font color="#0000ff">*</font> -4-&gt; <font color="#0000ff">()</font> -5-&gt; <font color="#0000ff">[5] <span style="color: rgb(0, 0, 0);">-6-&gt;</span> char</font></font><br /><br />1说明：x是一个数组，这个数组包括3个元素 <br />2说明：每个元素均为一个指针 <br />3说明：上面的指针又指向另一个指针 <br />4说明：上面的第二个指针是一个函数的指针 <br />5说明：上面的函数返回的是一个数组，这个数组包括5个元素?? (<font color="#ff0000">错误!</font>) <br /><br />从
上述推导过程可以发现，当我们到达第5步时，其语义提到了“一个函数返回了一个数组”，这在C语言中实际上是错误的定义，即，( )与[
]相邻是非法的，因此，编译器将拒绝接受这一关于x变量的声明。同样的，在推导过程中[ ]与(
)相邻也是不合法的，什么叫做“一个数组，这个数组里面的每一个元素都是一个函数(而不是一个指针)”？在这种情况下，编译器也会100％报错。 <br /><br /><font face="宋体"><b>示例3.</b><font color="#0000ff">char</font> p[5][7]<span lang="zh-cn">、<font color="#0000ff">char</font> (*q)[7]、<font color="#0000ff">char </font>*r[5] 和<font color="#0000ff"> char</font> **s</span></font><br />不知<span lang="zh-cn">p、q、r、s这四个变量类型是否兼容？</span>根据结合律，<span lang="zh-cn">有： <br /><br /><font face="宋体"><font color="#0000ff">p</font> -&gt; <font color="#0000ff">[5]</font> -&gt; (<font color="#0000ff">[7]</font> -&gt; <font color="#0000ff">char</font></font></span><font face="宋体"><span lang="zh-cn">) <span style="color: rgb(0, 0, 255);"><font face="宋体"><span lang="zh-cn">const</span></font><span lang="zh-cn"><font face="宋体"> </font></span></span></span></font><span lang="zh-cn"><font face="宋体"><br /></font></span><font face="宋体"><span lang="zh-cn"><font color="#0000ff">q</font> -&gt;  </span><font color="#0000ff">*</font><span lang="zh-cn">  -&gt; (</span><font color="#0000ff">[7]</font></font><span lang="zh-cn"><font face="宋体"> -&gt; <font color="#0000ff">char</font></font></span><font face="宋体"><span lang="zh-cn">)</span></font><span lang="zh-cn"><font face="宋体"><br /></font></span><font color="#0000ff" face="宋体">r</font><font face="宋体"><span lang="zh-cn"> -&gt; </span><font color="#0000ff">[5]</font><span lang="zh-cn"> -&gt; { </span><font color="#0000ff">*</font></font><span lang="zh-cn"><font face="宋体">  -&gt; <font color="#0000ff">char</font></font></span><font face="宋体"><span lang="zh-cn">} <span style="color: rgb(0, 0, 255);">const</span><br /><font color="#0000ff">s</font> -&gt;  </span><font color="#0000ff">*</font><span lang="zh-cn">  -&gt; {</span><font color="#0000ff"> *</font></font><span lang="zh-cn"><font face="宋体">  -&gt; <font color="#0000ff">char</font>} <br /></font><br />不难发现，无需经过类型强制转换即可将p赋值给q、将r赋给s，而其他的赋值方式均是错误的。为什么？首先，p和r是两个数组，不是指针，因此不能修改其值；其次，不妨让我们来对p与q(或者r与s)在其括号内的类型部分分别进行<font face="宋体"><span style="color: rgb(0, 0, 255);">sizeof</span></font>运算，可以发现，二者的结果是一样的，即：p、q(或者r、s)指针变量具备一致的增量寻址行为，所以二者才兼容。 <br /></span><br />看完了上述解释，想必最唬人的指针复合定义恐怕也难不倒你了。试试下面的挑战如何？ <br /><font face="宋体"><font color="#0000ff"><br /><span style="color: rgb(0, 0, 0);">1. 解释一下x变量的含义：</span>char</font> *(*(**(*(*(*x[5])(<font color="#0000ff">int</font>,<font color="#0000ff">float</font>))[][12])(<font color="#0000ff">double</font>))(<font color="#0000ff">short</font>,<font color="#0000ff">long</font>))[][173]; <br />2. 在32位环境下，假设<span style="color: rgb(0, 0, 255);">void</span>* p=(<span style="color: rgb(0, 0, 255);">void </span>*)(x+1)，x=0x1234；则p的16进制值为多少？<font color="#0000ff">sizeof</font>(x)等于多少？</font><img src ="http://www.cnitblog.com/martin/aggbug/9704.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/martin/" target="_blank">Martin</a> 2006-04-26 18:35 <a href="http://www.cnitblog.com/martin/archive/2006/04/26/9704.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]谈谈C++继承中的重载，覆盖和隐藏</title><link>http://www.cnitblog.com/martin/archive/2006/04/26/9701.html</link><dc:creator>Martin</dc:creator><author>Martin</author><pubDate>Wed, 26 Apr 2006 09:40:00 GMT</pubDate><guid>http://www.cnitblog.com/martin/archive/2006/04/26/9701.html</guid><wfw:comment>http://www.cnitblog.com/martin/comments/9701.html</wfw:comment><comments>http://www.cnitblog.com/martin/archive/2006/04/26/9701.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cnitblog.com/martin/comments/commentRss/9701.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/martin/services/trackbacks/9701.html</trackback:ping><description><![CDATA[From: http://www.cppblog.com/ace/archive/2006/04/25/6243.html<br /><br />    写正题之前，先给出几个关键字的中英文对照，重载(overload)，覆盖(override),隐藏(hide)。在早期的C++书籍中，可能
翻译的人不熟悉专业用语（也不能怪他们，他们不是搞计算机编程的，他们是英语专业的），常常把重载(overload)和覆盖(override)搞错！<p>　　我们先来看一些代码及其编译结果。</p><p>　　实例一：<br />　　<br />　　#include "stdafx.h"<br />　　#include &lt;iostream.h&gt;</p><p>　　class CB<br />　　{<br />　　public:<br /> 　　　　<font color="#ff0000">void f(int)<br /></font> 　　　　{<br /> 　　　　　　 cout &lt;&lt; "CB::f(int)" &lt;&lt; endl;<br />　　　　 }</p><p>　　};</p><p><br />　　class CD : public CB<br />　　{<br />　　public:<br />　　　　 <font color="#ff0000">void f(int,int)<br /></font> 　　　　{<br />  　　　　　　cout &lt;&lt; "CD::f(int,int)" &lt;&lt; endl;<br /> 　　　　}</p><p> 　　　　void test()<br /> 　　　　{<br />  　　　　　f(1);<br /> 　　　　}<br />　　};</p><p>　int main(int argc, char* argv[])<br />　{<br />　　　 return 0;<br />　}<br />编译了一下<br />error C2660: 'f' : function does not take 1 parameters</p><p><br />结论：在类CD这个域中，没有f(int)这样的函数，基类中的void f(int)被<font color="#ff0000">隐藏</font></p><p>　　如果把派生CD中成员函数void f(int,int)的声明改成和基类中一样，即f(int)，基类中的void f(int)还是一样被覆盖，此时编译不会出错，在函数中test调用的是CD中的f(int)　</p><p>　　所以，在<font color="#ff0000">基类中</font>的某些函数，如果<font color="#ff0000">没有</font><font color="#ff0000">virtral</font>关键字，函数名是<font color="#ff0000">f(</font>参数是什么我们不管)，那么如果在派生类CD中<font color="#ff0000">也声明了某个f</font>成员函数，那么在类CD域中，<font color="#ff0000">基类中所有的那些f都被隐藏。</font><br />　　如果你比较心急，想知道什么是隐藏，看文章最后的简单说明，不过我建议你还是一步一步看下去。</p><p>　　我们刚才说的是没有virtual的情况，如果有virtual的情况呢？？<br />　　实例二：</p><p>#include "stdafx.h"<br />#include &lt;iostream.h&gt;</p><p>class CB<br />{<br />public:<br /> 　　<font color="#ff0000">virtual void f(int)<br /></font> 　　{<br />  　　　　cout &lt;&lt; "CB::f(int)" &lt;&lt; endl;<br /> 　　}</p><p>};</p><p><br />class CD : public CB<br />{<br />public:<br />　　 <font color="#ff0000">void f(int)<br /></font> 　　{<br />  　　　　cout &lt;&lt; "CD::f(int)" &lt;&lt; endl;<br />　　 }</p><p>};</p><p>int main(int argc, char* argv[])<br />{<br /> 　return 0;<br />}</p><p>　　这么写当然是没问题了，在这里我不多费口舌了，这是很简单的，多态，虚函数，然后什么指向基类的指针指向派生类对象阿，通过引用调用虚函数阿什么的，属性多的很咯，什么？？你不明白？？随便找本C++的书，对会讲多态和虚函数机制的哦！！<br />　　这种情况我们叫<font color="#ff0000">覆盖(override)！</font>覆盖指的是派生类的虚拟函数覆盖了基类的同名且参数相同的函数！<br />　　在这里，我要强调的是，这种覆盖，要满足两个条件<br />　<font size="4">(a)</font><font color="#ff0000">有virtual关键字</font>，在基类中函数声明的时候加上就可以了<br />　<font size="4">(b)</font>基类CB中的函数和派生类CD中的函数<font color="#ff0000">要一模一样</font>，什么叫一模一样，<font color="#ff0000">函数名，参数，返回类型三个条件</font>。<br />　　有人可能会对(b)中的说法质疑，说返回类型也要一样？？<br />　　是，覆盖的话必须一样，我试了试，如果在基类中,把f的声明改成virtual int f(int)，编译出错了<br />　　error C2555: 'CD::f' : overriding virtual function differs from 'CB::f' only by return type or calling convention<br />　　所以，覆盖的话，必须要满足上述的(a)(b)条件</p><p>　　那么如果基类CB中的函数f有关键字virtual　，但是参数和派生类CD中的函数f参数不一样呢，<br />实例三:<br />  #include "stdafx.h"<br />#include &lt;iostream.h&gt;</p><p>class CB<br />{<br />　public:<br /> 　　 virtual  void f(int)<br />　　 {<br /> 　　　　 cout &lt;&lt; "CB::f(int)" &lt;&lt; endl;<br />　　 }</p><p>}<br />;</p><p><br />class CD : public CB<br />{<br />public:<br /> 　　 void f(int，int)<br /> 　　{<br /> 　　　 cout &lt;&lt; "CD::f(int，int)" &lt;&lt; endl;<br /> 　　}</p><p> 　　void test()<br /> 　　{<br /> 　　　　 f(1);<br /> 　　}<br />}<br />;</p><p>int main(int argc, char* argv[])<br />{<br /> return 0;<br />}</p><p>编译出错了，<br /> error C2660: 'f' : function does not take 1 parameters<br />　　咦？？好面熟的错？？对，和实例一中的情况一样哦，结论也是基类中的函数被隐藏了。</p><p>　　通过上面三个例子，得出一个简单的结论<br />如果<font color="#ff0000">基类中的函数和派生类中的两个名字一样的函数f</font><br />满足下面的两个条件<br /><font size="4">(a)在基类中函数声明的时候有virtual关键字<br />(b)基类CB中的函数和派生类CD中的函数一模一样，函数名，参数，返回类型都一样。<br />那么这就是叫做<font color="#ff0000">覆盖(override)，</font>这也就是虚函数，多态的性质</font></p><p><font size="4">那么其他的情况呢？？只要名字一样，不满足上面覆盖的条件，就是<font color="#ff0000">隐藏</font>了。</font></p><p><font size="4">下面我要讲最关键的地方了</font>，好多人认为，基类CB中的f(int)会继承下来和CD中的f(int,int)在派生类CD中构成重载，就像实例一中想像的那样。<br />　　对吗？我们先看重载的定义<br />　　<font color="#ff0000">重载(overload):<br /></font>　　必须在一个域中,函数名称相同但是函数参数不同,重载的作用就是同一个函数有不同的行为,因此不是在一个域中的函数是无法构成重载的,这个是重载的重要特征<br />　　<font color="#ff0000" size="4">必须在一个域中</font>，而继承明显是在两个类中了哦，所以上面的想法是不成立的，我们测试的结构也是这样，派生类中的f(int,int)把基类中的f(int)隐藏了<br />　　所以，<font color="#ff0000">相同的函数名的函数，在基类和派生类中的关系只能是覆盖或者隐藏。</font></p><p>　　在文章中，我把重载和覆盖的定义都给了出来了，但是一直没有给隐藏的定义，在最后，我把他给出来，这段话是网上google来的，比较长，你可以简单的理解成，在派生类域中，看不到基类中的那个同名函数了，或者说，是并没有继承下来给你用，呵呵，如实例一　那样。<br />　　</p><p><font color="#ff0000">隐藏(hide):<br /></font>指
的是派生类的成员函数隐藏了基类函数的成员函数.隐藏一词可以这么理解:在调用一个类的成员函数的时候,编译器会沿着类的继承链逐级的向上查找函数的定
义,如果找到了那么就停止查找了,所以如果一个派生类和一个基类都有同一个同名(暂且不论参数是否相同)的函数,而编译器最终选择了在派生类中的函数,那
么我们就说这个派生类的成员函数"隐藏"了基类的成员函数,也就是说它阻止了编译器继续向上查找函数的定义</p><img src ="http://www.cnitblog.com/martin/aggbug/9701.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/martin/" target="_blank">Martin</a> 2006-04-26 17:40 <a href="http://www.cnitblog.com/martin/archive/2006/04/26/9701.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]如何调试MFC中的内存泄漏</title><link>http://www.cnitblog.com/martin/archive/2006/04/21/9460.html</link><dc:creator>Martin</dc:creator><author>Martin</author><pubDate>Fri, 21 Apr 2006 09:53:00 GMT</pubDate><guid>http://www.cnitblog.com/martin/archive/2006/04/21/9460.html</guid><wfw:comment>http://www.cnitblog.com/martin/comments/9460.html</wfw:comment><comments>http://www.cnitblog.com/martin/archive/2006/04/21/9460.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/martin/comments/commentRss/9460.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/martin/services/trackbacks/9460.html</trackback:ping><description><![CDATA[
		<font color="#808080">
				<font color="#000000">From: http://www.cnitblog.com/wangk/archive/2005/12/14/5369.html</font>
				<br />
				<br />最近好像常常看到有人问如何调试内存泄漏的问题，于是我写下本文，抛砖引玉……<br />  <br />         </font>
		<font color="#000000">首先，应该是MFC报告我们发现内存泄漏。注意：要多运行几次，以确定输出的内容不变，特别是{}之间的数值，不能变，否则下面的方法就不好用了。<br /><img src="http://www.cnitblog.com/images/cnitblog_com/martin/image001.jpg" alt="image001.jpg" border="0" height="194" width="880" /><br /><br />        我们来看看：</font>
		<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);">
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">F:\CodeSample\Test\TestPipe\LeakTest\MainFrm.cpp(</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">54</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">) : {</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">86</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">} normal block at </font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">0x00422E80</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">, </font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">10</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000"> bytes </font>
				</span>
				<span style="color: rgb(0, 0, 255);">
						<font color="#000000">long</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">.<br /> Data: </font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">&lt;</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">          </font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">&gt;</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000"> 1F 1F 1F 1F 1F CD CD CD CD CD <br /></font>
				</span>
		</div>
		<font color="#000000">
				<br />         <span style="color: rgb(0, 0, 0);">F:\CodeSample\Test\TestPipe\LeakTest\MainFrm.cpp(</span><span style="color: rgb(0, 0, 0);">54</span><span style="color: rgb(0, 0, 0);">)
告诉我们MFC认为是在该文件的54行，发生了内存泄漏。你双击改行就可以转到该文件的54行了。但是有时候这一信息并不能用来准确判断，比如：MFC可
能报告Strcore.cpp文件的某行，实际上这是CString的实现函数，此时并不知道什么时候发生了内存泄漏。<br /><br /></span>         此时我们需要更多的信息。那么我们看看紧接其后的：<br /><span style="color: rgb(0, 0, 0);"><br /></span></font>
		<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);">
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">{</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">86</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">} normal block at </font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">0x00422E80</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">, </font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">10</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000"> bytes </font>
				</span>
				<span style="color: rgb(0, 0, 255);">
						<font color="#000000">long</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">.<br /> Data: </font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">&lt;</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">          </font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">&gt;</font>
				</span>
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000"> 1F 1F 1F 1F 1F CD CD CD CD CD <br /></font>
				</span>
		</div>
		<font color="#000000">
				<br />         它告诉我们：在第86次分配的内存没有释放，一共有10字节，内容移16进制方式打印给我们看。<br /><br />         有了这些信息，我们可以开始调试内存泄漏了。<br /><br />         按下F10在程序的刚开始处，停下来，打开Watch窗口：<br /><br /><img src="http://www.cnitblog.com/images/cnitblog_com/martin/image002.jpg" alt="image002.jpg" border="0" height="334" width="665" /><br /><br />         在Watch窗口中输入：<br /><br /></font>
		<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);">
				<span style="color: rgb(0, 0, 0);">
						<font color="#000000">{,,msvcrtd.dll}_crtBreakAlloc</font>
				</span>
		</div>
		<p>
				<font color="#000000">
						<br /> 
						<img src="http://www.cnitblog.com/images/cnitblog_com/martin/image003.jpg" alt="image003.jpg" border="0" height="174" width="450" /><br /><br />         然后更改值为上文提到的分配次数：86<br /><br /><img src="http://www.cnitblog.com/images/cnitblog_com/martin/image004.jpg" alt="image004.jpg" border="0" height="174" width="356" /><br /><br />         接着按下F5继续，然后在第86次分配的时候会发生中断：<br /><br /><img src="http://www.cnitblog.com/images/cnitblog_com/martin/image005.jpg" alt="image005.jpg" border="0" height="125" width="311" /><br /><br />         然后我们打开堆栈窗口：<br /><br /><img src="http://www.cnitblog.com/images/cnitblog_com/martin/image006.jpg" alt="image006.jpg" border="0" height="315" width="326" /><img src="http://www.cnitblog.com/images/cnitblog_com/martin/image007.jpg" alt="image007.jpg" border="0" height="174" width="356" /><br /><br />      往回查看最近我们自己的代码，双击堆栈我们自己的函数那一层，上图有绿色三角的那一层。就定位到泄漏时分配的内存了。<br /><br /><img src="http://www.cnitblog.com/images/cnitblog_com/martin/image008.jpg" alt="image008.jpg" border="0" height="418" width="729" /><br /></font>
		</p>
		<p>
				<font color="#000000">         之后，就是看你的编码功底了。</font>
		</p>
		<p>
				<br />
		</p>
		<p>
				<br />
		</p>
		<br />
		<h2>
				<a id="viewpost1_TitleUrl" class="singleposttitle" href="../../Raistlin/archive/2005/12/14/5380.html">你也许还没用过的vc++的调试的功能</a>
		</h2>
		<p>
				<font color="#000000">From: http://www.cnitblog.com/Raistlin/archive/2005/12/14/5380.html</font>
		</p>
		<p>刚刚在IT博客网闲逛的时候看到了<a href="../../wangk/">孤独的夜</a>的一片文章《<a href="../../wangk/archive/2005/12/14/5369.html">如何调试MFC中的内存泄漏</a>》，讲道用设置{,,msvcrtd.dll}_crtBreakAlloc<font style="background-color: rgb(255, 255, 255);"><strong>这个变量</strong></font>来调试内存泄露的问题。<br /><br />在<a href="http://support.microsoft.com/kb/q151585/">How to use _crtBreakAlloc to debug a memory allocation</a>你可以找到英文的更完整的版本，静态链接和动态连接到C运行库的名称是不一样的<br />静态:_crtBreakAlloc<br />动态:{,,msvcr40d.dll}*__p__crtBreakAlloc()  (vc++4.0 和4.1版本，估计没人在用吧)<br />         {,,msvcrtd.dll}*__p__crtBreakAlloc()  (Visual C++ 4.2 or later)<br />         {,,msvcrtd.dll}_crtBreakAlloc (好像这样也是可以的)<br /><br /><br />{,,msvcrtd.dll}__p__crtBreakAlloc()是个什么东西呢？<br /><br />查看msdn索引“Advanced Breakpoint”and you will find out...<br /><br />语法如下：<br />{[function],[source],[exe] } location<br />{[function],[source],[exe] } variable_name<br />{[function],[source],[exe] } expression</p>
		<p>
				<br />
		</p>
<img src ="http://www.cnitblog.com/martin/aggbug/9460.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/martin/" target="_blank">Martin</a> 2006-04-21 17:53 <a href="http://www.cnitblog.com/martin/archive/2006/04/21/9460.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《C++程序设计语言(特别版)》忠告(advice)部分 (转贴)</title><link>http://www.cnitblog.com/martin/archive/2006/04/21/9447.html</link><dc:creator>Martin</dc:creator><author>Martin</author><pubDate>Fri, 21 Apr 2006 04:17:00 GMT</pubDate><guid>http://www.cnitblog.com/martin/archive/2006/04/21/9447.html</guid><wfw:comment>http://www.cnitblog.com/martin/comments/9447.html</wfw:comment><comments>http://www.cnitblog.com/martin/archive/2006/04/21/9447.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/martin/comments/commentRss/9447.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/martin/services/trackbacks/9447.html</trackback:ping><description><![CDATA[
		<span style="text-decoration: underline;">
		</span>From: http://kevinwan.cnblogs.com/archive/2006/04/14/375481.html<br /><p>Bjarne Stroustrup<br />裘宗燕 译<br />_________________________________________
</p><table align="center" border="0" cellpadding="4" cellspacing="0" height="101" width="654"><tbody><tr><td><p>这
里是一组在你学习C++的过程中或许应该考虑的"规则"。随着你变得更加熟练，你将能把它转化为某种更适合你的那类应用系统或者你自己的程序设计风格的东
西。它们有意被写得很简单，因此都缺乏细节。请不要太拘泥于它们的字面意义。要写出一个好程序需要智慧、品味和耐性。你不会第一次就能把它搞好的。试验！
</p></td></tr></tbody></table><br /><p>[1]在编程序时，你是在为你针对某个问题的解决方案中的思想建立起一种具体表示。让程序的结构尽可能地直接反映这些思想： <br />[a]
如果你能把"它"看成一个独立的概念，就把它做成一个类。 <br />[b] 如果你能把"它"看成一个独立的实体，就把它做成某个类的一个对象。 <br />[c]
如果两个类有共同的界面，将此界面做成一个抽象类。 <br />[d] 如果两个类的实现有某些显著的共同东西，将这些共性做成一个基类。 <br />[e]
如果一个类是一种对象的容器，将它做成一个模板。 <br />[f] 如果一个函数实现对某容器的一个算法，将它实现为对一族容器可用的模板函数。 <br />[g]
如果一组类、模板等互相之间有逻辑联系，将它们放进一个名字空间里。 </p><p>[2]在你定义一个并不是实现某个像矩阵或复数这样的数学对象的类时，或者定义一个低层的类型如链接表的时候： <br />[a] 不要使用全局数据（使用成员）。
<br />[b] 不要使用全局函数。 <br />[c] 不要使用公用数据成员。 <br />[d] 不要使用友元，除非为了避免[a]或[c]。 <br />[e]
不要在一个类里面放"类型域"；采用虚函数。 <br />[f] 不要使用在线函数，除非作为效果显著的优化。 </p><p>更特殊或更详尽的实用规则可以在每章最后的"忠告"一节里找到。请记住，这些忠告只是粗略的实用规则，而不是万古不变的定律。它们只应使用在"合理的地方"。从来就没有任何东西能够替代智慧、经验、常识和好的鉴赏力。
</p><p>我发现具有"绝不要做这个"形式的规则不大有帮助。因此，大部分忠告被写成应该做什么的建议，而否定性的建议也倾向于不采用绝对禁止的短语。据我所
知，没有任何一种主要的C++特征没有被良好地使用过。在有关"忠告"的节里不包括解释，相反，每条忠告都引用了本书中某些适当的章节。在给出否定性的忠
告时，对应章节里通常都提供了有关其他替代方式的建议。
</p><p>第2章忠告 <br />[1] 不用害怕，一切都会随着时间的推移而逐渐明朗起来；2.1节。 <br />[2]
你并不需要在知道了C++的所有细节之后才能写出好的C++程序；1.7节。 <br />[3] 请特别关注程序设计技术，而不是各种语言特征；2.1节。 </p><p>第3章忠告 <br />[1] 不要像重新发明车轮那样企图做每件事；去使用库。 <br />[2]
不要相信奇迹；要理解你的库能做什么，它们如何做，它们做时需要多大的代价。 <br />[3] 当你遇到一个选择时，应该优先选择标准库而不是其他的库。 <br />[4]
不要认为标准库对于任何事情都是最理想的。 <br />[5] 切记#include你所用到的功能的头文件；3.3节。 <br />[6]
记住，标准库的功能定义在名字空间std之中；3.3节。 <br />[7] 请用string，而不是char*；3.5节、3.6节。 <br />[8]
如果怀疑，就用一个检查区间范围的向量（例如Vec）；3.7.2节。 <br />[9] vector、list和map 都比T[ ]
好；3.7.1节、3.7.3节、3.7.4节。 <br />[10]
如要向一个容器中添加一个元素，用push_back()或back_inserter()；3.7.3节、3.8节。 <br />[11]
采用对vector的push_back()，而不是对数组的realloc()；3.8节。 <br />[12] 在main()中捕捉公共的异常；3.7.2节。
</p><p>第4章忠告 <br />[1] 保持较小的作用域；4.9.4节。 <br />[2] 不要在一个作用域和它外围的作用域里采用同样的名字；4.9.4节。
<br />[3] 在一个声明中（只）声明一个名字；4.9.2节。 <br />[4] 让常用的和局部的名字比较短，让不常用的和全局的名字比较长；4.9.3节。
<br />[5] 避免看起来类似的名字；4.9.3节。 <br />[6] 维持某种统一的命名风格；4.9.3节。 <br />[7]
仔细选择名字，反映其意义而不是反映实现方式；4.9.3节； <br />[8]
如果所用的内部类型表示某种可能变化的值，请用typedef为它定义一个有意义的名字；4.9.7节。 <br />[9]
用typedef为类型定义同义词，用枚举或类去定义新类型；4.9.7节。 <br />[10]
切记每个声明中都必须描述一个类型（没有"隐式的int"）；4.9.1节。 <br />[11] 避免有关字符数值的不必要假设；4.3.1节、C.6.2.1节。
<br />[12] 避免有关整数大小的不必要假设；4.6节。 <br />[13] 避免有关浮点类型表示范围的不必要假设；4.6节。 <br />[14]
优先使用普通的int而不是short int或者long int；4.6节。 <br />[15] 优先使用double而不是float或者long
double；4.5节。 <br />[16] 优先使用普通的char而不是signed char或者unsigned char；C.3.4节。 <br />[17]
避免做出有关对象大小的不必要假设； 4.6节。 <br />[18] 避免无符号算术；4.4节。 <br />[19]
应该带着疑问去看待从signed到unsigned，或者从unsigned到signed的转换；C.6.2.6节。 <br />[20]
应该带着疑问去看待从浮点到整数的转换； C.6.2.6节。 <br />[21] 应该带着疑问去看待向较小类型的转换，如将int转换到char；C.6.2.6节。
</p><p>第5章忠告 <br />[1] 避免非平凡的指针算术；5.3节。 <br />[2] 当心，不要超出数组的界线去写；5.3.1节。 <br />[3]
尽量使用0而不是NULL；5.1.1节。 <br />[4] 尽量使用vector和valarray而不是内部（C风格）的数组；5.3.1节。 <br />[5]
尽量使用string而不是以0结尾的char数组；5.3节。 <br />[6] 尽量少用普通的引用参数；5.5节。 <br />[7]
避免void*，除了在某些低级代码里；5.6节。 <br />[8]
避免在代码中使用非平凡的文字量（"神秘的数"）。相反，应该定义和使用各种符号常量。4.8节、5.4节。 </p><p>第6章忠告 <br />[1] 应尽可能使用标准库，而不是其他的库和"手工打造的代码"；6.1.8节。 <br />[2] 避免过于复杂的表达式；6.2.3节。
<br />[3] 如果对运算符的优先级有疑问，加括号；6.2.3节。 <br />[4] 避免显式类型转换（强制）；6.2.7节。 <br />[5]
若必须做显式类型转换，提倡使用特殊强制运算符，而不是C风格的强制；6.2.7节。 <br />[6] 只对定义良好的构造使用T(e)记法；6.2.8节。
<br />[7] 避免带有无定义求值顺序的表达式； 6.2.2节。 <br />[8] 避免goto；6.3.4节。 <br />[9] 避免do语句；6.3.3节。
<br />[10] 在你已经有了去初始化某个变量的值之前，不要去声明它；6.3.1节、6.3.2.1节、6.3.3.1节。 <br />[11]
使注释简洁、清晰、有意义；6.4节。 <br />[12] 保持一致的缩进编排风格；6.4节。 <br />[13] 倾向于去定义一个成员函数operator
new()（15.6节）去取代全局的operator new()；6.2.6.2节。 <br />[14] 在读输入的时候，总应考虑病态形式的输入；6.1.3节。
</p><p>第7章忠告 <br />[1] 质疑那些非const的引用参数；如果你想要一个函数去修改其参数，请使用指针或者返回值；5.5节。 <br />[2]
当你需要尽可能减少参数复制时，应该使用const引用参数；5.5节。 <br />[3] 广泛而一致地使用const；7.2节。 <br />[4] 避免宏；7.8节。
<br />[5] 避免不确定数目的参数；7.6节。 <br />[6] 不要返回局部变量的指针或者引用；7.3节。 <br />[7]
当一些函数对不同的类型执行概念上相同的工作时，请使用重载；7.4节。 <br />[8] 在各种整数上重载时，通过提供函数去消除常见的歧义性；7.4.3节。
<br />[9] 在考虑使用指向函数的指针时，请考虑虚函数（2.5.5节）或模板（2.7.2节）是不是一种更好的选择；7.7节。 <br />[10]
如果你必须使用宏，请使用带有许多大写字母的丑陋的名字；7.8节。 </p><p>第8章忠告 <br />[1] 用名字空间表示逻辑结构；8.2节。 <br />[2] 将每个非局部的名字放入某个名字空间里，除了main()之外；8.2节。
<br />[3] 名字空间的设计应该让你能很方便地使用它，而又不会意外地访问了其他的无关名字空间；8.2.4节。 <br />[4]
避免对名字空间使用很短的名字；8.2.7节。 <br />[5] 如果需要，通过名字空间别名去缓和长名字空间名的影响； 8.2.7节。 <br />[6]
避免给你的名字空间的用户添加太大的记法负担；8.2.2节、8.2.3节。 <br />[7]
在定义名字空间的成员时使用namespace::member的形式；8.2.8节。 <br />[8] 只在转换时，或者在局部作用域里，才用using
namespace；8.2.9节。 <br />[9] 利用异常去松弛"错误"处理代码和正常处理代码之间的联系；8.3.3节。 <br />[10]
采用用户定义类型作为异常，不用内部类型；8.3.2节。 <br />[11] 当局部控制结构足以应付问题时，不要用异常；8.3.3.1节。 </p><p>第9章忠告 <br />[1] 利用头文件去表示界面和强调逻辑结构；9.1节、9.3.2节。 <br />[2]
用#include将头文件包含到实现有关功能的源文件里；9.3.1节。 <br />[3]
不要在不同编译单位里定义具有同样名字，意义类似但又不同的全局实体；9.2节。 <br />[4] 避免在头文件里定义非inline函数；9.2.1节。
<br />[5] 只在全局作用域或名字空间里使用#include；9.2.1节。 <br />[6] 只用#include包含完整的定义；9.2.1节。
<br />[7] 使用包含保护符；9.3.3节。 <br />[8] 用#include将C头文件包含到名字空间里，以避免全局名字；8.2.9.1节、9.2.2节。
<br />[9] 将头文件做成自给自足的；9.2.3节。 <br />[10] 区分用户界面和实现界面；9.3.2节。 <br />[11]
区分一般用户界面和专家用户界面；9.3.2节。 <br />[12] 在有意向用于非C++程序组成部分的代码中，应避免需要运行时初始化的非局部对象；9.4.1节。
</p><p>第10章忠告 <br />[1] 用类表示概念；10.1节。 <br />[2]
只将public数据（struct）用在它实际上仅仅是数据，而且对于这些数据成员并不存在不变式的地方；10.2.8节。 <br />[3]
一个具体类型属于最简单的类。如果适用的话，就应该尽可能使用具体类型，而不要采用更复杂的类，也不要用简单的数据结构；10.3节。 <br />[4]
只将那些需要直接访问类的表示的函数作为成员函数；10.3.2节。 <br />[5] 采用名字空间，使类与其协助函数之间的关系更明确；10.3.2节。
<br />[6] 将那些不修改对象值的成员函数做成const成员函数；10.2.6节。 <br />[7]
将那些需要访问类的表示，但无须针对特定对象调用的成员函数做成static成员函数；10.2.4节。 <br />[8]
通过构造函数建立起类的不变式；10.3.1节。 <br />[9] 如果构造函数申请某种资源，析构函数就应该释放这一资源；10.4.1节。 <br />[10]
如果在一个类里有指针成员，它就需要有复制操作（包括复制构造函数和复制赋值）；10.4.4.1节。 <br />[11]
如果在一个类里有引用成员，它就可能需要有复制操作（复制构造函数和复制赋值）；10.4.6.3节。 <br />[12]
如果一个类需要复制操作或析构函数，它多半还需要有构造函数、析构函数、复制赋值和复制构造函数；10.4.4.1节。 <br />[13]
在复制赋值里需要检查自我赋值；10.4.4.1节。 <br />[14]
在写复制构造函数时，请小心地复制每个需要复制的元素（当心默认的初始式）；10.4.4.1节。 <br />[15]
在向某个类中添加新成员时，一定要仔细检查，看是否存在需要更新的用户定义构造函数，以使它能够初始化新成员；10.4.6.3节。 <br />[16]
在类声明中需要定义整型常量时，请使用枚举；10.4.6.1节。 <br />[17] 在构造全局的和名字空间的对象时，应避免顺序依赖性；10.4.9节。
<br />[18] 用第一次开关去缓和顺序依赖性问题；10.4.9节。 <br />[19]
请记住，临时对象将在建立它们的那个完整表达式结束时销毁；10.4.10节。 </p><p>第11章忠告 <br />[1] 定义运算符主要是为了模仿习惯使用方式；11.1节。 <br />[2]
对于大型运算对象，请使用const引用参数类型；11.6节。 <br />[3] 对于大型的结果，请考虑优化返回方式；11.6节。 <br />[4]
如果默认复制操作对一个类很合适，最好是直接用它；11.3.4节。 <br />[5] 如果默认复制操作对一个类不合适，重新定义它，或者禁止它；11.2.2节。
<br />[6] 对于需要访问表示的操作，优先考虑作为成员函数而不是作为非成员函数；11.5.2节。 <br />[7]
对于不访问表示的操作，优先考虑作为非成员函数而不是作为成员函数；11.5.2节。 <br />[8] 用名字空间将协助函数与"它们的"类关联起来；11.2.4节。
<br />[9] 对于对称的运算符采用非成员函数；11.3.2节。 <br />[10] 用()作为多维数组的下标；11.9节。 <br />[11]
将只有一个"大小参数"的构造函数做成explicit；11.7.1节。 <br />[12]
对于非特殊的使用，最好是用标准string（第20章）而不是你自己的练习；11.12节。 <br />[13] 要注意引进隐式转换的问题；11.4节。
<br />[14] 用成员函数表达那些需要左值作为其左运算对象的运算符；11.3.5节。 </p><p>第12章忠告 <br />[1] 避免类型域；12.2.5节。 <br />[2] 用指针和引用避免切割问题；12.2.3节。 <br />[3]
用抽象类将设计的中心集中到提供清晰的界面方面；12.3节。 <br />[4] 用抽象类使界面最小化；12.4.2节。 <br />[5]
用抽象类从界面中排除实现细节；12.4.2节。 <br />[6] 用虚函数使新的实现能够添加进来，又不会影响用户代码；12.4.1节。 <br />[7]
用抽象类去尽可能减少用户代码的重新编译；12.4.2节。 <br />[8] 用抽象类使不同的实现能够共存；12.4.3节。 <br />[9]
一个有虚函数的类应该有一个虚析构函数；12.4.2节。 <br />[10] 抽象类通常不需要构造函数；12.4.2节。 <br />[11]
让不同概念的表示也不相同；12.4.1.1节。 </p><p>第13章忠告 <br />[1] 用模板描述需要使用到许多参数类型上去的算法；13.3节。 <br />[2] 用模板表述容器；13.2节。 <br />[3]
为指针的容器提供专门化，以减小代码规模；13.5节。 <br />[4] 总是在专门化之前声明模板的一般形式；13.5节。 <br />[5]
在专门化的使用之前先声明它；13.5节。 <br />[6] 尽量减少模板定义对于实例化环境的依赖性；13.2.5节、C ..13.8节。 <br />[7]
定义你所声明的每一个专门化；13.5节。 <br />[8] 考虑一个模板是否需要有针对C风格字符串和数组的专门化；13.5.2节。 <br />[9]
用表述策略的对象进行参数化；13.4节。 <br />[10] 用专门化和重载为同一概念的针对不同类型的实现提供统一界面；13.5节。 <br />[11]
为简单情况提供简单界面，用重载和默认参数去表述不常见的情况；13.5节、13.4节。 <br />[12]
在修改为通用模板之前，在具体实例上排除程序错误；13.2.1节。 <br />[13] 如果模板定义需要在其他编译单位里访问，请记住写export；13.7节。
<br />[14] 对大模板和带有非平凡环境依赖性的模板，应采用分开编译的方式；13.7节。 <br />[15]
用模板表示转换，但要非常小心地定义这些转换；13.6.3.1节。 <br />[16]
如果需要，用constraint()成员函数给模板的实参增加限制；13.9<br />[16]，C.13.10节。 <br />[17]
通过显式实例化减少编译和连接时间；C.13.10节。 <br />[18] 如果运行时的效率非常重要，那么最好用模板而不是派生类；13.6.1节。 <br />[19]
如果增加各种变形而又不重新编译是很重要的，最好用派生类而不是模板；13.6.1节。 <br />[20]
如果无法定义公共的基类，最好用模板而不是派生类；13.6.1节。 <br />[21]
当有兼容性约束的内部类型和结构非常重要时，最好用模板而不是派生类；13.6.1节。 </p><p>第14章忠告 <br />[1] 用异常做错误处理；14.1节、14.5节、14.9节。 <br />[2]
当更局部的控制机构足以应付时，不要使用异常；14.1节。 <br />[3] 采用"资源申请即初始化"技术去管理资源；14.4节。 <br />[4]
并不是每个程序都要求具有异常时的安全性；14.4.3节。 <br />[5] 采用"资源申请即初始化"技术和异常处理器去维持不变式；14.3.2节。 <br />[6]
尽量少用try块，用"资源申请即初始化"技术，而不是显式的处理器代码；14.4节。 <br />[7] 并不是每个函数都需要处理每个可能的错误；14.9节。
<br />[8] 在构造函数里通过抛出异常指明出现失败；14.4.6节。 <br />[9] 在从赋值中抛出异常之前，使操作对象处于合法状态；14.4.6.2节。
<br />[10] 避免从析构函数里抛出异常；14.4.7节。 <br />[11] 让main()捕捉并报告所有的异常；14.7节。 <br />[12]
使正常处理代码和错误处理代码相互分离；14.4.5节、14.5节。 <br />[13]
在构造函数里抛出异常之前，应保证释放在此构造函数里申请的所有资源；14.4节。 <br />[14] 使资源管理具有层次性；14.9节。 <br />[15]
对于主要界面使用异常描述；14.9节。 <br />[16]
当心通过new分配的内存在发生异常时没有释放，并由此而导致存储的流失；14.4.1节、14.4.2节、14.4.4节。 <br />[17]
如果一函数可能抛出某个异常，就应假定它一定会抛出这个异常；14.6节。 <br />[18] 不要假定所有异常都是由exception类派生出来的；14.10节。
<br />[19] 库不应该单方面终止程序。相反，应该抛出异常，让调用者去做决定；14.1节。 <br />[20]
库不应该生成面向最终用户的错误信息。相反，它应该抛出异常，让调用者去做决定；14.1节。 <br />[21] 在设计的前期开发出一种错误处理策略；14.9节。
</p><p>第15章忠告 <br />[1] 利用常规的多重继承表述特征的合并；15.2节、15.2.5节。 <br />[2]
利用多重继承完成实现细节与界面的分离；15.2.5节。 <br />[3] 用virtual基类表达在类层次结构里对某些类（不是全部类）共同的东西；15.2.5节。
<br />[4] 避免显式的类型转换（强制）；15.4.5节。 <br />[5]
在不可避免地需要漫游类层次结构的地方，使用dymamic_cast；15.4.1节。 <br />[6]
尽量用dynamic_cast而不是typeid；15.4.4节。 <br />[7] 尽量用private而不是protected；15.3.1.1节。
<br />[8] 不要声明protected数据成员；15.3.1.1节。 <br />[9] 如果某个类定义了operator delete(
)，它也应该有虚析构函数；15.6节； <br />[10] 在构造和析构期间不要调用虚函数；15.4.3节。 <br />[11]
尽量少用为解析成员名而写的显式限定词，最好是在覆盖函数里用它；15.2.1节。 </p><p>第16章忠告 <br />[1] 利用标准库功能，以维持可移植性；16.1节。 <br />[2] 决不要另行定义标准库的功能；16.1.2节。 <br />[3]
决不要认为标准库比什么都好。 <br />[4] 在定义一种新功能时，应考虑它是否能够纳入标准库所提供的框架中；16.3节。 <br />[5]
记住标准库功能都定义在名字空间std里；16.1.2节。 <br />[6] 通过包含标准库头文件声明其功能，不要自己另行显示声明；16.1.2节。 <br />[7]
利用后续抽象的优点；16.2.1节。 <br />[8] 避免肥大的界面；16.2.2节。 <br />[9]
与自己写按照反向顺序的显式循环相比，最好是写利用反向迭代器的算法；16.3.2节。 <br />[10]
用base()从reverse_iterator抽取出iterator；16.3.2节。 <br />[11] 通过引用传递容器；16.3.4节。 <br />[12]
用迭代器类型，如list::iterator，而不要采用索引容器元素的指针；16.3.1节。 <br />[13]
在不需要修改容器元素时，使用const迭代器；16.3.1节。 <br />[14] 如果希望检查访问范围，请（直接或间接）使用at()；16.3.3节。
<br />[15] 多用容器和push_back()或resize()，少用数组和realloc()；16.3.5节。 <br />[16]
vector改变大小之后，不要使用指向其中的迭代器；16.3.8节。 <br />[17] 利用reserve()避免使迭代器非法；16.3.8节。
<br />[18] 在需要的时候，reserve()可以使执行情况更容易预期；16.3.8节。 </p><p>第17章忠告 <br />[1] 如果需要用容器，首先考虑用vector；17.1节。 <br />[2]
了解你经常使用的每个操作的代价（复杂性，大O度量）；17.1.2节。 <br />[3] 容器的界面、实现和表示是不同的概念，不要混淆；17.1.3节。
<br />[4] 你可以依据多种不同准则去排序和搜索；17.1.4.1节。 <br />[5]
不要用C风格的字符串作为关键码，除非你提供了一种适当的比较准则；17.1.4.1节。 <br />[6]
你可以定义这样的比较准则，使等价的但是不相同的关键码值映射到同一个关键码；17.1.4.1节。 <br />[7]
在插入和删除元素时，最好是使用序列末端的操作（back操作）；17.1.4.1节。 <br />[8]
当你需要在容器的前端或中间做许多插入和删除时，请用list；17.2.2节。 <br />[9]
当你主要通过关键码访问元素时，请用map或multimap；17.4.1节。 <br />[10] 尽量用最小的操作集合，以取得最大的灵活性；17.1.1节。
<br />[11] 如果要保持元素的顺序性，选用map而不是hash_map；17.6.1节。 <br />[12]
如果查找速度极其重要，选hash_map而不是map；17.6.1节。 <br />[13]
如果无法对元素定义小于操作时，选hash_map而不是map；17.6.1节。 <br />[14]
当你需要检查某个关键码是否在关联容器里的时候，用find()；17.4.1.6节。 <br />[15] 用equal_range( )
在关联容器里找出所有具有给定关键码的所有元素；17.4 ..1.6节。 <br />[16]
当具有同样关键码的多个值需要保持顺序时，用multimap；17.4.2节。 <br />[17]
当关键码本身就是你需要保存的值时，用set或multiset；17.4.3节。 </p><p>第18章忠告 <br />[1] 多用算法，少用循环；18.5节。 <br />[2] 在写循环时，考虑是否能将它表述为一个通用的算法；18.2节。
<br />[3] 常规性地重温算法集合，看看是不是能将新应用变得更明晰；18.2节。 <br />[4] 保证一对迭代器参数确实表述了一个序列；18.3.1节。
<br />[5] 设计时应该让使用最频繁的操作是简单而安全的；18.3节、18.3.1节。 <br />[6] 把测试表述成能够作为谓词使用的形式；18.4.2节。
<br />[7] 切记谓词是函数和对象，不是类型；18.4.2节。 <br />[8] 你可以用约束器从二元谓词做出一元谓词；18.4.4.1节。 <br />[9]
利用mem_fun()和mem_fun_ref()将算法应用于容器；18.4.4.2节。 <br />[10]
当你需要将一个参数约束到一个函数上时，用ptr_fun()；18.4.4.3节。 <br />[11]
切记strcmp()用0表示"相等"，与==不同；18.4.4.4节。 <br />[12]
仅在没有更特殊的算法时，才使用for_each()和tranform()；18.5.1节。 <br />[13]
利用谓词，以便能以各种比较准则和相等准则使用算法；18.4.2.1节、18.6.3.1节。 <br />[14]
利用谓词和其他函数对象，以使标准算法能用于表示范围广泛的意义；18.4.2节。 <br />[15]
运算符&lt;和==在指针上的默认意义很少适用于标准算法；18.6.3.1节。 <br />[16] 算法并不直接为它们的参数序列增加或减少元素；18.6节。
<br />[17] 应保证用于同一个序列的小于和相等谓词相互匹配；18.6.3.1节。 <br />[18] 有时排好序的序列用起来更有效且优雅；18.7节。
<br />[19] 仅为兼容性而使用qsort()和bsearch()；18.11节。 </p><p>第19章忠告 <br />[1]
在写一个算法时，设法确定需要用哪种迭代器才能提供可接受的效率，并（只）使用这种迭代器所支持的操作符去表述算法；19.2.1节。 <br />[2]
当给定的迭代器参数提供了多于算法所需的最小支持时，请通过重载为该算法提供效率更高的实现；19.2.3节。 <br />[3]
利用iterator_traits为不同迭代器类别描述适当的算法；19.2.2节。 <br />[4]
记住在istream_iterator和ostream_iterator的访问之间使用++；19.2.6节。 <br />[5]
用插入器避免容器溢出；19.2.4节。 <br />[6] 在排错时使用额外的检查，后面只在必须时才删除这些检查；19.3.1节。 <br />[7]
多用++p，少用p++；19.3节。 <br />[8] 使用未初始化的存储去改善那些扩展数据结构的算法的性能；19.4.4节。 <br />[9]
使用临时缓冲区去改善需要临时数据结构的算法的性能；19.4.4节。 <br />[10] 在写自己的分配器之前三思；19.4节。 <br />[11]
避免malloc()、free()、realloc()等；19.4.6节。 <br />[12]
你可以通过为rebind所用的技术去模拟对模板的typedef；19.4.1节。 </p><p>第20章忠告 <br />[1] 尽量使用string操作，少用C风格字符串函数；20.4.1节。 <br />[2]
用string作为变量或者成员，不作为基类；20.3节、25.2.1节。 <br />[3]
你可以将string作为参数值或者返回值，让系统去关心存储管理问题；20.3.6节。 <br />[4]
当你希望做范围检查时，请用at()而不是迭代器或者[]；20.3.2节、20.3.5节。 <br />[5]
当你希望优化速度时，请用迭代器或[]而不是at()；20.3.2节、20.3.5节。 <br />[6]
直接或者间接地使用substr()去读子串，用replace()去写子串；20.3.12节、20.3.13节。 <br />[7]
用find()操作在string里确定值的位置（而不是写一个显式的循环）；20.3.11节。 <br />[8]
在你需要高效率地添加字符时，请在string的后面附加；20.3.9节。 <br />[9]
在没有极端时间要求情况下用string作为字符输入的目标；20.3.15节。 <br />[10]
用string::npos表示"string的剩余部分"；20.3.5节。 <br />[11]
如果必要，就采用低级操作去实现极度频繁使用的string（而不是到处用低级数据结构）；20.3.10节。 <br />[12]
如果你使用string，请在某些地方捕捉length_error和out_of_range异常；20.3.5节。 <br />[13]
小心，不要将带值0的char*传递给字符串函数；20.3.7节。 <br />[14]
只是到必须做的时候，（再）用c_str()产生string的C风格表示；20.3.7节。 <br />[15]
当你需要知道字符的类别时，用isalpha()、isdigit()等函数，不要自己去写对字符值的检测；20.4.1节。 </p><p>第21章忠告 <br />[1]
在为用户定义类型的值定义&lt;&lt;和&gt;&gt;时，应该采用意义清晰的正文表示形式；21.2.3节、21.3.5节。 <br />[2]
在打印包含低优先级运算符的表达式时需要使用括号；21.2节。 <br />[3]
在添加新的&lt;&lt;和&gt;&gt;运算符时，你不必修改istream或ostream；21.2.3节。 <br />[4]
你可以定义函数，使其能基于第二个（或更后面的）参数，具有像virtual函数那样行为；21.2.3.1节。 <br />[5]
切记，按默认约定&gt;&gt;跳过所有空格；21.3.2节。 <br />[6]
使用低级输入函数（如get()和read()）主要是为了实现高级输入函数；21.3.4节。 <br />[7]
在使用get()、getline()和read()时留心其终止准则；21.3.4节。 <br />[8]
在控制I/O时，尽量采用操控符，少用状态标志；21.3.3节、21.4节、21.4.6节。 <br />[9] （只）用异常去捕捉罕见的I/O错误；21.3.6节。
<br />[10] 联结用于交互式I/O的流；21.3.7节。 <br />[11] 使用哨位将许多函数的入口和出口代码集中到一个地方；21.3.8节。
<br />[12] 在无参数操控符最后不要写括号；21.4.6.2节。 <br />[13] 使用标准操控符时应记住写#include ；21.4.6.2节。
<br />[14] 你可以通过定义一个简单函数对象得到三元运算符的效果（和效率）；21.4.6.3节。 <br />[15]
切记，width描述只应用于随后的一个I/O操作；21.4.4节。 <br />[16]
切记precision描述只对随后所有的浮点数输出操作有效；21.4.3节。 <br />[17] 用字符串流做内存里的格式化；21.5.3节。 <br />[18]
你可以描述一个文件流的模式；21.5.1节。 <br />[19]
在扩充I/O系统时，应该清楚地区分格式化（iostream）和缓冲（streambuf）；21.1节、21.6节。 <br />[20]
将传输值的非标准方式实现为流缓冲区；21.6.4节。 <br />[21] 将格式化值的非标准方式实现为流操作；21.2.3节、21.3.5节。 <br />[22]
你可以利用一对函数隔离和封装起对用户定义代码的调用；21.6.4节。 <br />[23]
你可以在读入之前用in_avail()去确定输入操作是否会被阻塞；21.6.4节。 <br />[24]
划分清楚需要高效的简单操作和实现某种策略的操作（将前者做成inline，将后者做成virtual）；21.6.4节。 <br />[25]
用locale将"文化差异"局部化；21.7节。 <br />[26]
用sync_with_stdio(x)去混合C风格和C++风格的I/O，或者离解C风格和C++风格的I/O；21.8节。 <br />[27]
当心C风格I/O的类型错误；21.8节。 </p><p>第22章忠告 <br />[1] 数值问题常常很微妙。如果你对数值问题的数学方面不是100 %有把握，请去找专家或者做试验；22.1节。 <br />[2]
用numeric_limits去确定内部类型的性质；22.2节。 <br />[3] 为用户定义的标量类型描述numeric_limits；22.2节。
<br />[4] 如果运行时效率比对于操作和元素的灵活性更重要的话，那么请用valarray去做数值计算；22.4节。 <br />[5]
用切割表述在数组的一部分上的操作，而不是用循环；22.4.6节。 <br />[6] 利用组合器，通过清除临时量和更好的算法来获得效率；22.4.7节。
<br />[7] 用std::complex做复数算术；22.5节。 <br />[8]
你可以把使用complex类的老代码通过一个typedef转为用std::complex模板；22.5节。 <br />[9]
在写循环从一个表出发计算某个值之前，先考虑一下accumulate()、inner_product()、partial_sum()和adjacent_difference()；22.6节。
<br />[10] 最好是用具有特定分布的随机数类，少直接用rand()；22.7节。 <br />[11] 注意使你的随机数充分随机；22.7节。 </p><p>第23章忠告 <br />[1] 知道你试图达到什么目的；23.3节。 <br />[2] 心中牢记软件开发是一项人的活动；23.2节、23.5.3节。
<br />[3] 用类比来证明是有意的欺骗；23.2节。 <br />[4] 保持一个特定的实实在在的目标；23.4节。 <br />[5]
不要试图用技术方式去解决社会问题；23.4节。 <br />[6] 在设计和对待人员方面都应该有长期考虑；23.4.1节、23.5.3节。 <br />[7]
对于什么程序在编码之前先行设计是有意义的，在程序规模上并没有下限；23.2节。 <br />[8] 设计过程应鼓励反馈；23.4节。 <br />[9]
不要将做事情都当做取得了进展；23.3节、23.4节。 <br />[10]
不要推广到超出了所需要的、你已有直接经验的和已经测试过的东西；23.4.1节、23.4.2节。 <br />[11]
将概念表述为类；23.4.2节、23.4.3.1节。 <br />[12] 系统里也存在一些不应该用类表述的性质；23.4.3.1节。 <br />[13]
将概念间的层次关系用类层次结构表示；23.4.3.1节 <br />[14]
主动到应用和实现中去寻找概念间的共性，将由此得到的一般性概念表示为基类；23.4.3.1节、23.4.3.5节。 <br />[15]
在其他领域中的分类方式未必适合作为应用中的继承模型的分类方式；23.4.3.1节。 <br />[16]
基于行为和不变式设计类层次结构；23.4.3.1节、23.4.3.5节、23.4.3.7.1节。 <br />[17] 考虑用例；23.4.3.1节。
<br />[18] 考虑使用CRC卡片；23.4.3.1节。 <br />[19] 用现存系统作为模型、灵感的源泉和出发点；23.4.3.6节。 <br />[20]
意识到视觉图形工程的重要性；23.4.3.1节。 <br />[21] 在原型成为负担时就抛弃它；23.4.4节。 <br />[22]
为变化而设计，将注意力集中到灵活性、可扩展性、可移植性和重用；23.4.2节。 <br />[23] 将注意力集中到组件设计；23.4.3节。 <br />[24]
让每个界面代表在一个抽象层次中的一个概念；23.4.3.1节。 <br />[25] 面向变化进行设计，以求得稳定性；23.4.2节。 <br />[26]
通过将广泛频繁使用的界面做得最小、最一般和抽象来使设计稳定；23.4.3.2、23.4.3.5节。 <br />[27]
保持尽可能小，不为"特殊需要"增加新特征；23.4.3.2节。 <br />[28]
总考虑类的其他表示方式。如果不可能有其他方式，这个类可能就没有代表某个清晰的概念；23.4.3.4节。 <br />[29]
反复评审、精化设计和实现；23.4节、23.4.3节。 <br />[30]
采用那些能用于调试，用于分析问题、设计和实现的最好工具；23.3节、23.4.1节、23.4.4节。 <br />[31]
尽早、尽可能频繁地进行试验、分析和测试；23.4.4节、23.4.5节。 <br />[32] 不要忘记效率；23.4.7节。 <br />[33]
保持某种适合项目规模的规范性水平；23.5.2节。 <br />[34] 保证有人负责项目的整体设计；23.5.2节。 <br />[35]
为可重用组件做文档、推介和提供支持；23.5.1节。 <br />[36] 将目标与细节一起写进文档里；23.4.6节。 <br />[37]
将为新开发者提供的教学材料作为文档的一部分；23.4.6节。 <br />[38] 鼓励设计、库和类的重用，并给予回报；23.5.1节。 </p><p>第24章忠告 <br />[1] 应该向数据抽象和面向对象程序设计的方向发展；24.2节。 <br />[2]
（仅仅）根据需要去使用C++的特征和技术；24.2节。 <br />[3] 设计应与编程风格相互匹配；24.2.1节。 <br />[4]
将类/概念作为设计中最基本的关注点，而不是功能/处理；24.2.1节。 <br />[5] 用类表示概念；24.2.1节、24.3节。 <br />[6]
用继承（仅仅）表示概念间的层次结构关系；24.2.2节、24.5.2节、24.3.2节。 <br />[7]
利用应用层静态类型的方式给出有关界面的更强的保证；24.2.2节。 <br />[8] 使用程序生成器和直接界面操作工具去完成定义良好的工作；24.2.3节。
<br />[9] 不要去使用那些与任何通用程序设计语言之间都没有清晰界面的程序生成器或者直接界面操作工具；24.2.4节。 <br />[10]
保持不同层次的抽象相互分离；24.3.1节。 <br />[11] 关注组件设计；24.4节。 <br />[12]
保证虚函数有定义良好的意义，每个覆盖函数都实现预期行为；24.3.4节、24.3.2.1节。 <br />[13]
公用界面继承表示的是"是一个"关系；24.3.4节。 <br />[14] 成员表示的是"有一个"关系；24.3.4节。 <br />[15]
在表示简单包容时最好用直接成员，不用指向单独分配的对象的指针；24.3.3节、24.3.4节。 <br />[16]
设法保证使用依赖关系为易理解的，尽可能不出现循环，而且最小；24.3.5节。 <br />[17] 对于所有的类，定义好不变式；24.3.7.1节。
<br />[18] 显式地将前条件、后条件和其他断言表述为断言（可能使用Assert()）；24.3.5节。 <br />[19]
定义的界面应该只暴露出尽可能少的信息；24.4节。 <br />[20] 尽可能减少一个界面对其他界面的依赖性；24.4.2节。 <br />[21]
保持界面为强类型的；24.4.2节。 <br />[22] 利用应用层的类型来表述界面；24.4.2节。 <br />[23]
将界面表述得使请求可以传递给远程的服务器；24.4.2节。 <br />[24] 避免肥大的界面；24.4.3节。 <br />[25]
尽可能地使用private数据和成员函数；24.4.2节。 <br />[26]
用protected/public区分开派生类的设计者与一般用户间的不同需要；24.4.2节。 <br />[27] 使用模板去做通用型程序设计；24.4.1节。
<br />[28] 使用模板去做算法策略的参数化；24.4.1节。 <br />[29] 如果需要在编译时做类型解析，请使用模板；24.4.1节。 <br />[30]
如果需要在运行时做类型解析，使用类层次结构；24.4.1节。 </p><p>第25章忠告 <br />[1] 应该对一个类的使用方式做出有意识的决策（作为设计师或者作为用户）；25.1节。 <br />[2]
应注意到涉及不同种类的类之间的权衡问题；25.1节。 <br />[3] 用具体类型去表示简单的独立概念；25.2节。 <br />[4]
用具体类型去表示那些最佳效率极其关键的概念；25.2节。 <br />[5] 不要从具体类派生；25.2节。 <br />[6]
用抽象类去表示那些对象的表示可能变化的界面；25.3节。 <br />[7] 用抽象类去表示那些可能出现多种对象表示共存情况的界面；25.3节。 <br />[8]
用抽象类去表示现存类型的新界面；25.3节。 <br />[9] 当类似概念共享许多实现细节时，应该使用结点类；25.4节。 <br />[10]
用结点类去逐步扩充一个实现；25.4节。 <br />[11] 用运行时类型识别从对象获取界面；25.4.1节。 <br />[12]
用类去表示具有与之关联的状态信息的动作；25.5节。 <br />[13] 用类去表示需要存储、传递或者延迟执行的动作；25.5节。 <br />[14]
利用界面类去为某种新的用法而调整一个类（不修改这个类）；25.6节。 <br />[15] 利用界面类增加检查，25.6节。 <br />[16]
利用句柄去避免直接使用指针和引用；25.7节。 <br />[17] 利用句柄去管理共享的表示；25.7节。 <br />[18]
在那些能预先定义控制结构的应用领域中使用应用框架；25.8节。 </p><p>附录B 忠告 <br />[1] 要学习C++，应该使用你可以得到的标准C++的最新的和完全的实现； B.3节。 <br />[2]
C和C++的公共子集并不是学习C++时最好的开始子集；1.6节、B.3节。 <br />[3]
对于产品代码，请记住并不是每个C++实现都是完全的最新的。在产品代码中使用某个新特征之前应先做试验，写一个小程序，测试你计划使用的实现与标准的相符情况和性能。例如，参见8.5[6~7]、16.5[10]
和B.5[7]。 <br />[4] 避免被贬斥的特征，例如全局的static；还应避免C风格的强制；6.2.7节、B.2.3节。
<br />[5]"隐含的int"已禁止，因此请明确描述每个函数、变量、const等的类型；B.2.2节。 <br />[6]
在将C程序转为C++程序时，首先保证函数声明（原型）和标准头文件的一致使用；B.2.2节。 <br />[7]
在将C程序转为C++程序时，对以C++关键字为名的变量重新命名；B.2.2节。 <br />[8]
在将C程序转为C++程序时，将malloc()的结果强制到适当类型，或者将malloc()的所有使用都改为new；B.2.2节。 <br />[9]
在将malloc()和free()转为new和delete时，请考虑用vector、push_back()和reserve()而不是realloc()；3.8节、16.3.5节。
<br />[10] 在将C程序转为C++程序时，记住这里没有从int到枚举的隐式转换；如果需要，请用显式转换；4.8节。 <br />[11]
在名字空间std里定义的功能都定义在无后缀的头文件里（例如，std::cout声明在里）。早些的实现将标准库功能定义在全局空间里，声明在带.h后缀的头文件里（例如，std::cout声明在里）；9.2.2节、B.3.1节。
<br />[12] 如果老的代码检测new的结果是否为0，那么必须将它修改为捕捉bad_alloc或者使用new(nothrow)；B.3.4节。
<br />[13]
如果你用的实现不支持默认模板参数，请显式提供参数；用typedef可以避免重复写模板参数（类似于string的typedef使你无须写basic_string,
allocator &gt;）；B.3.5节。 <br />[14] 用得到std::string（里保存的是C风格的串函数）；9.2.2节、B.3.1节。
<br />[15] 对每个标准C头文件，它将名字放入全局名字空间；与之对应的头文件将名字放入名字空间std；B.3.1节。 <br />[16]
许多系统有一个"String.h"头文件里定义了一个串类型。注意，这个串类型与标准库的string不同。 <br />[17]
尽可能使用标准库功能，而不是非标准的功能；20.1节、B.3节、C.2节。 <br />[18] 在声明C函数时用extern "C"；9.2.4节 </p><p>附录C 忠告 <br />[1] 应集中关注软件开发而不是技术细节；C.1节。 <br />[2] 坚持标准并不能保证可移植性；C.2节。 <br />[3]
避免无定义行为（包括专有的扩充）；C.2节。 <br />[4] 将那些实现定义的行为局部化；C.2节。 <br />[5]
在没有{、}、[、]、|或!的系统里用关键字和二联符表示程序，在没有\的地方用三联符；C.3.1节。 <br />[6]
为了方便通信，用ASCII字符去表示程序；C.3.3节。 <br />[7] 采用符号转义字符比用数值表示字符更好些；C.3.2节。 <br />[8]
不要依赖于char的有符号或者无符号性质；C.3.4节。 <br />[9] 如果对整数文字量的类型感到有疑问，请使用后缀；C.4节。 <br />[10]
避免破坏值的隐式转换；C.6节。 <br />[11] 用vector比数组好；C.7节。 <br />[12] 避免union；C.8.2节。 <br />[13]
用位域表示外部确定的布局；C.8.1节。 <br />[14] 注意不同存储管理风格间的权衡；C.9节。 <br />[15] 不要污染全局名字空间；C.10.1节。
<br />[16] 在需要作用域（模块）而不是类型的地方，用namespace比class更合适；C.10.3节。 <br />[17]
记住static类成员需要定义；C.13.1节。 <br />[18] 用typename消除对模板参数中类型成员的歧义性；C.13.5节。 <br />[19]
在需要用模板参数显式限定之处，用template消除模板类成员的歧义性；C.13.6节。 <br />[20]
写模板定义时，应尽可能减少对实例化环境的依赖性；C.13.8节。 <br />[21] 如果模板实例化花的时间过长，请考虑显式实例化；C.13 ..10节。
<br />[22] 如果需要编译顺序的显式可预见性，请考虑显式实例化；C.13.10节。 </p><p>附录D 忠告 <br />[1] 应预期每个直接与人打交道的非平凡程序或者系统都会用在多个国家；D.1节。 <br />[2]
不要假定每个人使用的都是你所用的字符集；D.4.1节。 <br />[3] 最好是用locale而不是写实质性代码去做对文化敏感的I/O；D.1节。 <br />[4]
避免将现场名字字符串嵌入到程序正文里；D.2.1节。 <br />[5] 尽可能减少全局格式信息的使用；D.2.3节、D.4.4.7节。 <br />[6]
最好是用与现场有关的字符串比较和排序；D.2.4节、D.4.1节。 <br />[7] 保存facet的不变性；D.2.2节、D.3节。 <br />[8]
应保证改变现场的情况只出现在程序里的几个地方；D.2.3节。 <br />[9] 利用现场去管理刻面的生存期；D.3节。 <br />[10]
在写对现场敏感的I/O函数时，记住去处理用户（通过覆盖）提供的函数所抛出的异常；D.4.2.2节。 <br />[11]
用简单的Money类型保存货币值；D.4.3节。 <br />[12] 要做对现场敏感的I /
O，最好是用简单的用户定义类型保存所需的值（而不是从内部类型的值强制转换）；D.4.3节。 <br />[13]
在你对涉及到的所有因素有了很好的看法之前，不要相信计时结果；D.4.4.1节。 <br />[14]
当心time_t的取值范围；D.4.4.1节、D.4.4.5节。 <br />[15] 使用能接受多种输入格式的日期输入例程；D.4.4.5节。 <br />[16]
最好采用那些明显表明了所用现场的字符分类函数；D.4.5节、D.4.5.1节。 </p><p>附录E忠告 <br />[1] 弄清楚你想要什么级别的异常时安全性；E.2节。 <br />[2] 异常时安全性应该是整体容错策略的一部分；E.2节。
<br />[3] 为所有的类提供基本保证，也就是说，维持一个不变式，而且不流失资源；E.2节、E.3.2节、E.4节。 <br />[4]
在可能和可以负担之处提供强保证，使操作或者成功，或者保持所有操作对象不变；E.2节、E.3节。 <br />[5]
不要从析构函数里抛出异常；E.2节、E.3.2节、E.4节。 <br />[6] 不要从一个遍历合法序列的迭代器里抛出异常；E.4.1节、E.4.4节。
<br />[7] 异常时安全性涉及到仔细检查各个操作；E.3节。 <br />[8] 将模板设计为对异常透明的；E.3.1节。 <br />[9]
更应该用申请资源的构造函数方式，不要采用init()函数；E.3.5节。 <br />[10]
为类定义一个不变式，使什么是合法状态变得非常清晰；E.2节、E.6节。 <br />[11] 确保总将对象放在合法状态中，也不要怕抛出异常；E.3.2节、E.6节。
<br />[12] 保持不变式简单；E.3.5节。 <br />[13] 在抛出异常之前，让所有操作对象都处于合法状态；E.2节、E.6节。 <br />[14]
避免资源流失；E.2节、E.3.1节、E.6节。 <br />[15] 直接表示资源；E.3.2节、E.6节。 <br />[16]
记住swap()有时可以成为复制元素的替代方式；E.3.3节。 <br />[17] 在可能时依靠操作的顺序，而不是显式地使用try块；E.3.4节。
<br />[18] 在替代物已经安全生成之前不销毁"老"信息；E.3.3节、E.6节。 <br />[19]
依靠"资源申请即初始化"技术；E.3节、E.3.2节、E.6节。 <br />[20] 确保关联容器的比较操作能够复制；E.3.3节。 <br />[21]
标明关键性数据结构，并为它们定义能够提供强保证的操作；E.6节。 </p><img src ="http://www.cnitblog.com/martin/aggbug/9447.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/martin/" target="_blank">Martin</a> 2006-04-21 12:17 <a href="http://www.cnitblog.com/martin/archive/2006/04/21/9447.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>