﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-Thinking in C++-文章分类-C++</title><link>http://www.cppblog.com/yishanhante/category/3660.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 10 Jul 2009 13:59:42 GMT</lastBuildDate><pubDate>Fri, 10 Jul 2009 13:59:42 GMT</pubDate><ttl>60</ttl><item><title>toi，atol，strtod，strtol，strtoul实现类型转换</title><link>http://www.cppblog.com/yishanhante/articles/89642.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Thu, 09 Jul 2009 06:07:00 GMT</pubDate><guid>http://www.cppblog.com/yishanhante/articles/89642.html</guid><wfw:comment>http://www.cppblog.com/yishanhante/comments/89642.html</wfw:comment><comments>http://www.cppblog.com/yishanhante/articles/89642.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yishanhante/comments/commentRss/89642.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yishanhante/services/trackbacks/89642.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 版权声明：转载时请以超链接形式标明文章原始出处和作者信息及本声明http://ivanvic.blogbus.com/logs/1920125.htmlatof（将字符串转换成浮点型数）相关函数atoi，atol，strtod，strtol，strtoul表头文件#include&nbsp;定义函数double atof(const char *nptr);函数说明atof()会扫描参数nptr字...&nbsp;&nbsp;<a href='http://www.cppblog.com/yishanhante/articles/89642.html'>阅读全文</a><img src ="http://www.cppblog.com/yishanhante/aggbug/89642.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yishanhante/" target="_blank">jay</a> 2009-07-09 14:07 <a href="http://www.cppblog.com/yishanhante/articles/89642.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CString,string,char*的综合比较</title><link>http://www.cppblog.com/yishanhante/articles/26258.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Wed, 13 Jun 2007 08:13:00 GMT</pubDate><guid>http://www.cppblog.com/yishanhante/articles/26258.html</guid><wfw:comment>http://www.cppblog.com/yishanhante/comments/26258.html</wfw:comment><comments>http://www.cppblog.com/yishanhante/articles/26258.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yishanhante/comments/commentRss/26258.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yishanhante/services/trackbacks/26258.html</trackback:ping><description><![CDATA[<p><strong>(一) 概述</strong><br><br>string和CString均是字符串模板类，string为标准模板类（STL）定义的字符串类，已经纳入C++标准之中；<br><br>CString（typedef CStringT&lt;TCHAR, StrTraitMFC&lt;TCHAR&gt;&gt; CString）为Visual C++中最常用的字符串类，继承自CSimpleStringT类，主要应用在MFC和ATL编程中，主要数据类型有char(应用于ANSI)，wchar_t(unicode)，TCHAR(ANSI与unicode均可)；<br><br>char*为C编程中最常用的字符串指针，一般以&#8217;\0&#8217;为结束标志；<br><br><strong>(二) 构造</strong><br><br>&nbsp;string是方便的，可以从几乎所有的字符串构造而来，包括CString和char*；<br><br>&nbsp;CString次之，可以从基本的一些字符串变量构造而来，包括char*等；<br><br>&nbsp;char*没有构造函数，仅可以赋值；<br><br>&nbsp;举例：<br><br>char* psz = &#8220;joise&#8221;;<br><br>CString cstr( psz );<br><br>string str( cstr );<br><br><strong>(三) 运算符重载</strong><br><br>a) operator=<br><br>&nbsp;string是最方便的，几乎可以直接用所有的字符串赋值，包括CString和char*；<br><br>&nbsp;CString次之，可以直接用些基本的字符串赋值，包括char*等；<br><br>&nbsp;char*只能由指针赋值，并且是极危险的操作，建议使用strcpy或者memcpy，而且char*在声明的时候如未赋初值建议先设为NULL，以避免野指针，令你抓狂；<br><br>&nbsp;举例：<br><br>char *psz = NULL;<br><br>psz = new char[10]; //当然，以上的直接写成char *psz = new char[10];也是一样<br><br>memset( psz, 0, 10 );<br><br>strcpy( psz, &#8220;joise&#8221; );&nbsp;<br><br>CString cstr;<br><br>cstr = psz;<br><br>string str;<br><br>str = psz;<br><br>str = cstr;<br><br>delete []psz;<br><br>b) operator+<br><br>string与CString差不多，可以直接与char*进行加法，但不可以相互使用+运算符，即string str = str + cstr是非法的，须转换成char*；<br><br>char*没有+运算，只能使用strcat把两个指针连在一起；<br><br>&nbsp;举例：<br><br>char* psz = &#8220;joise&#8221;;<br><br>CString cstr = psz;<br><br>cstr = cstr + psz;<br><br>string str = psz;<br><br>str = str + str + psz;<br><br>strcat( psz, psz );<br><br>strcat( psz, cstr );//合法<br><br>strcat( psz, str );//非法，由此可见，CString可自动转换为const char*，而string不行<br><br>c) operator +=<br><br>&nbsp;string是最强大的，几乎可以与所有的字符串变量+=，包括CString和char*；<br><br>&nbsp;CString次之，可以与基本的一些字符串变量进行+=而来，包括char*等；<br><br>char*没有+=运算符，只能使用strcat把两个指针连在一起；<br><br>d) operator[]<br><br>&nbsp;CString最好，当越界时会抛出断言异常；<br><br>&nbsp;string与char*下标越界结果未定义；<br><br>举例：<br><br>char* psz = &#8220;joise&#8221;;<br><br>CString cstr = psz;<br><br>cout &lt;&lt; cstr[8];<br><br>string str = psz;<br><br>cout &lt;&lt; str[8];<br><br>cout &lt;&lt; psz[8];<br><br>e) operator== 、operator!=、operator&gt; 、operator&lt; 、operator&gt;= 、perator&lt;=<br><br>&nbsp;CString与string之间不可以进行比较，但均可以与char*进行比较，并且比较的是值，而不是地址；<br><br>cout &lt;&lt; ( psz == cstr );<br><br>cout &lt;&lt; ( psz == str );<br><br>cout &lt;&lt; ( str == psz );<br><br>cout &lt;&lt; ( cstr == psz );//以上代码返回均为1<br><br><strong>(四) 常用算法</strong><br><br>a) 查找</p>
<table cellSpacing=1 width="70%" border=1>
    <tbody>
        <tr>
            <td width="25%">作用</td>
            <td width="25%">char*</td>
            <td width="25%">string</td>
            <td width="25%">CString<br></td>
        </tr>
        <tr>
            <td width="25%">查找指定值</td>
            <td width="25%">strchr<br>strstr<br>strrstr<br>strspn</td>
            <td width="25%">find</td>
            <td width="25%">Find<br></td>
        </tr>
        <tr>
            <td width="25%">第一个匹配的值</td>
            <td width="25%">　</td>
            <td width="25%">fild_first_of</td>
            <td width="25%">FindOneOf</td>
        </tr>
        <tr>
            <td width="25%">从后面开始查找</td>
            <td width="25%">　</td>
            <td width="25%">　</td>
            <td width="25%">ReserveFind</td>
        </tr>
        <tr>
            <td width="25%">指定匹配方式</td>
            <td width="25%">　</td>
            <td width="25%">find_if</td>
            <td width="25%">　</td>
        </tr>
    </tbody>
</table>
<p>注：find_if中是把范围内的值挨个代入匹配函数直至返回true<br><br>b) 比较<br></p>
<table width="71%" border=1>
    <tbody>
        <tr>
            <td width="25%">作用</td>
            <td width="25%">char*</td>
            <td width="25%">string</td>
            <td width="25%">CString</td>
        </tr>
        <tr>
            <td width="25%">查找指定值(区分大小写)</td>
            <td width="25%">strcmp<br>strncmp<br>strcoll<br>_strncoll</td>
            <td width="25%">operator&lt;<br>operator&gt;<br>operator&lt;=&nbsp;<br>operator&gt;=<br>operator==<br>operator!=</td>
            <td width="25%">Collate<br><br>Compare</td>
        </tr>
        <tr>
            <td width="25%">查找指定值(不区分大小写)</td>
            <td width="25%">_stricmp<br>_strnicmp<br>_stricoll<br>_strnicoll</td>
            <td width="25%">　</td>
            <td width="25%">CollateNoCase<br><br>CompareNoCas</td>
        </tr>
    </tbody>
</table>
<p>注：返回值如果&lt;0则前面的值小于后面的值，反之亦然<br><br>c) 替换</p>
<table width="72%" border=1>
    <tbody>
        <tr>
            <td width="25%">作用</td>
            <td width="25%">char*</td>
            <td width="25%">string</td>
            <td width="25%">CString</td>
        </tr>
        <tr>
            <td width="25%">查找指定值</td>
            <td width="25%">_strset<br>_strnset<br></td>
            <td width="25%">replace<br>replace_copy<br>replace_copy_if<br>replace_if</td>
            <td width="25%">Replace</td>
        </tr>
    </tbody>
</table>
<p>d) 插入</p>
<table width="73%" border=1>
    <tbody>
        <tr>
            <td width="25%">作用</td>
            <td width="25%">char*</td>
            <td width="25%">string</td>
            <td width="25%">CString</td>
        </tr>
        <tr>
            <td width="25%">查找指定值</td>
            <td width="25%">　</td>
            <td width="25%">insert</td>
            <td width="25%">Insert</td>
        </tr>
    </tbody>
</table>
<br>e) 增加
<table width="61%" border=1>
    <tbody>
        <tr>
            <td width="25%">作用</td>
            <td width="25%">char*</td>
            <td width="25%">string</td>
            <td width="25%">CString</td>
        </tr>
        <tr>
            <td width="25%">动态增加值</td>
            <td width="25%">strcat</td>
            <td width="25%">push<br><br>append</td>
            <td width="25%">Append<br><br>AppendChar<br><br>AppendFormat</td>
        </tr>
    </tbody>
</table>
<p><br>f) 截取</p>
<table width="61%" border=1>
    <tbody>
        <tr>
            <td width="25%">作用</td>
            <td width="25%">char*</td>
            <td width="25%">string</td>
            <td width="25%">CString</td>
        </tr>
        <tr>
            <td width="25%">得到部分值</td>
            <td width="25%">用下标操作</td>
            <td width="25%">substr</td>
            <td width="25%">Left<br><br>Mid<br><br>Right<br><br>Truncate</td>
        </tr>
    </tbody>
</table>
<p><br>g) 移除</p>
<table width="62%" border=1>
    <tbody>
        <tr>
            <td width="25%">作用</td>
            <td width="25%">char*</td>
            <td width="25%">string</td>
            <td width="25%">CString</td>
        </tr>
        <tr>
            <td width="25%">移除部份值</td>
            <td width="25%">　</td>
            <td width="25%">remove</td>
            <td width="25%">Remove</td>
        </tr>
        <tr>
            <td width="25%">移除空白值</td>
            <td width="25%">RemoveBlanks<br><br>注：此为ATL提供，非C函数</td>
            <td width="25%">remove_if</td>
            <td width="25%">Trim<br><br>TrimLeft<br><br>TrimRig</td>
        </tr>
    </tbody>
</table>
<p><br>h) 转换大小写</p>
<table width="62%" border=1>
    <tbody>
        <tr>
            <td width="25%">作用</td>
            <td width="25%">char*</td>
            <td width="25%">string</td>
            <td width="25%">CString</td>
        </tr>
        <tr>
            <td width="25%">转换大小写</td>
            <td width="25%">_strlwr<br><br>_strupr</td>
            <td width="25%">　</td>
            <td width="25%">MakeLower<br><br>MakeUpper</td>
        </tr>
    </tbody>
</table>
<p><br>i) 与其他类型转换</p>
<table width="62%" border=1>
    <tbody>
        <tr>
            <td width="25%">作用</td>
            <td width="25%">char*</td>
            <td width="25%">string</td>
            <td width="25%">CString</td>
        </tr>
        <tr>
            <td width="25%">转化为数字</td>
            <td width="25%">atoi<br><br>atod<br><br>atof</td>
            <td width="25%">　</td>
            <td width="25%">Format</td>
        </tr>
        <tr>
            <td width="25%">转化为char*</td>
            <td width="25%">　</td>
            <td width="25%">c_str</td>
            <td width="25%"><br>GetBuffer<br><br>GetBufferSetLen</td>
        </tr>
    </tbody>
</table>
<p>j) 格式化</p>
<table width="62%" border=1>
    <tbody>
        <tr>
            <td width="25%">作用</td>
            <td width="25%">char*</td>
            <td width="25%">string</td>
            <td width="25%">CString</td>
        </tr>
        <tr>
            <td width="25%">格式化</td>
            <td width="25%">sprintf</td>
            <td width="25%">　</td>
            <td width="25%">Format<br></td>
        </tr>
    </tbody>
</table>
<p><br>k) 得到长度</p>
<table width="62%" border=1>
    <tbody>
        <tr>
            <td width="25%">作用</td>
            <td width="25%">char*</td>
            <td width="25%">string</td>
            <td width="25%">CString<br></td>
        </tr>
        <tr>
            <td width="25%">得到长度</td>
            <td width="25%">strlen</td>
            <td width="25%">length</td>
            <td width="25%">GetLength</td>
        </tr>
        <tr>
            <td width="25%">得到大小</td>
            <td width="25%">　</td>
            <td width="25%">size</td>
            <td width="25%">GetAllocLength</td>
        </tr>
    </tbody>
</table>
<p>l) 判断为空</p>
<table width="62%" border=1>
    <tbody>
        <tr>
            <td width="25%">作用</td>
            <td width="25%">char*</td>
            <td width="25%">string</td>
            <td width="25%">CString</td>
        </tr>
        <tr>
            <td width="25%">判断是否为空</td>
            <td width="25%">判断是否==NULL或者第一个字符是否是&#8217;\0&#8217;</td>
            <td width="25%">empty</td>
            <td width="25%">IsEmpty</td>
        </tr>
    </tbody>
</table>
<p>m) 重定义大小</p>
<table width="62%" border=1>
    <tbody>
        <tr>
            <td width="25%">作用</td>
            <td width="25%">char*</td>
            <td width="25%">string</td>
            <td width="25%">CString</td>
        </tr>
        <tr>
            <td width="25%">重定义大小</td>
            <td width="25%">realloc<br>new</td>
            <td width="25%">resize</td>
            <td width="25%">GetBufferSetLength</td>
        </tr>
    </tbody>
</table>
<p>n) 释放资源</p>
<table width="63%" border=1>
    <tbody>
        <tr>
            <td width="25%">作用</td>
            <td width="25%">char*</td>
            <td width="25%">string</td>
            <td width="25%">CString</td>
        </tr>
        <tr>
            <td width="25%">释放</td>
            <td width="25%">free<br><br>delete (delete[])</td>
            <td width="25%">　</td>
            <td width="25%">ReleaseBuffer<br><br>ReleaseBufferSetLength<br></td>
        </tr>
    </tbody>
</table>
<p><strong>(五) 安全性&gt;</strong><br><br>CString &gt; string &gt; char*；<br><br><strong>(六) 灵活性</strong><br><br>CString &gt; string &gt;char*；<br><br><strong>(七) 可移植性</strong><br><br>char* = string &gt; CString<br></p>
<img src ="http://www.cppblog.com/yishanhante/aggbug/26258.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yishanhante/" target="_blank">jay</a> 2007-06-13 16:13 <a href="http://www.cppblog.com/yishanhante/articles/26258.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入浅出C++中的引用 </title><link>http://www.cppblog.com/yishanhante/articles/21384.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Fri, 06 Apr 2007 04:38:00 GMT</pubDate><guid>http://www.cppblog.com/yishanhante/articles/21384.html</guid><wfw:comment>http://www.cppblog.com/yishanhante/comments/21384.html</wfw:comment><comments>http://www.cppblog.com/yishanhante/articles/21384.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yishanhante/comments/commentRss/21384.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yishanhante/services/trackbacks/21384.html</trackback:ping><description><![CDATA[<div>引用是C++引入的新语言特性，是C++常用的一个重要内容之一，正确、灵活地使用引用，可以使程序简洁、高效。</div>
<div>&nbsp;</div>
<div>一、引用简介</div>
<div>　　引用就是某一变量（目标）的一个别名，对引用的操作与对变量直接操作完全一样。</div>
<div>　　引用的声明方法：类型标识符 &amp;引用名=目标变量名；</div>
<div>　　【例1】：int a; int &amp;ra=a; //定义引用ra,它是变量a的引用，即别名</div>
<div>　　说明：</div>
<div>　　（1）&amp;在此不是求地址运算，而是起标识作用。</div>
<div>　　（2）类型标识符是指目标变量的类型。</div>
<div>　　（3）声明引用时，必须同时对其进行初始化。</div>
<div>　　（4）引用声明完毕后，相当于目标变量名有两个名称，即该目标原名称和引用名，且不能再把该引用名作为其他变量名的别名。</div>
<div>　　 ra=1; 等价于 a=1; </div>
<div>　　（5）声明一个引用，不是新定义了一个变量，它只表示该引用名是目标变量名的一个别名，它本身不是一种数据类型，因此引用本身不占存储单元，系统也不给引用分配存储单元。故：对引用求地址，就是对目标变量求地址。&amp;ra与&amp;a相等。</div>
<div>　　（6）不能建立数组的引用。因为数组是一个由若干个元素所组成的集合，所以无法建立一个数组的别名。</div>
<div>&nbsp;</div>
<div>二、引用应用</div>
<div>　　1、引用作为参数 </div>
<div>　　引用的一个重要作用就是作为函数的参数。以前的C语言中函数参数传递是值传递，如果有大块数据作为参数传递的时候，采用的方案往往是指针，因为这样可以避免将整块数据全部压栈，可以提高程序的效率。但是现在（C++中）又增加了一种同样有效率的选择（在某些特殊情况下又是必须的选择），就是引用。</div>
<div>　　【例2】：</div>
<div>void swap(int &amp;p1, int &amp;p2) //此处函数的形参p1, p2都是引用 <br>{ int p; p=p1; p1=p2; p2=p; } </div>
<div>　　为在程序中调用该函数，则相应的主调函数的调用点处，直接以变量作为实参进行调用即可，而不需要实参变量有任何的特殊要求。如：对应上面定义的swap函数，相应的主调函数可写为：</div>
<div>main( )<br>{ <br>　int a,b;<br>　cin&gt;&gt;a&gt;&gt;b; //输入a,b两变量的值<br>　swap(a,b); //直接以变量a和b作为实参调用swap函数 <br>　cout&lt;&lt;a&lt;&lt; ' ' &lt;&lt;b; //输出结果 <br>} </div>
<div>　　上述程序运行时，如果输入数据10 20并回车后，则输出结果为20 10。</div>
<div>　　由【例2】可看出：</div>
<div>　　（1）传递引用给函数与传递指针的效果是一样的。这时，被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用，所以在被调函数中对形参变量的操作就是对其相应的目标对象（在主调函数中）的操作。</div>
<div>　　（2）使用引用传递函数的参数，在内存中并没有产生实参的副本，它是直接对实参操作；而使用一般变量传递函数的参数，当发生函数调用时，需要给形参分配存储单元，形参变量是实参变量的副本；如果传递的是对象，还将调用拷贝构造函数。因此，当参数传递的数据较大时，用引用比用一般变量传递参数的效率和所占空间都好。</div>
<div>　　（3）使用指针作为函数的参数虽然也能达到与使用引用的效果，但是，在被调函数中同样要给形参分配存储单元，且需要重复使用"*指针变量名"的形式进行运算，这很容易产生错误且程序的阅读性较差；另一方面，在主调函数的调用点处，必须用变量的地址作为实参。而引用更容易使用，更清晰。</div>
<div>　　如果既要利用引用提高程序的效率，又要保护传递给函数的数据不在函数中被改变，就应使用常引用。</div>
<div>&nbsp;</div>
<div>　2、常引用</div>
<div>　　常引用声明方式：const 类型标识符 &amp;引用名=目标变量名；</div>
<div>　　用这种方式声明的引用，不能通过引用对目标变量的值进行修改,从而使引用的目标成为const，达到了引用的安全性。</div>
<div>　　【例3】： </div>
<div>int a ;<br>const int &amp;ra=a;<br>ra=1; //错误<br>a=1; //正确 </div>
<div>　　这不光是让代码更健壮，也有些其它方面的需要。</div>
<div>　　【例4】：假设有如下函数声明：</div>
<div>string foo( );<br>void bar(string &amp; s); </div>
<div>　　那么下面的表达式将是非法的：</div>
<div>bar(foo( ));<br>bar("hello world"); </div>
<div>　　原因在于foo( )和"hello world"串都会产生一个临时对象，而在C++中，这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型，这是非法的。</div>
<div>　　引用型参数应该在能被定义为const的情况下，尽量定义为const 。</div>
<div><br>　3、引用作为返回值 </div>
<div>　　要以引用返回函数值，则函数定义时要按以下格式：</div>
<div>类型标识符 &amp;函数名（形参列表及类型说明）<br>{函数体}</div>
<div>　　说明：</div>
<div>　　（1）以引用返回函数值，定义函数时需要在函数名前加&amp;</div>
<div>　　（2）用引用返回一个函数值的最大好处是，在内存中不产生被返回值的副本。</div>
<div>　　【例5】以下程序中定义了一个普通的函数fn1（它用返回值的方法返回函数值），另外一个函数fn2，它以引用的方法返回函数值。</div>
<div>#include &lt;iostream.h&gt;<br>float temp; //定义全局变量temp<br>float fn1(float r); //声明函数fn1<br>float &amp;fn2(float r); //声明函数fn2<br>float fn1(float r) //定义函数fn1，它以返回值的方法返回函数值<br>{ <br>　temp=(float)(r*r*3.14); <br>　return temp; <br>}<br>float &amp;fn2(float r) //定义函数fn2，它以引用方式返回函数值<br>{ <br>　temp=(float)(r*r*3.14); <br>　return temp;<br>}<br>void main() //主函数<br>{ <br>　float a=fn1(10.0); //第1种情况，系统生成要返回值的副本（即临时变量）<br>　float &amp;b=fn1(10.0); //第2种情况，可能会出错（不同 C++系统有不同规定）<br>　//不能从被调函数中返回一个临时变量或局部变量的引用<br>　float c=fn2(10.0); //第3种情况，系统不生成返回值的副本<br>　//可以从被调函数中返回一个全局变量的引用<br>　float &amp;d=fn2(10.0); //第4种情况，系统不生成返回值的副本<br>　//可以从被调函数中返回一个全局变量的引用<br>　cout&lt;&lt;a&lt;&lt;c&lt;&lt;d;<br>} </div>
<div>&nbsp;</div>
<div>　引用作为返回值，必须遵守以下规则：</div>
<div>　　（1）不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁，因此被返回的引用就成为了"无所指"的引用，程序会进入未知状态。 </div>
<div>　　（2）不能返回函数内部new分配的内存的引用。虽然不存在局部变量的被动销毁问题，可对于这种情况（返回函数内部new分配内存的引用），又面临其它尴尬局面。例如，被函数返回的引用只是作为一个临时变量出现，而没有被赋予一个实际的变量，那么这个引用所指向的空间（由new分配）就无法释放，造成memory leak。</div>
<div>　　（3）可以返回类成员的引用，但最好是const。主要原因是当对象的属性是与某种业务规则（business rule）相关联的时候，其赋值常常与某些其它属性或者对象的状态有关，因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用（或指针），那么对该属性的单纯赋值就会破坏业务规则的完整性。</div>
<div>　　（4）引用与一些操作符的重载：</div>
<div>　　流操作符&lt;&lt;和&gt;&gt;，这两个操作符常常希望被连续使用，例如：cout &lt;&lt; "hello" &lt;&lt; endl;　因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括：返回一个流对象和返回一个流对象指针。但是对于返回一个流对象，程序必须重新（拷贝）构造一个新的流对象，也就是说，连续的两个&lt;&lt;操作符实际上是针对不同对象的！这无法让人接受。对于返回一个流指针则不能连续使用&lt;&lt;操作符。因此，返回一个流对象引用是惟一选择。这个唯一选择很关键，它说明了引用的重要性以及无可替代性，也许这就是C++语言中引入引用这个概念的原因吧。赋值操作符=。这个操作符象流操作符一样，是可以连续使用的，例如：x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值，以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。</div>
<div>　　【例6】 测试用返回引用的函数值作为赋值表达式的左值。</div>
<div>#include &lt;iostream.h&gt;<br>int &amp;put(int n);<br>int vals[10];<br>int error=-1;<br>void main()<br>{<br>put(0)=10; //以put(0)函数值作为左值，等价于vals[0]=10; <br>put(9)=20; //以put(9)函数值作为左值，等价于vals[9]=20; <br>cout&lt;&lt;vals[0]; <br>cout&lt;&lt;vals[9];<br>} <br>int &amp;put(int n)<br>{<br>if (n&gt;=0 &amp;&amp; n&lt;=9 ) return vals[n]; <br>else { cout&lt;&lt;"subscript error"; return error; }<br>} </div>
<div>（5）在另外的一些操作符中，却千万不能返回引用：+-*/ 四则运算符。它们不能返回引用。主要原因是这四个操作符没有side effect，因此，它们必须构造一个对象作为返回值，可选的方案包括：返回一个对象、返回一个局部变量的引用，返回一个new分配的对象的引用、返回一个静态对象引用。根据前面提到的引用作为返回值的三个规则，第2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。</div>
<div>&nbsp;</div>
<div>　4、引用和多态 </div>
<div>　　引用是除指针外另一个可以产生多态效果的手段。这意味着，一个基类的引用可以指向它的派生类实例。</div>
<div>　　【例7】： </div>
<div>class 　A;<br>class 　B：public A{&#8230;&#8230;};<br>B 　b;<br>A 　&amp;Ref = b; // 用派生类对象初始化基类对象的引用</div>
<div>Ref 只能用来访问派生类对象中从基类继承下来的成员，是基类引用指向派生类。如果A类中定义有虚函数，并且在B类中重写了这个虚函数，就可以通过Ref产生多态效果。</div>
<div>&nbsp;</div>
<div>三、引用总结</div>
<div>　　（1）在引用的使用中，单纯给某个变量取个别名是毫无意义的，引用的目的主要用于在函数参数传递中，解决大块数据或对象的传递效率和空间不如意的问题。</div>
<div>　　（2）用引用传递函数的参数，能保证参数传递中不产生副本，提高传递的效率，且通过const的使用，保证了引用传递的安全性。</div>
<div>　　（3）引用与指针的区别是，指针通过某个指针变量指向一个对象后，对它所指向的变量间接操作。程序中使用指针，程序的可读性差；而引用本身就是目标变量的别名，对引用的操作就是对目标变量的操作。</div>
<div>　　（4）使用引用的时机。流操作符&lt;&lt;和&gt;&gt;、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。</div>
<img src ="http://www.cppblog.com/yishanhante/aggbug/21384.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yishanhante/" target="_blank">jay</a> 2007-04-06 12:38 <a href="http://www.cppblog.com/yishanhante/articles/21384.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++常见问题 </title><link>http://www.cppblog.com/yishanhante/articles/21323.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Thu, 05 Apr 2007 08:01:00 GMT</pubDate><guid>http://www.cppblog.com/yishanhante/articles/21323.html</guid><wfw:comment>http://www.cppblog.com/yishanhante/comments/21323.html</wfw:comment><comments>http://www.cppblog.com/yishanhante/articles/21323.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yishanhante/comments/commentRss/21323.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yishanhante/services/trackbacks/21323.html</trackback:ping><description><![CDATA[<div class=postTitle>&nbsp;</div>
<p><br><font size=2>一、#include &#8220;filename.h&#8221;和#include filename.h&gt;的区别</font></p>
<p><font size=2>#include &#8220;filename.h&#8221;是指编译器将从当前工作目录上开始查找此文件</font></p>
<p><font size=2>#include filename.h&gt;是指编译器将从标准库目录中开始查找此文件</font></p>
<p><br><font size=2>二、头文件的作用</font></p>
<p><font size=2>加强安全检测</font></p>
<p><font size=2>通过头文件可能方便地调用库功能，而不必关心其实现方式</font></p>
<p><br><font size=2>三、* , &amp;修饰符的位置</font></p>
<p><font size=2>对于*和&amp;修饰符，为了避免误解，最好将修饰符紧靠变量名</font></p>
<p><br><font size=2>四、if语句</font></p>
<p><font size=2>不要将布尔变量与任何值进行比较，那会很容易出错的。</font></p>
<p><font size=2>整形变量必须要有类型相同的值进行比较</font></p>
<p><font size=2>浮点变量最好少比点，就算要比也要有值进行限制</font></p>
<p><font size=2>指针变量要和NULL进行比较，不要和布尔型和整形比较</font></p>
<p><br><font size=2>五、const和#define的比较</font></p>
<p><font size=2>const有数据类型，#define没有数据类型</font></p>
<p><font size=2>个别编译器中const可以进行调试，#define不可以进行调试</font></p>
<p><font size=2>在类中定义常量有两种方式</font></p>
<p><font size=2>1、 在类在声明常量，但不赋值，在构造函数初始化表中进行赋值；</font></p>
<p><font size=2>2、 用枚举代替const常量。</font></p>
<p><br><font size=2>六、C++函数中值的传递方式</font></p>
<p><font size=2>有三种方式：值传递(Pass by value)、指针传递(Pass by pointer)、引用传递(Pass by reference)</font></p>
<p><font size=2>void fun(char c) //pass by value</font></p>
<p><font size=2>void fun(char *str) //pass by pointer</font></p>
<p><font size=2>void fun(char &amp;str) //pass by reference</font></p>
<p><font size=2>如果输入参数是以值传递的话，最好使用引用传递代替，因为引用传递省去了临时对象的构造和析构</font></p>
<p><font size=2>函数的类型不能省略，就算没有也要加个void</font></p>
<p><br><font size=2>七、函数体中的指针或引用常量不能被返回</font></p>
<p><font size=2>Char *func(void)</font></p>
<p><font size=2>{</font></p>
<p><font size=2>char str[]=&#8221;Hello Word&#8221;;</font></p>
<p><font size=2>//这个是不能被返回的，因为str是个指定变量，不是一般的值，函数结束后会被注销掉</font></p>
<p><font size=2>return str; </font></p>
<p><font size=2>}</font></p>
<p><font size=2>函数体内的指针变量并不会随着函数的消亡而自动释放</font></p>
<p><br><font size=2>八、一个内存拷贝函数的实现体</font></p>
<p><font size=2>void *memcpy(void *pvTo,const void *pvFrom,size_t size)</font></p>
<p><font size=2>{</font></p>
<p><font size=2>assert((pvTo!=NULL)&amp;&amp;(pvFrom!=NULL));</font></p>
<p><font size=2>byte *pbTo=(byte*)pvTo; //防止地址被改变</font></p>
<p><font size=2>byte *pbFrom=(byte*)pvFrom;</font></p>
<p><font size=2>while (size-- &gt;0)</font></p>
<p><font size=2>pbTo++ = pbForm++;</font></p>
<p><font size=2>return pvTo;</font></p>
<p><font size=2>} </font></p>
<p><br><font size=2>九、内存的分配方式</font></p>
<p><font size=2>分配方式有三种，请记住，说不定那天去面试的时候就会有人问你这问题</font></p>
<p><font size=2>1、 静态存储区，是在程序编译时就已经分配好的，在整个运行期间都存在，如全局变量、常量。</font></p>
<p><font size=2>2、 栈上分配，函数内的局部变量就是从这分配的，但分配的内存容易有限。</font></p>
<p><font size=2>3、 堆上分配，也称动态分配，如我们用new,malloc分配内存，用delete,free来释放的内存。</font></p>
<p><br><font size=2>十、内存分配的注意事项</font></p>
<p><font size=2>用new或malloc分配内存时，必须要对此指针赋初值。</font></p>
<p><font size=2>用delete 或free释放内存后，必须要将指针指向NULL</font></p>
<p><font size=2>不能修改指向常量的指针数据</font></p>
<p><br><font size=2>十一、内容复制与比较</font></p>
<p><font size=2>//数组&#8230;&#8230;</font></p>
<p><font size=2>char a[]=&#8221;Hello Word!&#8221;;</font></p>
<p><font size=2>char b[10];</font></p>
<p><font size=2>strcpy(b,a);</font></p>
<p><font size=2>if (strcmp(a,b)==0)</font></p>
<p><font size=2>{}</font></p>
<p><font size=2>//指针&#8230;&#8230;</font></p>
<p><font size=2>char a[]=&#8221;Hello Word!&#8221;;</font></p>
<p><font size=2>char *p;</font></p>
<p><font size=2>p=new char[strlen(a)+1];</font></p>
<p><font size=2>strcpy(p,a);</font></p>
<p><font size=2>if (strcmp(p,a)==0)</font></p>
<p><font size=2>{}</font></p>
<p><br><font size=2>十二、sizeof的问题</font></p>
<p><font size=2>记住一点，C++无法知道指针所指对象的大小，指针的大小永远为4字节</font></p>
<p><font size=2>char a[]=&#8221;Hello World!&#8221;</font></p>
<p><font size=2>char *p=a;</font></p>
<p><font size=2>count sizeof(a) end; //12字节</font></p>
<p><font size=2>count sizeof(p) endl; //4字节</font></p>
<p><font size=2>而且，在函数中，数组参数退化为指针，所以下面的内容永远输出为4</font></p>
<p><font size=2>void fun(char a[1000])</font></p>
<p><font size=2>{</font></p>
<p><font size=2>count sizeof(a) endl; //输出4而不是1000</font></p>
<p><font size=2>}</font></p>
<p><br><font size=2>十三、关于指针</font></p>
<p><font size=2>1、 指针创建时必须被初始化</font></p>
<p><font size=2>2、 指针在free 或delete后必须置为NULL</font></p>
<p><font size=2>3、 指针的长度都为4字节</font></p>
<p><font size=2>４、释放内存时，如果是数组指针，必须要释放掉所有的内存，如</font></p>
<p><font size=2>char *p=new char[100];</font></p>
<p><font size=2>strcpy(p,&#8221;Hello World&#8221;);</font></p>
<p><font size=2>delete []p; //注意前面的［］号</font></p>
<p><font size=2>p=NULL;</font></p>
<p><font size=2>５、数组指针的内容不能超过数组指针的最大容易。</font></p>
<p><font size=2>如:</font></p>
<p><font size=2>char *p=new char[5];</font></p>
<p><font size=2>strcpy(p,&#8221;Hello World&#8221;); //报错 目标容易不够大</font></p>
<p><font size=2>delete []p; //注意前面的［］号</font></p>
<p><font size=2>p=NULL;</font></p>
<p><br><font size=2>十四、关于malloc/free 和new /delete</font></p>
<p><font size=2>l malloc/free 是C/C+的内存分配符，new /delete是C++的内存分配符。</font></p>
<p><font size=2>l 注意：malloc/free是库函数，new/delete是运算符</font></p>
<p><font size=2>l malloc/free不能执行构造函数与析构函数，而new/delete可以</font></p>
<p><font size=2>l new/delete不能在C上运行，所以malloc/free不能被淘汰</font></p>
<p><font size=2>l 两者都必须要成对使用</font></p>
<p><font size=2>l C++中可以使用_set_new_hander函数来定义内存分配异常的处理</font></p>
<p><br><font size=2>十五、Ｃ++的特性</font></p>
<p><font size=2>Ｃ++新增加有重载(overload)，内联（inline），Const，Virtual四种机制</font></p>
<p><font size=2>重载和内联：即可用于全局函数，也可用于类的成员函数；</font></p>
<p><font size=2>Const和Virtual：只可用于类的成员函数；</font></p>
<p><font size=2>重载：在同一类中，函数名相同的函数。由不同的参数决定调用那个函数。函数可要不可要Virtual关键字。和全局函数同名的函数不叫重载。如果在类中调用同名的全局函数，必须用全局引用符号::引用。</font></p>
<p><font size=2>覆盖是指派生类函数覆盖基类函数</font></p>
<p><font size=2>函数名相同；</font></p>
<p><font size=2>参数相同；</font></p>
<p><font size=2>基类函数必须有Virtual关键字；</font></p>
<p><font size=2>不同的范围(派生类和基类)。</font></p>
<p><font size=2>隐藏是指派生类屏蔽了基类的同名函数相同</font></p>
<p><font size=2>1、 函数名相同，但参数不同，此时不论基类有无Virtual关键字，基类函数将被隐藏。</font></p>
<p><font size=2>2、 函数名相同，参数也相同，但基类无Virtual关键字(有就是覆盖)，基类函数将被隐藏。</font></p>
<p><font size=2>内联：inline关键字必须与定义体放在一起，而不是单单放在声明中。</font></p>
<p><font size=2>Const：const是constant的缩写，&#8220;恒定不变&#8221;的意思。被const修饰的东西都受到强制保护，可以预防意外的变动，能提高程序的健壮性。</font></p>
<p><font size=2>1、 参数做输入用的指针型参数，加上const可防止被意外改动。</font></p>
<p><font size=2>2、 按值引用的用户类型做输入参数时，最好将按值传递的改为引用传递，并加上const关键字，目的是为了提高效率。数据类型为内部类型的就没必要做这件事情；如：</font></p>
<p><font size=2>将void Func(A a) 改为void Func(const A &amp;a)。</font></p>
<p><font size=2>而void func(int a)就没必要改成void func(const int &amp;a);</font></p>
<p><font size=2>3、 给返回值为指针类型的函数加上const，会使函数返回值不能被修改，赋给的变量也只能是const型变量。如：函数const char*GetString(void); char *str=GetString()将会出错。而const char *str=GetString()将是正确的。</font></p>
<p><font size=2>4、 Const成员函数是指此函数体内只能调用Const成员变量，提高程序的键壮性。如声明函数 int GetCount(void) const;此函数体内就只能调用Const成员变量。</font></p>
<p><font size=2>Virtual：虚函数：派生类可以覆盖掉的函数，纯虚函数：只是个空函数，没有函数实现体；</font></p>
<p><br><font size=2>十六、extern&#8220;C&#8221;有什么作用？</font></p>
<p><font size=2>Extern &#8220;C&#8221;是由Ｃ＋＋提供的一个连接交换指定符号，用于告诉Ｃ＋＋这段代码是Ｃ函数。这是因为C++编译后库中函数名会变得很长，与C生成的不一致，造成Ｃ＋＋不能直接调用C函数，加上extren &#8220;c&#8221;后，C++就能直接调用C函数了。</font></p>
<p><font size=2>Extern &#8220;C&#8221;主要使用正规DLL函数的引用和导出 和 在C++包含C函数或C头文件时使用。使用时在前面加上extern &#8220;c&#8221; 关键字即可。</font></p>
<p><br><font size=2>十七、构造函数与析构函数</font></p>
<p><font size=2>派生类的构造函数应在初始化表里调用基类的构造函数；</font></p>
<p><font size=2>派生类和基类的析构函数应加Virtual关键字。</font></p>
<p><font size=2>不要小看构造函数和析构函数，其实编起来还是不容易。</font></p>
<p><font size=2>#include iostream.h&gt;</font></p>
<p><font size=2>class Base</font></p>
<p><font size=2>{</font></p>
<p><font size=2>public: </font></p>
<p><font size=2>virtual ~Base() { cout "~Base" endl ; }</font></p>
<p><font size=2>};</font></p>
<p><font size=2>class Derived : public Base</font></p>
<p><font size=2>{</font></p>
<p><font size=2>public: </font></p>
<p><font size=2>virtual ~Derived() { cout "~Derived" endl ; }</font></p>
<p><font size=2>};</font></p>
<p><font size=2>void main(void)</font></p>
<p><font size=2>{</font></p>
<p><font size=2>Base * pB = new Derived; // upcast</font></p>
<p><font size=2>delete pB;</font></p>
<p><font size=2>}</font></p>
<p><font size=2>输出结果为：</font></p>
<p><font size=2>~Derived</font></p>
<p><font size=2>~Base</font></p>
<p><font size=2>如果析构函数不为虚，那么输出结果为</font></p>
<p><font size=2>~Base</font></p>
<p><br><font size=2>十八、#IFNDEF/#DEFINE/#ENDIF有什么作用</font></p>
<p><font size=2>仿止该头文件被重复引用</font></p>
<p><font size=2>&nbsp;<br></font></p>
<img src ="http://www.cppblog.com/yishanhante/aggbug/21323.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yishanhante/" target="_blank">jay</a> 2007-04-05 16:01 <a href="http://www.cppblog.com/yishanhante/articles/21323.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>const使用详解 </title><link>http://www.cppblog.com/yishanhante/articles/21322.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Thu, 05 Apr 2007 07:59:00 GMT</pubDate><guid>http://www.cppblog.com/yishanhante/articles/21322.html</guid><wfw:comment>http://www.cppblog.com/yishanhante/comments/21322.html</wfw:comment><comments>http://www.cppblog.com/yishanhante/articles/21322.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yishanhante/comments/commentRss/21322.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yishanhante/services/trackbacks/21322.html</trackback:ping><description><![CDATA[<table cellSpacing=1 cellPadding=4 width="100%" border=0>
    <tbody>
        <tr>
            <td vAlign=top>
            <div class=subhead dir=ltr><strong></strong>&nbsp;</div>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p class=content>一 const基础<br><br>如果const关键字不涉及到指针，我们很好理解，下面是涉及到指针的情况：<br><br>int b = 500;<br>const int* a = &amp;b; [1]<br>int const *a = &amp;b; [2]<br>int* const a = &amp;b; [3]<br>const int* const a = &amp;b; [4]<br><br>如果你能区分出上述四种情况，那么，恭喜你，你已经迈出了可喜的一步。不知道，也没关系，我们可以参考《Effective c++》Item21上的做法，如果const位于星号的左侧，则const就是用来修饰指针所指向的变量，即指针指向为常量；如果const位于星号的右侧，const就是修饰指针本身，即指针本身是常量。因此，[1]和[2]的情况相同，都是指针所指向的内容为常量（const放在变量声明符的位置无关），这种情况下不允许对内容进行更改操作，如不能*a = 3 ；[3]为指针本身是常量，而指针所指向的内容不是常量，这种情况下不能对指针本身进行更改操作，如a++是错误的；[4]为指针本身和指向的内容均为常量。<br>另外const 的一些强大的功能在于它在函数声明中的应用。在一个函数声明中，const 可以修饰函数的返回值，或某个参数；对于成员函数，还可以修饰是整个函数。有如下几种情况，以下会逐渐的说明用法：<br><br>A&amp; operator=(const A&amp; a);<br>void fun0(const A* a ); <br>void fun1( ) const; // fun1( ) 为类成员函数<br>const A fun2( );<br><br>二 const的初始化<br><br>先看一下const变量初始化的情况<br>1) 非指针const常量初始化的情况：<br><br>A b;<br>const A a = b;<br><br>2) 指针(引用)const常量初始化的情况：<br><br>A* d = new A();<br>const A* c = d;<br>或者：const A* c = new A();<br>引用：<br>A f;<br>const A&amp; e = f; // 这样作e只能访问声明为const的函数，而不能访问一般的成员函数；<br><br>[思考1]： 以下的这种赋值方法正确吗？<br>const A* c=new A();<br>A* e = c;<br>[思考2]： 以下的这种赋值方法正确吗？<br>A* const c = new A();<br>A* b = c;<br><br>三 作为参数和返回值的const修饰符<br><br>其实，不论是参数还是返回值，道理都是一样的，参数传入时候和函数返回的时候，初始化const变量<br>1 修饰参数的const，如 void fun0(const A* a ); void fun1(const A&amp; a);<br>调用函数的时候，用相应的变量初始化const常量，则在函数体中，按照const所修饰的部分进行常量化，如形参为const A* a，则不能对传递进来的指针的内容进行改变，保护了原指针所指向的内容；如形参为const A&amp; a，则不能对传递进来的引用对象进行改变，保护了原对象的属性。<br>[注意]：参数const通常用于参数为指针或引用的情况;<br>2 修饰返回值的const，如const A fun2( ); const A* fun3( );<br>这样声明了返回值后，const按照"修饰原则"进行修饰，起到相应的保护作用。<br><br>const Rational operator*(const Rational&amp; lhs, const Rational&amp; rhs)<br>{<br>return Rational(lhs.numerator() * rhs.numerator(),<br>lhs.denominator() * rhs.denominator());<br>}<br><br>返回值用const修饰可以防止允许这样的操作发生:<br><br>Rational a,b;<br>Radional c;<br>(a*b) = c;<br><br>一般用const修饰返回值为对象本身（非引用和指针）的情况多用于二目操作符重载函数并产生新对象的时候。<br>[总结] 一般情况下，函数的返回值为某个对象时，如果将其声明为const时，多用于操作符的重载。通常，不建议用const修饰函数的返回值类型为某个对象或对某个对象引用的情况。<br>原因如下：<br>如果返回值为某个对象为const（const A test = A 实例）或某个对象的引用为const（const A&amp; test = A实例），则返回值具有const属性，则返回实例只能访问类A中的公有（保护）数据成员和const成员函数，并且不允许对其进行赋值操作，这在一般情况下很少用到。<br><br>[思考3]： 这样定义赋值操作符重载函数可以吗？<br>const A&amp; operator=(const A&amp; a);<br><br>四 类成员函数中const的使用<br><br>一般放在函数体后，形如：void fun() const;<br>如果一个成员函数的不会修改数据成员，那么最好将其声明为const，因为const成员函数中不允许对数据成员进行修改，如果修改，编译器将报错，这大大提高了程序的健壮性。<br><br>五 使用const的一些建议<br><br>1 要大胆的使用const，这将给你带来无尽的益处，但前提是你必须搞清楚原委；<br>2 要避免最一般的赋值操作错误，如将const变量赋值，具体可见思考题；<br>3 在参数中使用const应该使用引用或指针，而不是一般的对象实例，原因同上；<br>4 const在成员函数中的三种用法（参数、返回值、函数）要很好的使用；<br>5 不要轻易的将函数的返回值类型定为const;<br>6除了重载操作符外一般不要将返回值类型定为对某个对象的const引用;<br><br><br><br><br>[思考题答案]<br>1 这种方法不正确，因为声明指针的目的是为了对其指向的内容进行改变，而声明的指针e指向的是一个常量，所以不正确；<br>2 这种方法正确，因为声明指针所指向的内容可变；<br>3 这种做法不正确；<br>在const A::operator=(const A&amp; a)中，参数列表中的const的用法正确，而当这样连续赋值的时侯，问题就出现了：<br>A a,b,c:<br>(a=b)=c;<br>因为a.operator=(b)的返回值是对a的const引用，不能再将c赋值给const常量。</p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/yishanhante/aggbug/21322.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yishanhante/" target="_blank">jay</a> 2007-04-05 15:59 <a href="http://www.cppblog.com/yishanhante/articles/21322.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>STATIC_CAST   VERSUS   REINTERPRET_CAST </title><link>http://www.cppblog.com/yishanhante/articles/20359.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Thu, 22 Mar 2007 06:51:00 GMT</pubDate><guid>http://www.cppblog.com/yishanhante/articles/20359.html</guid><wfw:comment>http://www.cppblog.com/yishanhante/comments/20359.html</wfw:comment><comments>http://www.cppblog.com/yishanhante/articles/20359.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yishanhante/comments/commentRss/20359.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yishanhante/services/trackbacks/20359.html</trackback:ping><description><![CDATA[
		<p>static_cast 和 reinterpret_cast 操作符修改了操作数类型. 它们不是互逆的; static_cast 在编译时使用类型信息执行转换, 在转换执行必要的检测(诸如指针越界计算, 类型检查). 其操作数相对是安全的. 另一方面, reinterpret_cast 仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换, 例子如下:</p>
		<p>    int n=9; double d=static_cast &lt; double &gt; (n); </p>
		<p>上面的例子中, 我们将一个变量从 int 转换到 double. 这些类型的二进制表达式是不同的. 要将整数 9 转换到 双精度整数 9, static_cast 需要正确地为双精度整数 d 补足比特位. 其结果为 9.0. reinterpret_cast 的行为却不同: </p>
		<p>    int n=9;<br />    double d=reinterpret_cast&lt;double &amp; &gt; (n); </p>
		<p>这次, 结果有所不同. 在进行计算以后, d 包含无用值. 这是因为 reinterpret_cast 仅仅是复制 n 的比特位到 d, 没有进行必要的分析. </p>
		<p>因此, 你需要谨慎使用 reinterpret_cast. <br /> </p>
		<p>
				<br />reinterpret_casts的最普通的用途就是在函数指针类型之间进行转换。例如，假设你有一个函数指针数组：<br />typedef void (*FuncPtr)();      // FuncPtr is 一个指向函数<br />                                // 的指针，该函数没有参数<br />    // 返回值类型为void<br />FuncPtr funcPtrArray[10];       // funcPtrArray 是一个能容纳<br />                                // 10个FuncPtrs指针的数组<br />让我们假设你希望（因为某些莫名其妙的原因）把一个指向下面函数的指针存入funcPtrArray数组：<br />int doSomething();<br />你不能不经过类型转换而直接去做，因为doSomething函数对于funcPtrArray数组来说有一个错误的类型。在FuncPtrArray数组里的函数返回值是void类型，而doSomething函数返回值是int类型。<br />funcPtrArray[0] = &amp;doSomething;     // 错误！类型不匹配 <br />reinterpret_cast可以让你迫使编译器以你的方法去看待它们：<br />funcPtrArray[0] =                   // this compiles<br />  reinterpret_cast&lt;FuncPtr&gt;(&amp;doSomething);<br />转换函数指针的代码是不可移植的（C++不保证所有的函数指针都被用一样的方法表示），在一些情况下这样的转换会产生不正确的结果（参见条款M31），所以你应该避免转换函数指针类型，除非你处于着背水一战和尖刀架喉的危急时刻。</p>
		<p> </p>
<img src ="http://www.cppblog.com/yishanhante/aggbug/20359.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yishanhante/" target="_blank">jay</a> 2007-03-22 14:51 <a href="http://www.cppblog.com/yishanhante/articles/20359.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《The C++ Programming Language (Special Edition)》中的忠告</title><link>http://www.cppblog.com/yishanhante/articles/18995.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Mon, 26 Feb 2007 07:47:00 GMT</pubDate><guid>http://www.cppblog.com/yishanhante/articles/18995.html</guid><wfw:comment>http://www.cppblog.com/yishanhante/comments/18995.html</wfw:comment><comments>http://www.cppblog.com/yishanhante/articles/18995.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yishanhante/comments/commentRss/18995.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yishanhante/services/trackbacks/18995.html</trackback:ping><description><![CDATA[
		<p>第1章 致读者</p>
		<p>[1] 在编写程序时，你是在为你针对某个问题的解决方案中的思想建立起一种具体表示。让程序的结构尽可能地直接反映这些思想：<br />    [a] 如果你能把“它”看成一个独立的概念，就把它做成一个类。<br />    [b] 如果你能把“它”看成一个独立地实体，就把它做成某个类的一个对象。<br />    [c] 如果两个类有共同的界面，将此界面做成一个抽象类。<br />    [d] 如果两个类的实现有某些显著的共同东西，静这些共性做成一个基类。<br />    [e] 如果一个类是一种对象的容器，将它做成一个模板。<br />    [f] 如果一个函数实现对某容器的一个算法，将它实现为对一族容器可用的模板函数。<br />    [g] 如果一组类、模板等互相之间有逻辑关系，将它们放进一个名字空间力。<br />[2] 在你定义一个并不是实现某个像矩阵或复数这样的数学对象的类时，或者定义一个低层地类型如链接表的时候：<br />    [a] 不要使用全局数据（使用成员）。<br />    [b] 不要使用全局函数。<br />    [c] 不要使用公用数据成员。<br />    [d] 不要使用友元，除非为了避免[a]或[c]。<br />    [e] 不要在一个类里面放“类型域”；采用虚函数。<br />    [f] 不要使用在线函数，除非座位效果显著的优化。</p>
		<p>第2章 C++概览</p>
		<p>[1] 不用害怕，一切都会随着时间的推移而逐渐明朗起来。<br />[2] 你并不需要在知道了C++地所有细节之后才能写出好的C++程序。<br />[3] 请特别关注程序设计技术，而不是各种语言特征。</p>
		<p>第3章 标准库概念</p>
		<p>[1] 不要像重新发明车轮那样企图做每件事；去使用库。<br />[2] 不要相信奇迹；要理解你的库能做什么，它们如何做，它们做时需要多大代价。<br />[3] 当你遇到一个选择时，应该优先选择标准库而不是其他的库。<br />[4] 不要认为标准库对于任何事情都是最理想的。<br />[5] 切记#include你所用到的功能的头文件。<br />[6] 记住，标准库的功能定义在名字空间std中。<br />[7] 请用string，而不是char*。<br />[8] 如果怀疑，就用一个检查区间范围的向量。<br />[9] vector&lt;T&gt;、list&lt;T&gt;和map&lt;key, value&gt;都比T[]好。<br />[10] 如果要向一个容器中添加一个元素，用push_back()或back_insert()。<br />[11] 采用对vector的push_back()，而不是realloc()。<br />[12] 在main()中捕捉公共的异常。</p>
		<p>第4章 类型和声明</p>
		<p>[1] 保持较小的作用域。<br />[2] 不要在一个作用域和它外围的作用域里采用同样的名字。<br />[3] 在一个声明中（只）声明一个名字。<br />[4] 让常用的和局部的名字比较短，让不常用的和全局的名字比较长。<br />[5] 避免看起来类似的名字。<br />[6] 维持某种统一的命名风格。<br />[7] 仔细选择名字，反映其意义而不是反映实现方式。<br />[8] 如果所用的内部类型表示某种可能变化的值，请用typdef为它定义一个有意义的名字。<br />[9] 用typedef为类型定义同义词，用枚举或类去定义新类型。<br />[10] 切记每个声明中都必须描述一个类型（没有隐式的int）。<br />[11] 避免有关字符值的不必要的假设。<br />[12] 避免有关整数大小的不必要假设。<br />[13] 避免有关浮点类型表示范围的不必要假设。<br />[14] 优先使用普通的int而不是short int或者long int。<br />[15] 优先使用double而不是float或者long double。<br />[16] 优先使用普通的char而不是signed char或者unsigned char。<br />[17] 避免做出有关对象大小的不必要假设。<br />[18] 避免无符号算术。<br />[19] 应该带着疑问去看待从signed到unsigned，或者从unsigned到singed的转换。<br />[20] 应该带着疑问去看待从浮点到整数的转换。 <br />[21] 应该带着疑问其看待向较小类型的转换，如将int转换到char。</p>
		<p>第5章 指针、数组和结构</p>
		<p>[1] 避免非平凡的指针算术。<br />[2] 当心，不要超出函数的界限去写。<br />[3] 尽量使用0而不是NULL。<br />[4] 尽量使用vector和valarrray而不是内部（C风格）的数组。<br />[5] 尽量使用string而不是以0结尾的char数组。<br />[6] 尽量少使用普通的引用参数。<br />[7] 避免void*，除了在某些低级代理。<br />[8] 避免在代码中使用非平凡的文字量（“神秘的数”）。相反，应该定义和使用各种符号常量。</p>
		<p>第6章 表达式和语句</p>
		<p>[1] 应尽量可能使用标准库，而不是其他的库和“手工打造的代码”。<br />[2] 避免过于复杂的表达式。<br />[3] 如果对运算符的优先级有疑问，加括号。<br />[4] 避免显式类型转换。<br />[5] 若必须做显式类型转换，提倡使用特殊强制运算符，而不是C风格的强制。<br />[6] 只对定义良好的构造使用T(e)记法。<br />[7] 避免带有无定义求值顺序的表达式。<br />[8] 避免goto。<br />[9] 避免do语句。<br />[10] 在你已经有了去初始化某个变量的值之前，不要去声明它。<br />[11] 式注释简洁、清晰、有意义。<br />[12] 保持一致的缩进编排风格。<br />[13] 倾向于去定义一个成员函数operator new()去取代全局的operator new()。<br />[14] 在读输入的时候，总应考虑病态形式的输入。</p>
		<p>第7章 函数</p>
		<p>[1] 质疑那些非const的引用参数；如果你想要一个函数去修改其参数，请使用指针或者返回值。<br />[2] 当你需要尽可能减少参数复制时，应该使用const引用参数。<br />[3] 广泛而一致地使用const。<br />[4] 避免宏。<br />[5] 避免不确定数目的参数。<br />[6] 不要返回局部变量的指针或者引用。<br />[7] 当一些函数对不同的类型执行概念上相同的工作时，请使用重载。<br />[8] 在各种整数上重载时，通过提供函数去消除常见的歧义性。<br />[9] 在考虑使用指向函数的指针时，请考虑虚函数或模板是不是更好的选择。<br />[10] 如果你必须使用宏，请使用带有许多大写字母的丑陋的名字。</p>
		<p>第8章 名字空间和异常</p>
		<p>[1] 用名字空间表示逻辑结构。<br />[2] 将每个非局部的名字放入某个名字空间里，除了main()之外。<br />[3] 名字空间的设计应该让你能很方便地使用它，而又不会意外地访问了其他的无关名字空间。<br />[4] 避免对名字空间使用很短的名字。<br />[5] 如果需要，通过名字空间别名去缓和和长名字空间的影响。<br />[6] 避免给你的名字空间的用户添加太大的记法负担。<br />[7] 在定义名字空间的成员时使用namespace::member的形式。<br />[8] 只在转换时，或者在局部作用域里，才用using namespace。<br />[9] 利用异常去松弛“错误”处理代码和正常处理代码之间的联系。<br />[10] 采用用户定义类型作为异常，不用内部类型。<br />[11] 当局部控制结构足以应付问题，不要使用异常。</p>
		<p>第9章 源文件和程序</p>
		<p>[1] 利用头文件去表示界面和强调逻辑结构。<br />[2] 用#include将头文件包含到实现有关功能的源文件里。<br />[3] 不要在不同编译单位里定义具有同样名字，意义类似但又不同的全局变量。<br />[4] 避免在头文件里定义非inline函数。<br />[5] 只在全局作用域或名字空间里使用#include。<br />[6] 只用#include包含完整的定义。<br />[7] 使用包含保护符。<br />[8] 用#include将C头文件包含到名字空间里，以避免全局名字。<br />[9] 将头文件做成自给自足的。<br />[10] 区分用户界面和实现界面。<br />[11] 区分一般用户界面和专家用户界面。<br />[12] 在有意向用于非C++程序组成部分的代码中，应避免需要运行时初始化的非局部对象。</p>
		<p>第10章 类</p>
		<p>[1] 用类表示概念。<br />[2] 只将public数据（struct）用在它实际杀过那仅仅时数据，而且对于这些数据成员并不存在不变式的地方。<br />[3] 一个具体类型属于最简单的类。如果有用的话，就应该尽可能使用具体类型，而不要采用更复杂的阿里，也不要用简单的数据结构。<br />[4] 只将那些需要直接访问类的表示的函数作为成员函数。<br />[5] 采用名字空间，使类与其协助函数之间的关系更明确。<br />[6] 将那些不修改对象值的成员函数做成const成员函数。<br />[7] 将那些需要访问类的表示，但无须针对特定对象调用的成员函数做成static成员函数。<br />[8] 通过构造函数建立起类的不变式。<br />[9] 如果构造函数申请某种资源，析构函数就应该释放一资源。<br />[10] 如果在一个类里有指针成员，它就要有复制操作（包括复制构造函数和复制赋值）。<br />[11] 如果在一个类里有引用成员，它就可能需要有复制操作（包括复制构造函数和复制赋值）。<br />[12] 如果一个类需要复制操作或析构函数，它多半还需要有构造函数、析构函数、复制赋值函数和复制构造函数。<br />[13] 在复制赋值函数里需要检查自我赋值。<br />[14] 在写复制构造函数时，请小心地复制每个需要复制的元素（当心默认的初始式）。<br />[15] 在向某个类中添加新成员函数时，一定要仔细检查，看是否存在需要更新的用户定义构造函数，以使它能够初始化新成员。<br />[16] 在类声明中需要定义整型常量时，请使用枚举。<br />[17] 在构造全局的和名字空间的对象时，应避免顺序依赖性。<br />[18] 用第一次开关去缓和顺序依赖性问题。<br />[19] 请记住，临时对象将在建立它们的那个完整表达式结束时销毁。</p>
		<p>第11章 运算符重载</p>
		<p>[1] 定义运算符主要是为了模仿习惯使用方式。<br />[2] 对于大型运算对象，请使用const引用参数类型。<br />[3] 对于大型的结果，请考虑优化返回方式。<br />[4] 如果默认复制操作对一个类和合适，最好是直接用它。<br />[5] 如果默认复制操作对一个类不和合适，重新定义它，或者禁止它。<br />[6] 对于需要访问表示的操作，优先考虑作为成员函数而不是作为非成员函数。<br />[7] 对于不访问表示的操作，优先考虑作为非成员函数而不是作为成员函数。<br />[8] 用名字空间将协助函数与“它们的”类关联起来。<br />[9] 对于对称的运算符采用非成员函数。<br />[10] 用()作为多维数组的下标。<br />[11] 将只有一个“大小参数”的构造函数做成explicit。<br />[12] 对于非特殊的使用，最好是用标准string而不是你自己的练习。<br />[13] 要注意引进隐式转换的问题。<br />[14] 用成员函数表达那些需要左值作为其左运算对象的运算符。</p>
		<p>第12章 派生类</p>
		<p>[1] 避免类型域。<br />[2] 用指针和引用避免切割问题。<br />[3] 用抽象类将设计的中心集中到提供清晰的界面方面。<br />[4] 用抽象类是界面最小化。<br />[5] 用抽象类从界面中排除实现细节。<br />[6] 用虚函数是新的实现能够添加进来，又不会影响用户代码。<br />[7] 用抽象类去尽可能减少用户代码的重新编译。<br />[8] 用抽象类是不同的实现能够共存。<br />[9] 一个有虚函数的类应该有一个虚析构函数。<br />[10] 抽象类通常不需要构造函数。<br />[11] 让不同概念的表示也不同。</p>
		<p>第13章 模板</p>
		<p>[1] 用模板描述需要使用到许多参数类型上去的算法。<br />[2] 用模板表述容器。<br />[3] 为指针的容器提供专门化，以减小代码规模。<br />[4] 总是在专门化之前声明模板的一般形式。<br />[5] 在专门化的使用之前先声明它。<br />[6] 尽量减少模板定义对于实例化环境的依赖性。<br />[7] 定义你所声明的每一个专门化。<br />[8] 考虑一个模板是否需要有针对C风格字符串和数组的专门化。<br />[9] 用表述策略的对象进行参数化。<br />[10] 用专门化和重载为同一概念的针对不同类型的实现提供统一界面。<br />[11] 为简单情况提供简单界面，用重载和默认参数去表述不常见的情况。<br />[12] 在修改为通用模板之前，在具体实例上排除程序错误。<br />[13] 如果模板定义需要在其他编译单位里访问，请记住写export。<br />[14] 对大模板和带有非平凡环境依赖性的模板，应采用分开编译的方式。<br />[15] 用模板表示转换，但要非常小心地定义这些转换。<br />[16] 如果需要，用constraint()成员函数给模板的实参增加限制。<br />[17] 通过显式实例化减少编译和连接时间。<br />[18] 如果运行时的效率非常重要，那么最好用模板而不是派生类。<br />[19] 如果增加各种变形而又不重新编译是很重要的，最好用派生类而不是模板。<br />[20] 如果无法定义公共的基类，最好用模板而不是派生类。<br />[21] 当有兼容性约束的内部类型和结构非常重要时，最好用模板而不是派生类。</p>
		<p>第14章 异常处理</p>
		<p>[1] 用异常做错误处理。<br />[2] 当更局部的控制机构足以应付时，不要使用异常。<br />[3] 采用“资源申请即初始化”技术去管理资源。<br />[4] 并不是美国程序都要求具有异常时的安全性。<br />[5] 才用“资源申请即初始化”技术和异常处理器去维持不变式。<br />[6] 尽量少用try块，用“资源申请即初始化”技术，而不是显式的处理器代码。<br />[7] 并不是美国函数都需要处理每个可能的错误。<br />[8] 在构造函数里通过抛出异常指明出现失败。<br />[9] 在从赋值中抛出异常之前，式操作对象处于合法状态。<br />[10] 避免从析构函数里抛出异常。<br />[11] 让main()捕捉并报告所有的异常。<br />[12] 使正常处理代码和错误处理代码相互分离。<br />[13] 在构造函数里抛出异常之前，应保证释放在此构造函数里申请的所有资源。<br />[14] 使资源管理具有层次性。<br />[15] 对于主要界面使用异常描述。<br />[16] 当心通过new分配的内存在发生异常时没有释放，并由此而导致存储的流失。<br />[17] 如果一函数可能抛出某个异常，就应该假定它一定会抛出这个异常。<br />[18] 不要假定所有异常都时由excepion类派生出来的。<br />[19] 库不应该单方面终止程序。相反，应该抛出异常，让调用者去做决定。<br />[20] 库不应该生成面向最终用户的错误信息。相反，它应该抛出异常，让调用者去做决定。<br />[21] 在设计的前期开发出一种错误处理策略。</p>
		<p>第15章 类层次结构</p>
		<p>[1] 利用常规的多重继承表述特征的合并。<br />[2] 利用多重继承完成实现细节与界面分离。<br />[3] 用virtual基类表达在类层次结构里对某些类（不是全部类）共同的东西。<br />[4] 避免显式的类型转换（强制）。<br />[5] 在不可避免地需要漫游类层次结构的地方，使用dynamic_cast。<br />[6] 尽量使用dynamic_cast而不是typeid。<br />[7] 尽量使用private而不是protected。<br />[8] 不要声明protected数据成员。<br />[9] 如果某个类定义了operator delete()，它也应该有虚析构函数。<br />[10] 在构造和析构期间不要调用虚函数。<br />[11] 尽量少用为解析成员名而写的显式限定词，最好时在覆盖函数里用它。</p>
		<p>第16章 库组织和容器</p>
		<p>[1] 利用标准库功能，以维持可移植性。<br />[2] 决不要另行定义标准库的功能。<br />[3] 决不要认为标准库比什么都好。<br />[4] 在定义一种新功能时，应考虑它是否能纳入标准库所提供的框架中。<br />[5] 记住标准库功能都定义在名字空间std里。<br />[6] 通过包含保准卡头文件声明其功能，不要自己另行显式声明。<br />[7] 利用后续抽象的优点。<br />[8] 避免肥大的界面。<br />[9] 与自己写按照反向顺序的显式循环相比，最好是写利用反向迭代器的算法。<br />[10] 用base()从reverse_iterator抽取出iterator。<br />[11] 通过引用传递容器。<br />[12] 用迭代器类型，如list&lt;char&gt;::iterator，而不要采用索引容器元素的指针。<br />[13] 在不需要修改容器元素时，使用const迭代器。<br />[14] 如果希望检查访问范围，请（直接或间接）使用at()。<br />[15] 多用容器和push_back()或resize()，少用数组和realloc().<br />[16] vector改变大小之后，不要使用指向其中的迭代器。<br />[17] 利用reserve()避免使迭代器非法。<br />[18] 在需要的时候，reserve()可以使执行情况更容易预期。</p>
		<p>第17章 标准库容器</p>
		<p>[1] 如果要用容器，首先考虑用vector。<br />[2] 了解你经常使用的每个操作的代价（复杂性，大O度量）。<br />[3] 容器的界面、实现和表示使不同的概念，不要混淆。<br />[4] 你可以依据多种不同准则去排序和搜索。<br />[5] 不要用C风格的字符串作为关键码，除非你提供了一种适当的比较准则。<br />[6] 你可以定义这样的比较准则，使等价的但是不相同的关键码值映射到同一个关键码。<br />[7] 在插入和删除元素时，最好时使用序列末端的操作（back操作）。<br />[8] 当你需要在容器的前端或中间做许多插入和删除时，请用list。<br />[9] 当你主要通过关键码访问元素时，请用map或multimap。<br />[10] 尽量用最小的操作集合，以取得最大的灵活性。<br />[11] 如果要保持元素的顺序性，选用map而不是hash_map。<br />[12] 如果查找速度极其重要，选hash_map而不是map。<br />[13] 如果无法对元素定义小于操作时，选hash_map而不是map。<br />[14] 当你需要检查某个关键码是否在关联容器里的时候，用find()。<br />[15] 用equal_range()在关联容器里找出所有具有给定关键码的所有元素。<br />[16] 当具有同样关键码的多个值需要保持顺序时，用multimap。<br />[17] 当关键码本身就是你需要保存的值时，用set或multiset。</p>
		<p>第18章 算法和函数对象</p>
		<p>[1] 多用算法，少用循环。<br />[2] 在写循环时，考虑是否能将它表述为一个通用的算法。<br />[3] 常规性地重温算法集合，看卡是不是能将新应用变得更明晰。<br />[4] 保证一对迭代器参数确实表述了一个序列。<br />[5] 设计时应该让使用最频繁的操作时简单而安全的。<br />[6] 吧测试表述成能够作为谓词使用的形式。<br />[7] 切记谓词是函数和对象，不是类型。<br />[8] 你可以用约束器从二元谓词做出一元谓词。<br />[9] 利用mem_fun()和mem_fun_ref()将算法应用于容器。<br />[10] 当你需要将一个参数约束到一个函数上时，用ptr_fun()。<br />[11] 切记srrcmp()用0表示“相等”，与==不同。<br />[12] 仅在没有更特殊的算法时，才使用for_each()和tranform()。<br />[13] 利用谓词，以便能一各种比较准则和相等准则使用算法。<br />[14] 利用谓词和其他函数对象，以使标准算法能用于表示范围广泛的意义。<br />[15] 运算符&lt;和==在指针上的默认意义很少适用于标准算法。<br />[16] 算法并不直接为它们的参数序列增加或减少元素。<br />[17] 应保证用于同一个序列的小于和相等谓词相互匹配。<br />[18] 有时排好序的序列用起来更有效且优雅。<br />[19] 仅为兼容性而使用qsort()和bsearch()。</p>
		<p>第19章 迭代器和分配器</p>
		<p>[1] 在写一个算法时，设法确定需要用哪种迭代器才能提供可接受的效率，并（只）使用这种迭代器所支持的操作符去表述算法。<br />[2] 当给定的迭代器参数提供了多于算法所需 的最小支持时，请通过重载为该算法提供效率更高的实现。<br />[3] 利用istream_traits为不同迭代器类别描述适当的算法。<br />[4] 记住在istream_iterator和ostream_iterator的访问之前使用++。<br />[5] 用插入器避免容器溢出。<br />[6] 在排错时使用额外的检查，后面只在必须时才删除这些检查。<br />[7] 多用++p，少用p++。<br />[8] 使用未初始化的存储去改善那些扩展数据结构的算法性能。<br />[9] 使用临时缓冲区去改善需要临时数据结构的算法的性能。<br />[10] 在写自己的分配器之前三思。<br />[11] 避免malloc()、free()、realloc()等。<br />[12] 你可以通过为rebind所用的技术去模拟对模板的typedef。</p>
		<p>第20章 串</p>
		<p>[1] 尽量使用string操作，少用C风格字符串函数。<br />[2] 用string作为变量或者成员，不作为基类。<br />[3] 你可以将string作为参数值或者返回值，让系统去关心存储管理问题。<br />[4] 当你希望做范围检查时，请用at()而不是迭代器或者[]。<br />[5] 当你希望优化速度时，请用迭代器或[]而不是at()。<br />[6] 直接或者间接地使用substr()去读字子串，用replace()去写子串。<br />[7] 用find()操作在string里确定值的位置（而不是写一个显式的循环）。<br />[8] 在你需要高效率地添加字符时，请在string的后面附加。<br />[9] 在没有极端时间要求情况下用string作为字符输入的目标。<br />[10] 用string::npos表示“sring的剩余部分”。<br />[11] 如果必要，就采用低级操作去实现极度频繁使用的strng（而不是到处用低级数据结构）。<br />[12] 如果你使用string，请在某些地方捕捉length_error和out_of_rang异常、<br />[13] 小心，不要将带值0的char*传递给字符串函数。<br />[14] 只是到必须做的时候，（再）用c_str()产生string的C风格表示。<br />[15] 当你需要知道字符串的类别时，用isalpha()、isdigit()等函数，不要自己去写对字符值的检测。</p>
		<p>第21章 流</p>
		<p>[1] 在为用户定义类型的值定义&lt;&lt;和&gt;&gt;时，应该采用意义清晰的正文表达形式。<br />[2] 在打印包含低优先级运算符的表达式时需要用括号。<br />[3] 在添加新的&lt;&lt;和&gt;&gt;运算符时，你不必修改istream或ostream。<br />[4] 你可以定义函数，时其能基于第二个（或更后面的）参数，具有像virtual函数那样的行为。<br />[5] 切记，按默认约定&gt;&gt;跳过所有空格。<br />[6] 使用低级输入函数（如get()和read()）主要是为了实现高级输入函数。<br />[7] 在使用get()、getline()和read()时留心其终止准则。<br />[8] 在控制I/O时，尽量采用操控符，少用状态标志。<br />[9] （只）用异常去捕捉罕见的I/O错误。<br />[10] 联结用于交互式I/O的流。<br />[11] 使用哨位将许多函数的入口和出口代码集中到一个地方。<br />[12] 在无参数操控符最后不要写括号。<br />[13] 使用标准操控符式应记住写#include &lt;iomanip&gt;。<br />[14] 你可以通过定义一个简单函数对象得到三元运算符的效果（和效率）。<br />[15] 切记，width描述只应用于随后的一个I/O操作。<br />[16] 切记precision描述只对所后所的浮点数输出操作有效。<br />[17] 用字符串流做内存里的格式化。<br />[18] 你可以描述一个文件流的模式。<br />[19] 在扩充I/O系统时，应该清楚地区分格式化（iostream）和缓冲（streambuf）。<br />[20] 将传输值的非标准方式实现为流缓冲。<br />[21] 将格式化值的非标准方式实现为流操作。<br />[22] 你可以利用一对函数隔离和封装其对用户定义代码的调用。<br />[23] 你可以在读入之前用in_avail()去确定输入操作是否会被阻塞。<br />[24] 划分清楚需要高效的简单操作和实现某种策略的操作（将前者做成inline，将后者做成virtual）。<br />[25] 用locale将“文化差异”局部化。<br />[26] 用sync_with_stdio(x)去混合C风格和C++风格的I/O，或者离解C风格和C++风格的I/O。<br />[27] 当心C风格I/O的类型错误。</p>
		<p>第22章 数值</p>
		<p>[1] 数值问题常常和微妙。如果你对数值问题的数学方面不是100%有把握，请去找专家或者做试验。<br />[2] 用numberic_limits去确定内部类型的性质。<br />[3] 为用户定义的标量类型描述numberic_limits。<br />[4] 如果运行时效率比对于操作和元素的灵活性更重要的话，那么请用valarray去做数值计算。<br />[5] 用切割表述在数组的一部分上的操作，而不是用循环。<br />[6] 利用组合器，通过清除临时量和更好的算法来获得效率。<br />[7] 用std::complex做复数算术。<br />[8] 你可以把使用complex类的老代码通过一个typedef转为用str::complex模板。<br />[9] 在写循环从一个表出发计算某个值之前，先考虑一下accumulate()、inner_produce()、partial_sum()和adjacent_difference()。<br />[10] 最好使用具有特定分布的随机数，少直接用rand()。<br />[11] 注意是你的随机数充分随机。</p>
		<p>第23章 开发和设计</p>
		<p>[1] 知道你试图达到什么目的。<br />[2] 心中牢记软件开发是一项人的活动。<br />[3] 用类比来证明是有意的欺骗。<br />[4] 保持一个特定的实实在在的目标。<br />[5] 不要试图用技术方式去解决社会问题。<br />[6] 在设计和对待人员方面都应该有长期考虑。<br />[7] 对于什么程序在编码之前先行设计是有意义的，在程序规模上并没有下限。<br />[8] 设计过程应鼓励反馈。<br />[9] 不要将做事情都当做取得了进展。<br />[10] 不要推广到超出了所需要的、你已有直接经验的和已经测试过的东西。<br />[11] 将概念表述为类。<br />[12] 系统里也存在一些不应该用类表述的性质。<br />[13] 将概念间的层次关系用类层次结构表示。<br />[14] 主动到应用和实现中去寻找概念间的共性，将由此得到的一般性概念表示为基类。<br />[15] 在其他领域中的分类方式未必适合作为应用中的继承模型的分类方式。<br />[16] 基于行为和不变式设计类层次结构。<br />[17] 考虑用例。<br />[18] 考虑用CRC卡。<br />[19] 用现存系统作为模型、灵感的源泉和出发点。<br />[20] 意识到视觉图形工程的重要性。<br />[21] 在原型成为负担时就抛弃它。<br />[22] 为变化而设计，将注意力集中到灵活性、可扩展性、可移植性和重用。<br />[23] 将注意力集中到组件设计。<br />[24] 让每个界面代表在一个抽象层次中的一个概念。<br />[25] 面向变化进行设计，以求得稳定性。<br />[26] 通过将广泛频繁使用的界面做得最小、最一般和抽象来使设计稳定。<br />[27] 保持尽可能小，不为“特殊需要”增加新特征。<br />[28] 总考虑类的其他表示方式。如果不可能有其他方式，这个类可能就没有代表某个清晰的概念。<br />[29] 反复评审、精化设计和实现。<br />[30] 采用那些能用于调试，用于分析问题、设计和实现的最好工具。<br />[31] 尽早、尽可能频繁地进行试验、分析和测试。<br />[32] 不要忘记效率。<br />[33] 保持某种适合项目规模的规范性水平。<br />[34] 保证有人负责项目的整体设计。<br />[35] 为可重用组件做文档、推介和提供支持。<br />[36] 将目标与细节一起写进文档里。<br />[37] 将为新开发者提供的教许材料作为文档的一部分。<br />[38] 鼓励设计、库和类的重用，并给予回报。</p>
		<p>第24章 设计和编程</p>
		<p>[1] 应该向数据抽象和面向对象设计的方向发展。<br />[2] （仅仅）根据需要去使用C++的特征和技术。<br />[3] 设计应与编程风格相互匹配。<br />[4] 将类/概念作为设计中最基本的关注点，而不是功能/处理。<br />[5] 用类表示概念。<br />[6] 用继承（仅仅）表示概念间的层次结构关系。<br />[7] 利用应用层静态类型的方式给出有关界面的更强的保证。<br />[8] 使用程序生成器和直接界面操作工具去完成定义良好的工作。<br />[9] 不要去使用那些与任何通用程序设计语言之间都没有清晰界面的程序生成器或者直接界面操作工具。<br />[10] 保存不同层次的抽象相互分离。<br />[11] 关注组件设计。<br />[12] 保证虚函数有定义良好的意义，每个覆盖函数都实现预期行为。<br />[13] 公用界面表示的是“是一个”关系。<br />[14] 成员表示的是“有一个”关系。<br />[15] 在表示简单包容时最好用直接成员，不用指向单独分配的对象的指针。<br />[16] 设法保证使用依赖关系为易理解的，尽可能不出现循环，而且最小。<br />[17] 对于所有的类，定义好不变式。<br />[18] 显式地将前条件、后条件和其他断言表述为断言（可能使用Assert()）。<br />[19] 定义的界面应该只暴露初尽可能少的信息。<br />[20] 尽可能减少一个界面对其他界面的依赖性。<br />[21] 保持界面为强类型的。<br />[22] 利用应用层的类型来表述界面。<br />[23] 将界面表述得使请求可以传递给远程得服务器。<br />[24] 避免肥大的界面。<br />[25] 尽可能地使用private数据和成员函数。<br />[26] 用protected/private区分开派生类的设计者与一般用户间的不同需要。<br />[27] 使用模板去做通用型程序设计。<br />[28] 使用模板去做算法策略的参数化。<br />[29] 如果需要在编译时做类型解析，情使用模板。<br />[30] 如果需要在运行时做类型解析，请使用层次结构。</p>
		<p>第25章 类的作用</p>
		<p>[1] 应该对一个类的使用方式做出有意识的决策（作为设计师或者作为用户）。<br />[2] 应注意到涉及不同种类的类之间的权衡问题。<br />[3] 用具体类型去表示简单的独立概念。<br />[4] 用具体类型去表示那些最佳效果及其关键的概念。<br />[5] 不要从具体类派生。<br />[6] 用抽象类去表示那些对象的表示可能变化的界面。<br />[7] 用抽象类去表示那些可能出现多种对象表示共存情况的界面。<br />[8] 用抽象类去表示现存类型的新界面。<br />[9] 当类似概念共享许多实现细节时，应该使用结点类。<br />[10] 用结点类去逐步扩充一个实现。<br />[11] 用运行时类型识别从对象获取界面。<br />[12] 用类去表示具有与之关联的状态信息的动作。<br />[13] 用类去表示需要存储、传递或者延迟执行的动作。<br />[14] 利用界面类去为某种新的用法而调整一个类（不修改这个类）。 <br />[15] 利用界面类增加检查。<br />[16] 利用句柄去避免直接使用指针和引用。<br />[17] 利用句柄去管理共享的表示。<br />[18] 在那些能预先定义控制结构的应用领域中使用应用框架。</p>
<img src ="http://www.cppblog.com/yishanhante/aggbug/18995.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yishanhante/" target="_blank">jay</a> 2007-02-26 15:47 <a href="http://www.cppblog.com/yishanhante/articles/18995.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>