﻿<?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++博客-夏冰-随笔分类-程序</title><link>http://www.cppblog.com/summericeyl/category/16084.html</link><description>生如夏花之绚烂，死若秋叶之静美</description><language>zh-cn</language><lastBuildDate>Mon, 31 Oct 2011 00:28:22 GMT</lastBuildDate><pubDate>Mon, 31 Oct 2011 00:28:22 GMT</pubDate><ttl>60</ttl><item><title>doxygen 笔记</title><link>http://www.cppblog.com/summericeyl/archive/2011/10/29/159331.html</link><dc:creator>夏冰</dc:creator><author>夏冰</author><pubDate>Sat, 29 Oct 2011 14:53:00 GMT</pubDate><guid>http://www.cppblog.com/summericeyl/archive/2011/10/29/159331.html</guid><wfw:comment>http://www.cppblog.com/summericeyl/comments/159331.html</wfw:comment><comments>http://www.cppblog.com/summericeyl/archive/2011/10/29/159331.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/summericeyl/comments/commentRss/159331.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/summericeyl/services/trackbacks/159331.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: doxygen 笔记&nbsp;&nbsp;<a href='http://www.cppblog.com/summericeyl/archive/2011/10/29/159331.html'>阅读全文</a><img src ="http://www.cppblog.com/summericeyl/aggbug/159331.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/summericeyl/" target="_blank">夏冰</a> 2011-10-29 22:53 <a href="http://www.cppblog.com/summericeyl/archive/2011/10/29/159331.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Qt 笔记 Chapter4-Chapter5</title><link>http://www.cppblog.com/summericeyl/archive/2011/05/31/147754.html</link><dc:creator>夏冰</dc:creator><author>夏冰</author><pubDate>Tue, 31 May 2011 06:06:00 GMT</pubDate><guid>http://www.cppblog.com/summericeyl/archive/2011/05/31/147754.html</guid><wfw:comment>http://www.cppblog.com/summericeyl/comments/147754.html</wfw:comment><comments>http://www.cppblog.com/summericeyl/archive/2011/05/31/147754.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/summericeyl/comments/commentRss/147754.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/summericeyl/services/trackbacks/147754.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Qt 笔记 Chapter4-Chapter5&nbsp;&nbsp;<a href='http://www.cppblog.com/summericeyl/archive/2011/05/31/147754.html'>阅读全文</a><img src ="http://www.cppblog.com/summericeyl/aggbug/147754.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/summericeyl/" target="_blank">夏冰</a> 2011-05-31 14:06 <a href="http://www.cppblog.com/summericeyl/archive/2011/05/31/147754.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Qt 笔记 Chapter1-Chapter2</title><link>http://www.cppblog.com/summericeyl/archive/2011/05/11/146222.html</link><dc:creator>夏冰</dc:creator><author>夏冰</author><pubDate>Wed, 11 May 2011 15:43:00 GMT</pubDate><guid>http://www.cppblog.com/summericeyl/archive/2011/05/11/146222.html</guid><wfw:comment>http://www.cppblog.com/summericeyl/comments/146222.html</wfw:comment><comments>http://www.cppblog.com/summericeyl/archive/2011/05/11/146222.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/summericeyl/comments/commentRss/146222.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/summericeyl/services/trackbacks/146222.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/summericeyl/archive/2011/05/11/146222.html'>阅读全文</a><img src ="http://www.cppblog.com/summericeyl/aggbug/146222.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/summericeyl/" target="_blank">夏冰</a> 2011-05-11 23:43 <a href="http://www.cppblog.com/summericeyl/archive/2011/05/11/146222.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用 Qt 与 Visual C++ 2008 创建应用程序</title><link>http://www.cppblog.com/summericeyl/archive/2011/04/17/144380.html</link><dc:creator>夏冰</dc:creator><author>夏冰</author><pubDate>Sun, 17 Apr 2011 06:38:00 GMT</pubDate><guid>http://www.cppblog.com/summericeyl/archive/2011/04/17/144380.html</guid><wfw:comment>http://www.cppblog.com/summericeyl/comments/144380.html</wfw:comment><comments>http://www.cppblog.com/summericeyl/archive/2011/04/17/144380.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/summericeyl/comments/commentRss/144380.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/summericeyl/services/trackbacks/144380.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/summericeyl/archive/2011/04/17/144380.html'>阅读全文</a><img src ="http://www.cppblog.com/summericeyl/aggbug/144380.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/summericeyl/" target="_blank">夏冰</a> 2011-04-17 14:38 <a href="http://www.cppblog.com/summericeyl/archive/2011/04/17/144380.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ Primer 第10章</title><link>http://www.cppblog.com/summericeyl/archive/2011/03/05/141165.html</link><dc:creator>夏冰</dc:creator><author>夏冰</author><pubDate>Sat, 05 Mar 2011 08:00:00 GMT</pubDate><guid>http://www.cppblog.com/summericeyl/archive/2011/03/05/141165.html</guid><wfw:comment>http://www.cppblog.com/summericeyl/comments/141165.html</wfw:comment><comments>http://www.cppblog.com/summericeyl/archive/2011/03/05/141165.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/summericeyl/comments/commentRss/141165.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/summericeyl/services/trackbacks/141165.html</trackback:ping><description><![CDATA[<ul> <li>pair类型, 在 utility 头文件中定义, pair类型提供的操作:  <ul> <li>pair&lt;T1, T2&gt; p1;  <li>pari&lt;T1, T2&gt; p1(v1, v2);  <li>make_pair(v1, v2);  <li>p1 &lt; p2  <li>p1 == p2  <li>p.first  <li>p.second </li></ul> <li>关联容器不提供 front, push_front, pop_front, back, push_back, pop_back操作  <li>在实际使用中, map的键类型必须定义 &lt; 操作符, map的构造函数:  <ul> <li>map&lt;k, v&gt; m;  <li>map&lt;k, v&gt; m(m2);  <li>map&lt;k, v&gt; m(b, e); --- 其中元素的类型必须能转换为 pair&lt;const k, v&gt; </li></ul> <li>map&lt;K, V&gt;::value_type为pair类型, 其中键为 const. map&lt;K, V&gt;::key_type --- 用作索引的键类型; map&lt;K, V&gt;::mapped_type --- 键所关联的值的类型; 所以 map&lt;K, V&gt;为 pair&lt;const map&lt;K, V&gt;::key_type, map&lt;K, V&gt;::mapped_type &gt;.  <li>map 迭代器解引用产生 pair类型 对象 <pre>map&lt;string, int&gt;::iterator map_it = word_count.begin();
cout &lt;&lt; map_it-&gt;first;
cout &lt;&lt; " " &lt;&lt; map_it-&gt;second;
</pre>
<li>map的下标操作符, 如果该键已存在, 则返回其所关联的值, 否则为改键创建一个新的元素, 并插入到此map对象中. 
<li>map容器提供的insert操作 
<ul>
<li>m.insert(e) --- e 为 value_type类型的值, 如 e.first不在m中, 则插入新值, 否则m不变 
<li>m.insert(beg, end) 
<li>m.insert(iter, e) --- 以iter为起点搜索新元素存储的位置. </li></ul>
<li>可以使用 make_pair 和 typedef 简化插入的实参. 返回值为pair类型, 包含一个迭代器和bool值, 迭代器指向插入的位置, bool表示受否插入该元素 
<li>map提供了函数 count()和find()用于查看某键值是否存在而不会插入该键值. 
<li>从map中删除对象: m.erase(k), m.erase(p), m.erase(b, e); 
<li>set不支持下标操作, 没有定义 mapped_type 类型, 存储的元素仅仅是键, 没有所关联的值. 
<li>set 使用 insert() 成员函数添加元素, 返回 pair类型的值, 第一个为指向插入元素的迭代器, 第二个为是否成功插入. 可使用 find() 成员函数查找元素, 使用 count() 判断元素是否存在. 
<li>multimap 不支持下标运算, insert() 和 erase() 操作同样适用于 multimap 以及 multiset容器. multimap中的erase() 函数, 如果只有一个参数, 则删除所有该参数为键值的元素, 并返回删除元素的个数 
<li>如何在 multimap和 multiset中查找元素: (1) 使用 find 和 count 操作; (2) lower_bound 和 upper_bound 函数, 以及 equal_range 函数 
<ul>
<li>lower_bound(k): 返回一个迭代器, 指向键不小于k的第一个元素 
<li>upper_bound(k): 返回一个迭代器, 指向键大于k的第一个元素, 如元素不存在, 和上面的操作返回同一个迭代器, 指向该元素应当插入的位置. 
<li>equal_range(k): 返回一个迭代器的pair对象, 成员为上面两个操作的结果 </li></ul></li></ul><img src ="http://www.cppblog.com/summericeyl/aggbug/141165.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/summericeyl/" target="_blank">夏冰</a> 2011-03-05 16:00 <a href="http://www.cppblog.com/summericeyl/archive/2011/03/05/141165.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ Primer第9章 顺序容器 </title><link>http://www.cppblog.com/summericeyl/archive/2011/03/04/141124.html</link><dc:creator>夏冰</dc:creator><author>夏冰</author><pubDate>Fri, 04 Mar 2011 09:44:00 GMT</pubDate><guid>http://www.cppblog.com/summericeyl/archive/2011/03/04/141124.html</guid><wfw:comment>http://www.cppblog.com/summericeyl/comments/141124.html</wfw:comment><comments>http://www.cppblog.com/summericeyl/archive/2011/03/04/141124.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/summericeyl/comments/commentRss/141124.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/summericeyl/services/trackbacks/141124.html</trackback:ping><description><![CDATA[<ol>
    <li>
    顺序容器类型
    </li>
</ol>
<ol>
    <li>
    顺序容器适配器
    <table border="5">
        <tbody>
            <tr>
                <th>顺序容器</th>
                <th>&nbsp;</th>
            </tr>
            <tr>
                <td>vector</td>
                <td>快速随机访问</td>
            </tr>
            <tr>
                <td>list</td>
                <td>快速插入删除</td>
            </tr>
            <tr>
                <td>deque</td>
                <td>双端队列</td>
            </tr>
        </tbody>
    </table>
    </li>
</ol>
<ol>
    <li>
    容器的元素使用默认构造函数初始化能达到最佳运行时性能, 并且使容器更容易使用.
    <table border="5">
        <tbody>
            <tr>
                <th>顺序容器适配器</th>
                <th>&nbsp;</th>
            </tr>
            <tr>
                <td>stack</td>
                <td>后进先出</td>
            </tr>
            <tr>
                <td>queue</td>
                <td>先进先出</td>
            </tr>
            <tr>
                <td>priority_queue</td>
                <td>有优先级管理的队列</td>
            </tr>
        </tbody>
    </table>
    </li>
</ol>
<ol>
    <li>
    可以用作容器元素类型的类型必须满足两个条件: (1) 元素类型必须支持赋值运算;  (2) 元素类型的对象必须可以复制. 所以除了引用类型,
    所有内置或复合类型都可以作为元素类型. 没有元素是引用类型的元素; 除了输入输出(IO)标准库类型, auto_ptr类型,
    所有标准库类型都是有效的容器元素类型.
    <table border="5">
        <tbody>
            <tr>
                <th>容器构造函数</th>
                <th>&nbsp;</th>
            </tr>
            <tr>
                <td>C&lt;T&gt; c;</td>
                <td>创建一个名为c的空容器</td>
            </tr>
            <tr>
                <td>C c(c2)</td>
                <td>c2的副本, 相同容器类型, 相同容器元素</td>
            </tr>
            <tr>
                <td>C c(b, e)</td>
                <td>使用迭代式初始化</td>
            </tr>
            <tr>
                <td>C c(n, t)</td>
                <td>n个值为t的元素初始化容器</td>
            </tr>
            <tr>
                <td>C c(n)</td>
                <td>只适用于顺序容器, 创建 n个值默认构造函数初始化的值</td>
            </tr>
        </tbody>
    </table>
    </li>
    <li>
    可以定义元素是容器类型的元素
    <div class="cnblogs_Highlighter">
    <div class="syntaxhighlighter  cpp" id="highlighter_700198">
    <div class="bar">
    <div class="toolbar"><a class="item viewsource" style="width: 16px; height: 16px;" title="view source" href="http://www.cnblogs.com/summericeyl/archive/2011/03/04/1971095.html#viewSource">view source</a><a class="item printsource" style="width: 16px; height: 16px;" title="print" href="http://www.cnblogs.com/summericeyl/archive/2011/03/04/1971095.html#printSource">print</a><a class="item about" style="width: 16px; height: 16px;" title="?" href="http://www.cnblogs.com/summericeyl/archive/2011/03/04/1971095.html#about">?</a></div>
    </div>
    <div class="lines">
    <div class="line alt1">
    <table>
        <tbody>
            <tr>
                <td class="number"><code>1</code></td>
                <td class="content"><code class="cpp plain">vector&lt; vector&lt;string&gt; &gt; lines;</code></td>
            </tr>
        </tbody>
    </table>
    </div>
    </div>
    </div>
    </div>
    </li>
    <li>
    常用迭代器运算
    </li>
</ol>
<ol>
    <li>
    容器定义的类型别名
    <table border="5">
        <tbody>
            <tr>
                <th>常用迭代器运算</th>
                <th>vector和 deque类型迭代器支持的操作</th>
            </tr>
            <tr>
                <td>*iter</td>
                <td>iter + n</td>
            </tr>
            <tr>
                <td>iter-&gt;</td>
                <td>iter - n</td>
            </tr>
            <tr>
                <td>++iter</td>
                <td>iter1 += iter2</td>
            </tr>
            <tr>
                <td>--iter</td>
                <td>iter1 -= iter2</td>
            </tr>
            <tr>
                <td>iter++</td>
                <td>iter1 - iter2</td>
            </tr>
            <tr>
                <td>iter--</td>
                <td>&gt; &gt;= &lt; &lt;=</td>
            </tr>
            <tr>
                <td>iter1 == iter2</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>iter1 != iter2</td>
                <td>&nbsp;</td>
            </tr>
        </tbody>
    </table>
    </li>
</ol>
<ol>
    <li>
    value_type, referece, const_reference 三种类型使程序员无须直接知道容器元素的真正类型, 就能使用它.
    <table border="5">
        <tbody>
            <tr>
                <th>容器定义的类型别名</th>
                <th>&nbsp;</th>
            </tr>
            <tr>
                <td>size_type</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>iterator</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>const_iterator</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>reverse_iterator</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>const_reverse_iterator</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>difference_type</td>
                <td>存储两个迭代器差值的有符号整型, 可为复数</td>
            </tr>
            <tr>
                <td>value_type</td>
                <td>元素的类型</td>
            </tr>
            <tr>
                <td>referece</td>
                <td>元素的左值类型, 是 value_type&amp; 的同义词</td>
            </tr>
            <tr>
                <td>const_reference</td>
                <td>等同于 const value_type&amp;</td>
            </tr>
        </tbody>
    </table>
    </li>
    <li>
    所有顺序元素支持 push_back() 操作. list 和 deque 支持 push_front() 操作. insert()
    则在指定位置添加元素.任何 insert() 和 push() 操作都可能导致迭代器失败. 不要存储 end操作返回的迭代器.
    添加或删除元素都会导致其失效. 可以每次插入之后重新进行 end() 计算.
    <div class="cnblogs_Highlighter">
    <div class="syntaxhighlighter  cpp" id="highlighter_133113">
    <div class="bar  ">
    <div class="toolbar"><a class="item viewsource" style="width: 16px; height: 16px;" title="view source" href="http://www.cnblogs.com/summericeyl/archive/2011/03/04/1971095.html#viewSource">view source</a><a class="item printsource" style="width: 16px; height: 16px;" title="print" href="http://www.cnblogs.com/summericeyl/archive/2011/03/04/1971095.html#printSource">print</a><a class="item about" style="width: 16px; height: 16px;" title="?" href="http://www.cnblogs.com/summericeyl/archive/2011/03/04/1971095.html#about">?</a></div>
    </div>
    <div class="lines">
    <div class="line alt1">
    <table>
        <tbody>
            <tr>
                <td class="number"><code>1</code></td>
                <td class="content"><code class="cpp comments">// last = v.end(); while(first != last); 则是错误的用法</code></td>
            </tr>
        </tbody>
    </table>
    </div>
    </div>
    </div>
    </div>
    while(first != v.end())
    {
    first = v.insert(first, 42);
    ++first;
    }
    </li>
    <li>
    所有的容器类型都支持用关系操作符来实现两个容器的比较. 比较的容器必须具有相同的容器类型和元素类型.
    </li>
    <li>
    顺序容器的大小操作
    </li>
</ol>
<ol>
    <li>
    如果容器非空, 那么可使用 front()和back()成员访问容器第一个和最后一个元素. 在 vector 和 deque中, 可用c[n]或c.at(n) 访问元素. 两个函数的区别就是 at() 访问越界元素会抛出 out_of_range 异常.
    <table border="5">
        <tbody>
            <tr>
                <th>顺序容器的大小操作</th>
                <th>&nbsp;</th>
            </tr>
            <tr>
                <td>c.size()</td>
                <td>c中的元素个数</td>
            </tr>
            <tr>
                <td>c.max_size()</td>
                <td>c中最多可容纳的元素个数</td>
            </tr>
            <tr>
                <td>c.empty()</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>c.resize(n)</td>
                <td>调整容器c的长度大小, 如n&lt;c.size(), 删除多余元素. 否则用默认构造函数初始化新的值</td>
            </tr>
            <tr>
                <td>c.resize(n, t)</td>
                <td>同上, 如果 n &gt; c.size(), 则用t初始化多余的值</td>
            </tr>
        </tbody>
    </table>
    </li>
    <li>
    删除顺序容器内元素的操作
    </li>
</ol>
<ol>
    <li>
    容器的赋值操作符通常都是删除左操作数的所有元素---erase(), 而后插入右操作数的所有元素---insert().
    <table border="5">
        <tbody>
            <tr>
                <th>删除顺序容器内元素的操作</th>
                <th>&nbsp;</th>
            </tr>
            <tr>
                <td>c,erase(P)</td>
                <td>删除迭代器p所指向元素, 返回被删除元素后元素的迭代器</td>
            </tr>
            <tr>
                <td>c.erase(b, e)</td>
                <td>删除一个区间</td>
            </tr>
            <tr>
                <td>c.clear()</td>
                <td>删除所有元素, 返回 void</td>
            </tr>
            <tr>
                <td>c.pop_back()</td>
                <td>删除最后一个元素, 返回void</td>
            </tr>
            <tr>
                <td>c.pop_front()</td>
                <td>删除第一个元素, 返回void, 只适用于 list 和 deque</td>
            </tr>
        </tbody>
    </table>
    <div class="cnblogs_Highlighter">
    <div class="syntaxhighlighter  cpp" id="highlighter_525791">
    <div class="bar ">
    <div class="toolbar"><a class="item viewsource" style="width: 16px; height: 16px;" title="view source" href="http://www.cnblogs.com/summericeyl/archive/2011/03/04/1971095.html#viewSource">view source</a><a class="item printsource" style="width: 16px; height: 16px;" title="print" href="http://www.cnblogs.com/summericeyl/archive/2011/03/04/1971095.html#printSource">print</a><a class="item about" style="width: 16px; height: 16px;" title="?" href="http://www.cnblogs.com/summericeyl/archive/2011/03/04/1971095.html#about">?</a></div>
    </div>
    <div class="lines">
    <div class="line alt1">
    <table>
        <tbody>
            <tr>
                <td class="number"><code>1</code></td>
                <td class="content"><code class="cpp plain">c1 = c2;</code></td>
            </tr>
        </tbody>
    </table>
    </div>
    </div>
    </div>
    </div>
    // 等效于
    c1.erase(c1.begin(), c1.end());
    c1.insert(c2.begin(), c2.end());
    </li>
    <li>
    赋值和assign操作使左操作数容器的所有迭代器失效, swap操作则不会使迭代器失效, swap操作保证在常量时间内完成, 不会删除和插入任何元素, 所以迭代器不会失效. 但是该迭代器指向交换后的另一个容器, 即原来指向的元素所在的容器.
    </li>
</ol>
<ol>
    <li>
    vector 容器的两个成员函数: capacity 和 reserve. 前者用于告知在分配更多存储空间时, 该容器当前的存储空间. 后者用于告知应预留多少元素的存储空间
    <table border="5">
        <tbody>
            <tr>
                <th>顺序容器的赋值操作</th>
                <th>&nbsp;</th>
            </tr>
            <tr>
                <td>c1 = c2</td>
                <td>容器类型和元素类型必须相同</td>
            </tr>
            <tr>
                <td>c1.swap(c2)</td>
                <td>容器类型和元素类型必须相同, 速度比将c2的元素复制到c1的操作快</td>
            </tr>
            <tr>
                <td>c.assign(b, e)</td>
                <td>b, e必须不是指向c中元素的迭代器</td>
            </tr>
            <tr>
                <td>c.assign(n, t)</td>
                <td>&nbsp;</td>
            </tr>
        </tbody>
    </table>
    </li>
    <li>
    string 类型与 vector 类型不同的是, 它不支持以栈方式操纵容器, 在 string 类型中不能使用 front, back 和 pop_back()操作.
    </li>
</ol>
<table border="5">
    <tbody>
        <tr>
            <th>构造string对象的其他方法</th>
            <th>&nbsp;</th>
        </tr>
        <tr>
            <td>string s(cp, n)</td>
            <td>cp所指数组的前n个元素的副本</td>
        </tr>
        <tr>
            <td>string s(s2, pos2)</td>
            <td>&nbsp;</td>
        </tr>
        <tr>
            <td>string s(s2, pos2, len2)</td>
            <td>&nbsp;</td>
        </tr>
    </tbody>
</table>
<table border="5">
    <tbody>
        <tr>
            <th>与容器共有的string操作</th>
            <th>&nbsp;</th>
        </tr>
        <tr>
            <td>s.insert(p, t)</td>
            <td>&nbsp;</td>
        </tr>
        <tr>
            <td>s.insert(p, n, t)</td>
            <td>&nbsp;</td>
        </tr>
        <tr>
            <td>s.insert(p, b, e)</td>
            <td>&nbsp;</td>
        </tr>
        <tr>
            <td>s.assign(b, e)</td>
            <td>用迭代器b, e范围之内的元素替换s, 并返回s</td>
        </tr>
        <tr>
            <td>s.assign(n, t)</td>
            <td>&nbsp;</td>
        </tr>
        <tr>
            <td>s.erase(p)</td>
            <td>&nbsp;</td>
        </tr>
        <tr>
            <td>s.erase(b, e)</td>
            <td>&nbsp;</td>
        </tr>
    </tbody>
</table>
<ol>
    <li>
    只适用于 string 的操作, substr(), append(), replace(), find(); 其中 replace()
    不要求删除的文本长度和插入的相同. find() 函数返回 string::size_type类型的值, 以下标形式标记查找匹配所发生的位置,
    或返回 string::npos; 有 find(), find_first_of, find_first_not_of, rfind,
    find_last; 还有 compare() 函数用于比较, 类似于 strcmp.
    <table border="5">
        <tbody>
            <tr>
                <th>string特有的操作</th>
                <th>&nbsp;</th>
            </tr>
            <tr>
                <td>s.insert(pos, n, c)</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>s.insert(pos, s2)</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>s.inserr(pos, cp, len)</td>
                <td>cp为字符串数组</td>
            </tr>
            <tr>
                <td>s.insert(pos, cp)</td>
                <td>cp为以空字符结束的字符串数组</td>
            </tr>
            <tr>
                <td>s.assign(s2)</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>s.assign(s2, pos, n)</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>s.assign(cp, n)</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>s.assign(cp)</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>s.erase(pos, len)</td>
                <td>&nbsp;</td>
            </tr>
        </tbody>
    </table>
    </li>
    <li>
    容器适配器: queue, priority, queue, stack; 适配器是使一种事物的行为类似于另一事物的行为的一种机制. 例如 stack适配器使任何一种顺序容器以栈的方式使用.
    </li>
</ol>
<ol>
    <li>
    默认的 stack 和 queue 都基于 deque容器实现, priority_queue 则在 vector 容器上实现,
    可通过将一个顺序容器指定为适配器的第二类型参数覆盖默认关联类型, stack类适配器关联类型可以是任意一种顺序容器类型, queue 要求有
    push_front, 所以只能是deque, list; priority_queue要求能随机访问, 所以只能是vector, deque;
    <table border="5">
        <tbody>
            <tr>
                <th>适配器通用的操作和类型</th>
                <th>&nbsp;</th>
            </tr>
            <tr>
                <td>size_type</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>value_type</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>container_type</td>
                <td>基础容器的类型</td>
            </tr>
            <tr>
                <td>A a;</td>
                <td>创建一个新的适配器</td>
            </tr>
            <tr>
                <td>A a(c)</td>
                <td>适配器a为容器c的副本</td>
            </tr>
            <tr>
                <td>关系操作符</td>
                <td>==, !=, &lt;, &lt;=, &gt;, &gt;=</td>
            </tr>
        </tbody>
    </table>
    <div class="cnblogs_Highlighter">
    <div class="syntaxhighlighter  cpp" id="highlighter_612294">
    <div class="bar ">
    <div class="toolbar"><a class="item viewsource" style="width: 16px; height: 16px;" title="view source" href="http://www.cnblogs.com/summericeyl/archive/2011/03/04/1971095.html#viewSource">view source</a><a class="item printsource" style="width: 16px; height: 16px;" title="print" href="http://www.cnblogs.com/summericeyl/archive/2011/03/04/1971095.html#printSource">print</a><a class="item about" style="width: 16px; height: 16px;" title="?" href="http://www.cnblogs.com/summericeyl/archive/2011/03/04/1971095.html#about">?</a></div>
    </div>
    <div class="lines">
    <div class="line alt1">
    <table>
        <tbody>
            <tr>
                <td class="number"><code>1</code></td>
                <td class="content"><code class="cpp plain">stack&lt;string, vector&lt;string&gt; &gt; str_stk;</code></td>
            </tr>
        </tbody>
    </table>
    </div>
    </div>
    </div>
    </div>
    </li>
    <li>
    栈适配器支持的操作
    </li>
</ol>
<ol>
    <li>
    队列和优先级队列支持的操作
    <table border="5">
        <tbody>
            <tr>
                <th>栈适配器支持的操作</th>
                <th>&nbsp;</th>
            </tr>
            <tr>
                <td>s.empty()</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>s.size()</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>s.pop()</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>s.top()</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>s.push(item)</td>
                <td>&nbsp;</td>
            </tr>
        </tbody>
    </table>
    </li>
</ol>
<table border="5">
    <tbody>
        <tr>
            <th>队列和优先级队列支持的操作</th>
            <th>&nbsp;</th>
        </tr>
        <tr>
            <td>q.empty()</td>
            <td>&nbsp;</td>
        </tr>
        <tr>
            <td>q.size()</td>
            <td>&nbsp;</td>
        </tr>
        <tr>
            <td>q.pop()</td>
            <td>&nbsp;</td>
        </tr>
        <tr>
            <td>q.front()</td>
            <td>&nbsp;</td>
        </tr>
        <tr>
            <td>q.back()</td>
            <td>&nbsp;</td>
        </tr>
        <tr>
            <td>q.top()</td>
            <td>&nbsp;</td>
        </tr>
        <tr>
            <td>q.push(item)</td>
            <td>&nbsp;</td>
        </tr>
    </tbody>
</table>
<br><img src ="http://www.cppblog.com/summericeyl/aggbug/141124.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/summericeyl/" target="_blank">夏冰</a> 2011-03-04 17:44 <a href="http://www.cppblog.com/summericeyl/archive/2011/03/04/141124.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ Primer 第8章笔记</title><link>http://www.cppblog.com/summericeyl/archive/2011/03/03/141043.html</link><dc:creator>夏冰</dc:creator><author>夏冰</author><pubDate>Thu, 03 Mar 2011 03:42:00 GMT</pubDate><guid>http://www.cppblog.com/summericeyl/archive/2011/03/03/141043.html</guid><wfw:comment>http://www.cppblog.com/summericeyl/comments/141043.html</wfw:comment><comments>http://www.cppblog.com/summericeyl/archive/2011/03/03/141043.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/summericeyl/comments/commentRss/141043.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/summericeyl/services/trackbacks/141043.html</trackback:ping><description><![CDATA[<ol>
    <li>
    国际字符的支持, wchar_t类型的标准输入对象为 wcin, 标准输出为 wcout, 标准错误为 wcerr.
    </li>
    <li>
    IO对象不可复制或赋值: (1) 由于不可复制, 所以不能存储在 vector(或其他)容器中. (2) 形参或返回类型不能为流类型, 只能为流的引用或者指针类型.
    </li>
    <li>
    IO标准库管理一系列条件状态(condition state)用来标记给定的IO对象是否处于可用状态, 或碰到了哪种特定的错误.
    </li>
</ol>
<ol>
    <li>
    流必须处于无错误状态, 才能用于输入或输出. 检测流是否可用的最简单的方法是检查其真值:
    <table>
        <tbody>
            <tr>
                <th>&nbsp;</th>
                <th>IO标准库的条件状态</th>
            </tr>
            <tr>
                <td>strm::iostate</td>
                <td>机器相关的整型名, 由各个 iostream 类定义, 用于定义条件状态</td>
            </tr>
            <tr>
                <td>strm::badbit</td>
                <td>strm::iostate类型的值, 用于指出被破坏的流</td>
            </tr>
            <tr>
                <td>strm::failbit</td>
                <td>strm::iostate类型的值, 用于指出失败的IO操作</td>
            </tr>
            <tr>
                <td>strm::eofbit</td>
                <td>strm::iostate类型的值, 用于指出流已经到达文件结束符</td>
            </tr>
            <tr>
                <td>s.eof()</td>
                <td>如果设置了流的eofbit值, 则函数返回true</td>
            </tr>
            <tr>
                <td>s.fail()</td>
                <td>如果设置了流的failbit值, 则函数返回true</td>
            </tr>
            <tr>
                <td>s.bad()</td>
                <td>如果设置了流的badbit值, 则函数返回true</td>
            </tr>
            <tr>
                <td>s.good()</td>
                <td>如果流a处于有效状态, 则函数返回true</td>
            </tr>
            <tr>
                <td>s.clear()</td>
                <td>将流s中的所有状态值都冲设为有效状态</td>
            </tr>
            <tr>
                <td>s.clear(flag)</td>
                <td>将流s中的某个指定条件状态设置为有效, flag的类型是 strm::iostate</td>
            </tr>
            <tr>
                <td>s.setstate(flag)</td>
                <td>给流s添加指定条件, flag的类型是 strm::iostream</td>
            </tr>
            <tr>
                <td>s.rdstate()</td>
                <td>返回流s的当前条件, 返回值类型为 strm::iostate</td>
            </tr>
        </tbody>
    </table>
    <pre>if(cin)<br>	// 有效状态<br><br>while(cin &gt;&gt; word)<br>	// 读操作成功<br></pre>
    </li>
    <li>
    各种io错误
    </li>
    <ul>
        <li>
        badbit: 系统级的故障, 如无法恢复的读写错误. 通常该流不能再继续使用.
        </li>
        <li>
        failbit: 可恢复的错误, 如在应输入数值型数据时输入字符.
        </li>
        <li>
        eofbit: 遇到文件结束符, 此时还同时设置了 failbit.
        </li>
    </ul>
    <li>
    rdstate 成员函数返回一个 iostate类型的值, 表示流当前的整个条件状态
    <pre>istream::iostate old_state = cin.rdstate();<br>cin.clear();<br>process_input();	// 使用 cin<br>cin.clear(old_state);	// 恢复到原来的状态.<br></pre>
    </li>
    <li>
    使用或按位或操作符(OR)打开多个状态位
    <pre>is.setstate(ifstream::badbit | ifstream::failbit);		// 同时打开了 badbit和failbit<br></pre>
    </li>
    <li>
    输出缓冲区, 何时刷新输出缓冲区
    </li>
    <ul>
        <li>
        程序正常结束时
        </li>
        <li>
        缓冲区满的时候, 写下一个值之前刷新
        </li>
        <li>
        使用操纵符(manipulator)显式刷新缓冲区, 例如行结束符 endl;
        </li>
        <li>
        每次输出操作执行完后, 用 unitbuf 操纵符设置流的内部状态, 从而清空缓冲区.
        </li>
        <li>
        将输出流和输入流关联起来, 读输入流时刷新其关联的输出缓冲区.
        </li>
    </ul>
    <li>
    两个刷新输出缓冲区的操纵符: flush-刷新流, 但不添加任何字符; ends-插入空字符null, 然后刷新它.
    </li>
    <li>
    操纵符 unitbuf - 每次执行完写操作后都刷新流, 如:
    <pre>cout &lt;&lt; unitbuf &lt;&lt; "first" &lt;&lt; " second" &lt;&lt; nounitbuf;<br>// 等价于<br>cout &lt;&lt; "first" &lt;&lt; flush &lt;&lt; "second" &lt;&lt; flush;<br></pre>
    </li>
    <li>
    tie成员函数用于绑定一个 ostream对象到调用该函数的对象上, 其参数为 ostream对象指针. 这样调用的类对象的任何IO操作都会刷新 ostream对象参数的缓冲区. 一个ostream对象一次只能与一个istream对象绑定到一起.
    <pre>cin.tie(cout);		// 缺省<br>ostream *old_tie = cin.tie();	// 得到现在绑定的输出对象指针<br>cin.tie(0);  // 取消绑定<br>cin.tie(&amp;cerr);		<br>// ...<br>cin.tie(0);<br>cin.tie(old_tie);<br></pre>
    </li>
    <li>
    文件输入和输出流的三个类: ifstream, ofstream, fstream. 两个额外的操作函数 open 和 close. 以及形参为要打开的文件名的构造函数. 注意形参为C风格字符串.
    </li>
    <li>
    关闭流并不能改变文件流的状态, 要调用clear()才能重置状态.
    <pre>ifstream input;<br>vector&lt;string&gt;::const_iterator it = files.begin();<br>while(it != files.end())<br>{<br>	input.open(it-&gt;c_str());<br>	if(!input)<br>		break;<br>	while(input &gt;&gt; s)<br>		process(s);<br>	input.close();<br>	input.clear();	// 如果不调用该函数, 则输入流的 eofbit 始终被设置.<br>	++it;<br>}<br></pre>
    </li>
    <li>
    文件模式, 可用位操作符设置一个或多个模式
    </li>
</ol>
<ol>
    <li>
    out模式等效于同时制定了 out和trunc模式.
    <table>
        <tbody>
            <tr>
                <th>文件模式</th>
                <th>&nbsp;</th>
            </tr>
            <tr>
                <td>in</td>
                <td>打开文件做读操作</td>
            </tr>
            <tr>
                <td>out</td>
                <td>打开文件做写操作</td>
            </tr>
            <tr>
                <td>app</td>
                <td>在每次写之前找到文件尾</td>
            </tr>
            <tr>
                <td>ate</td>
                <td>打开文件后立即将文件定位到文件尾</td>
            </tr>
            <tr>
                <td>trunc</td>
                <td>打开文件时清空已存在的文件流</td>
            </tr>
            <tr>
                <td>binary</td>
                <td>以二进制模式进行IO操作</td>
            </tr>
        </tbody>
    </table>
    </li>
    <li>
    fstream 可同时读写它所关联的文件, 默认以in和out模式打开, 不清空已存在的文件流. 如只指定out, 则会清空已存在的文件流. 如设定了trunc模式, 无论是否存在in模式都会清空文件流.
    </li>
    <li>
    有效的文件模式组合: out, out|app, out|trunc, in, in|out, in|out|trunc. 这些模式都可增加 ate 模式.
    </li>
    <li>
    字符串流 istringstream, ostringstream, stringstream. 该流对象增加了 string形参的构造函数以及名为str的string类型内部成员
    </li>
</ol>
<ol>
    <li>
    stringstream 常用于数据类型之间实现自动格式化, 使用输入字符串流时, 空白符和换行符都会被忽略. 例如:
    <table>
        <tbody>
            <tr>
                <th>stringstream特定的操作</th>
                <th>&nbsp;</th>
            </tr>
            <tr>
                <td>stringstream strm</td>
                <td>默认构造</td>
            </tr>
            <tr>
                <td>stringstream strm(s)</td>
                <td>s为string类型</td>
            </tr>
            <tr>
                <td>strm.str()</td>
                <td>返回strm中存储的string类型对象</td>
            </tr>
            <tr>
                <td>strm.str(s)</td>
                <td>将s复制给strm</td>
            </tr>
        </tbody>
    </table>
    <pre>int val1 = 512, val2 = 1024;<br>ostringstream format_message;<br>format_message &lt;&lt; "Val1: " &lt;&lt; val1 &lt;&lt; "\n"<br>	&lt;&lt; "val2: " &lt;&lt; val2 &lt;&lt; "\n";<br>istringstream input_string(format_message.str());<br>string dump;<br>input_string &gt;&gt; dump &gt;&gt; val1 &gt;&gt; dump &gt;&gt; val2;<br>cout &lt;&lt; val1 &lt;&lt; " " &lt;&lt; val2 &lt;&lt; endl;<br></pre>
    </li>
</ol>
<br><img src ="http://www.cppblog.com/summericeyl/aggbug/141043.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/summericeyl/" target="_blank">夏冰</a> 2011-03-03 11:42 <a href="http://www.cppblog.com/summericeyl/archive/2011/03/03/141043.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ Primer 第14章笔记</title><link>http://www.cppblog.com/summericeyl/archive/2011/03/02/140996.html</link><dc:creator>夏冰</dc:creator><author>夏冰</author><pubDate>Wed, 02 Mar 2011 09:26:00 GMT</pubDate><guid>http://www.cppblog.com/summericeyl/archive/2011/03/02/140996.html</guid><wfw:comment>http://www.cppblog.com/summericeyl/comments/140996.html</wfw:comment><comments>http://www.cppblog.com/summericeyl/archive/2011/03/02/140996.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/summericeyl/comments/commentRss/140996.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/summericeyl/services/trackbacks/140996.html</trackback:ping><description><![CDATA[<p>
第14章 重载操作符与转换
</p>
<ol>
    <li>
    重载操作符的形参数目与操作符的操作数数目相同.
    </li>
    <li>
    可重载的操作符: +, -, *, /, %, ^, &amp;, |, ~, !, ,, =, &lt;, &gt;, &lt;=,
    &gt;=, ++, --, &lt;&lt;, &gt;&gt;, ==, !=, &amp;&amp;, ||, +=, -=, /=,
    %=, ....
    </li>
    <li>
    不可重载的操作符: ::, .*, ., ?:
    </li>
    <li>
    不能通过连接其他合法符号来创建新的操作符.
    </li>
    <li>
    用于内置类型的操作符, 其含义不能改变. 所以重载操作符必须具有至少一个类类型或枚举类型的操作数.
    </li>
    <li>
    操作符的优先级, 结合性和操作数数目不能改变
    </li>
    <li>
    除了函数操作符operator ()之外, 重载操作符使用默认实参是非法的.
    </li>
    <li>
    作为类成员函数的操作符有一个隐含的this形参, 限定为第一个参数.
    </li>
    <li>
    一般将算术运算和关系操作符定义为非成员函数, 而赋值操作符定义为类的成员函数. 复合赋值返回对左操作符的引用.
    </li>
    <li>
    操作符定义为非成员函数时, 通常必须将它们设置为所操作类的友元.<br>
    <pre>class Sales_item{<br>	friend std::istream&amp; operator&gt;&gt;<br>		(std::istream&amp;, Sales_item&amp;);<br>	friend std::ostream&amp; operator&lt;&lt;<br>		(std::ostream&amp;, const Sales_item&amp;);<br>public:<br>	Sales_item&amp; operator+=(const Sales_item&amp;);<br>}<br>// 可以通过 Sales_item类的成员函数 operator+= 实现加法, 所以不将 operator+ 函数设置为友元<br>Sales_item operator+(const Sales_item&amp;, const Sales_item);<br></pre>
    </li>
    <li>
    可以像调用普通函数一样调用重载操作符函数, 例如:<br>
    <pre>// 假设 item1 和 item2 是 Sales_item对象, 则下面两个表达式等价<br>cout &lt;&lt; item1 + item2 &lt;&lt; endl;<br>cout &lt;&lt; operator+(item1 + item2) &lt;&lt;endl;<br>// 下面也等价<br>item1 += item2;<br>item1.operator+=(item2);<br></pre>
    </li>
    <li>
    不要重载具有内置含义的操作符, 如赋值操作符, 取地址操作符, 逗号操作符. 合成赋值操作符会逐个成员赋值;
    取地址操作符(&amp;)返回类对象的地址; 逗号操作符(,)从左至右计算每个表达式的值, 返回最右边操作数的值.
    内置逻辑与(&amp;&amp;)和逻辑或(||)操作符使用短路求值, 如果重定义该操作符, 将失去操作符的短路求值特征.
    如果我们要重载赋值操作符, 在赋值之后, 左右操作数的值应该相同, 并返回对左操作数的引用.
    </li>
    <li>
    一般重载的操作符: 相等测试操作应使用 operator==; 通过重载移位操作符(&gt;&gt;和&lt;&lt;)进行输入和输出;
    测试对象是否为空的操作可用逻辑非操作符 operator! 表示. 如果一个类有算术操作符或为操作符, 那么最好提供相应的复合赋值操作符, 例如
    operator+=.
    </li>
    <li>
    当一个重载操作符的含义不明显时, 给操作取一个名字更好; 对于很少用到的操作使用命名函数比用操作符更好; 如果不是普通函数, 没有必要为了简洁而使用操作符.
    </li>
    <li>
    将要用作关联容器元素类型的类应当定义 operator&lt; 操作符, 类通常也应当定义相等(==)操作符, 许多算法都假定相等和小于操作符存在. 类此, 也可定义(!=), (&gt;, &gt;=, &lt;=, &lt;)操作符.
    </li>
    <li>
    选择成员或非成员函数实现, (1) 赋值(=), 下标([]), 调用(())和成员访问箭头(-&gt;)等操作符必须定义为成员. (2)
    复合赋值操作符通常也定义为类的成员; (3) 改变对象状态或与给定类型紧密联系的其他一些操作符, 如自增, 自减和解引用, 通常应定义为类成员.
    (4) 对称的操作符, 如算术操作符, 相等操作符, 关系操作符和位操作符, 最好定义为普通非成员函数.
    </li>
    <li>
    输出操作符(&lt;&lt;)的重载, 一般的简单定义如下:<br>
    <pre>ostream&amp;<br>operator&lt;&lt;(ostream&amp; os, const ClassType &amp;object)<br>{<br>	// ... 其他操作<br>	// 输出<br>	os &lt;&lt; // ...<br>	return os;<br>}<br></pre>
    </li>
    <li>
    一个const的类对象, 只能调用该类对象的 const 成员函数, 例如:<br>
    <pre>const Sales_item&amp; s;<br>// ...<br>s.avg_price();		// avg_price() 是 Sales_item类的const成员函数.<br></pre>
    </li>
    <li>
    输出操作符所做的格式化应该尽可能少, 比如不应该输出换行符, 让用户自己控制输出细节. IO操作符必须为非成员函数.
    </li>
    <li>
    重载输入操作符必须处理错误和文件结束的可能性.
    </li>
    <li>
    输入流可通过检查流来判断输入是否成功. 如果用户需要设置输入流失败, 则设输入操作符仅需设置 failbit即可. eofbit表示文件耗尽, badbit表示流被破坏. 这些错误最好留给IO标准库自己来指出.
    </li>
    <li>
    算术运算操作符, 例如其中加法运算操作符, 参数为const引用类型, 返回的值为类类型, 而非引用,
    在函数体内应创建一个临时变量计算最终的结果, 而后返回该临时变量. 如定义了该算术操作符相关的复合赋值操作符,
    一般应使用该复合赋值操作符实现算术操作符.
    </li>
    <li>
    如果类定义了==操作符, 该操作符的函数是两个对象包含相同的数据成员. 如果定义了operator==, 那么也应当定义operator!=.
    定义了相等操作符, 一般也会定义operator&lt;等关系操作符. 一般而言, 关系操作符, 诸如相等操作符应定义为非成员函数.
    </li>
    <li>
    赋值操作符可以重载, 无论形参为何类型, 赋值操作符必须定义为成员函数, 这一点和复合赋值操作符有所不同. 且赋值要返回对 *this的引用, 这样就不需要创建和撤销结果的临时副本. 一般而言, 赋值和复合赋值操作符应返回左操作数的引用.
    </li>
    <li>
    下标操作符必须定义为类成员. 定义下标操作符比较复杂的地方在于, 它在用作赋值的左右操作数时都应该表现正常. 所以一般要定义两个版本, 一个为非const成员并返回引用, 另一个为const成员并返回const引用. <br>
    <pre>int&amp; Foo::operator[] (const size_t index)<br>{<br>	return data[index];<br>}<br>const int&amp; Foo::operator[] (const size_t index) const<br>{<br>	return data[index];<br>}<br></pre>
    </li>
    <li>
    引用操作符(&amp;)和箭头操作符(-&gt;)的重载, 箭头操作符必须为成员函数, 解引用操作符不要为定义为成员函数, 但一般定义为成员函数. 与下标操作符一样, 解引用操作符我们需要const和非const版本.
    </li>
    <li>
    箭头操作符在编译器内的操作: 例如 point-&gt;action(); 其中假设pointer为定义了operator-&gt;操作符的类,
    则执行 point.operator-&gt;()-&gt;action(); 然后递归执行, 直到找到action()函数.
    所以重载箭头操作符必须返回指向类类型的指针, 或者返回定义了自己的箭头操作符的类类型对象.
    </li>
    <li>
    自增自减(++, --)操作符的重载, 其中分为前缀和后缀, 后缀的重载带一个int形参, 该形参没有任何用处, 只是用来分别前缀和后缀. 前缀操作符返回一个引用, 后缀操作符不返回引用, 而是返回一个操作之前该类对象的副本.
    </li>
    <li>
    调用操作符, 一般为表示操作的类重载调用操作符. 函数调用操作符必须声明为成员函数 一个类可以定义函数调用操作符的多个版本, 由形参的数目或类型加以区别. 这种类的对象成为(Function object), 函数对象/仿函数.
    </li>
    <li>
    将函数对象应用于标准库算法, 可根据不同的状态创建不同的函数对象, 无需重新定义多个函数. 例如 count_if 算法:<br>
    <pre>bool GT6(const string&amp; s)<br>{<br>	return s.size() &gt; 6;<br>}<br>vector&lt;string&gt;::size_type wc = count_if(words.begin(), words.end(), GT6);<br>// 上面算法的缺点就是如果要计算大于7, 8或者其他数字的单词数目, 就要重新定义函数. 使用函数对象就没有这么麻烦, 例如<br>class GT_cls<br>{<br>public:<br>	GT_cls(size_t val = 0) : bound(val) {}<br>	bool operator()(const string&amp; s)<br>	{<br>		return s.size() &gt; bound;<br>	}<br>private:<br>	std::string::size_type bound;<br>};<br>vector&lt;string&gt;::size_type wc = count_if(words.begin(), words.end(), GT_cls(6));<br></pre>
    </li>
    <li>
    标准库定义的函数对象, 在 functional 中定义的
    </li>
</ol>
<ol>
    <li>
    每个标准库函数对象类表示一个操作符. 每个函数对象类都是一个类模板, 例如:<br>
    <table>
        <tbody>
            <tr>
                <th>类型</th>
                <th>函数对象</th>
                <th>所应用的操作符</th>
            </tr>
            <tr>
                <td>算术函数对象类型</td>
                <td>plus&lt;Type&gt;</td>
                <td>+</td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>minus&lt;Type&gt;</td>
                <td>-</td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>multiplies&lt;Type&gt;</td>
                <td>*</td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>divides&lt;Type&gt;</td>
                <td>/</td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>modulus&lt;Type&gt;</td>
                <td>%</td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>negate&lt;Type&gt;</td>
                <td>-</td>
            </tr>
            <tr>
                <td>关系函数对象类型</td>
                <td>equal_to&lt;Type&gt;</td>
                <td>==</td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>not_equal_to&lt;Type&gt;</td>
                <td>!=</td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>greater&lt;Type&gt;</td>
                <td>&gt;</td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>greater_equal&lt;Type&gt;</td>
                <td>&gt;=</td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>less&lt;Type&gt;</td>
                <td>&lt;</td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>less_equal&lt;Type&gt;</td>
                <td>&lt;=</td>
            </tr>
            <tr>
                <td>逻辑对象类型</td>
                <td>logical_and&lt;Type&gt;</td>
                <td>&amp;&amp;</td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>logical_or&lt;Type&gt;</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>logical_not&lt;Type&gt;</td>
                <td>!</td>
            </tr>
        </tbody>
    </table>
    <pre>plus&lt;int&gt; intAdd;<br>negate&lt;int&gt; intNegate;<br>int sum = intAdd(10, 30);<br>sum = intAdd(19, intNegate(10));<br></pre>
    </li>
    <li>
    函数适配器(function adapter), 用于特化和扩展一元和二元函数对象. 分为两类: (1)绑定器(binder),
    将一个操作数绑定到给定值, 从而将二元函数对象转换为一元函数对象. 有 bind1st 和 bind2nd. (2) 求反器(negator),
    将函数对象的真值求反. 有 not1, not2. 前者将一元函数对象的值求反, 后者将二元函数对象的值求反. 例如:<br>
    <pre>// 所小于等于10的元素个数<br>count_if(vec.begin(), vec.end(), bind2nd(less_equal&lt;int&gt;(), 10));<br>not1(count_if(vec.begin(), vec.end(), bind2nd(less_equal&lt;int&gt;(), 10)));<br></pre>
    </li>
    <li>
    一个类可以定义自己的转换, 将自己转换至其他类的类型. 例如有一个用户定义的类 SmallInt, 定义了到int的转换, 这样就可以实现
    SmallInit 类对象和其他内置类型的算术运算操作. 这是由于SmallInt可以转换为int之后再进行算术操作.
    转换操作符是一种特殊的类成员函数, 定义将类类型转换为其他类类型的转换. 在类定义体内声明, 在保留字 operator
    之后跟着转换的目标类型, 转换函数的通用形式: operator type(); 转换函数一般不改变被转换的对象, 所以转换函数一般为
    const 成员函数. 例如:<br>
    <pre>class SmallInt {<br>public:<br>	SmallInt(int i = 0) : val(i)<br>	{<br>		if(i &lt; 0 || i &gt; 255)<br>		{<br>			throw std::out_of_range("Bad Smallint initializer");<br>		}<br>	}<br>	operator int() const { return val; }<br>private:<br>	std::size_t val;<br>};<br></pre>
    </li>
    <li>
    只能应用一个类型转换, 例如 Integral 内有转换函数 operator SmallInt(), 则 Integral 对象只能用于需要 SmallInt对象的地方, 不能用于需要 int 类型的地方.
    </li>
    <li>
    标准转换可放在类类型转换之前, 例如 SmallInt 类构造函数的参数为一个 double值, 则首先转换 double 值为 int, 而后调用 SmallInt(int) 构造函数.
    </li>
    <li>
    实参匹配和二义性: (1) 多个转换操作符导致的二义性, 比如有 operator int() 和 operator double(), 遇到
    long double 类型产生二义性. (2) 构造函数导致的二义性, 例如构造函数接受int和double类型, 遇到 long 类型,
    则产生二义性. (2) 定义转换产生的二义性, 例如A类的构造函数的参数为B类类型, 而B类类型中存在operator A();成员函数,
    实现B到A的转换操作, 这样从B到A有隐式转换和转换操作符两种转换, 产生二义性.
    </li>
    <li>
    避免二义性最好的方法是: 保证最多只有一种途径将一个类型转换为另一个类型, 限制转换操作符的数目, 且到一种内置类型应该只有一种转换.
    </li>
    <li>
    函数重载确定, 即确定使用哪个重载函数: (1) 确定候选函数集合, 即与被调用函数同名的函数; (2) 选择可行的函数,
    即形参数目和类型与函数调用中的实参相匹配的候选函数, 如有转换操作, 确定需要哪个转换操作来匹配每个形参; (3) 选择最佳匹配的函数,
    对将实参转换为对应形参所需的类型转换进行分类.
    </li>
    <li>
    操作符重载一定要确定使用哪个版本的操作符函数. 成员函数和非成员函数都是可能的候选函数. 举例:<br>
    <pre>class SmallInt{<br>	SmallInt(int = 0);<br>	operator int() const;<br>	friend SmallInt operator+(const SmallInt&amp;, const SmallInt&amp;); <br>};<br><br>SmallInt s1, s2;<br>SmallInt s3 = s1 + s2;<br>int i = s3 + 0;		// 这一句产生了二义性. 是用 SmallInt operator+() 版本, 还是用 int内置的 operator+().<br></pre>
    </li>
    <li>
    正确地设计类的重载操作符:
    </li>
    <ul>
        <li>
        不要定义相互转换的类, 例如类Foo具有接受类Bar的对象的构造函数, 不要再为类Bar定义到类型Foo的转换操作符
        </li>
        <li>
        避免到内置算术类型的转换, 即
        </li>
        <ul>
            <li>
            不要定义接受算术类型的操作符的重载版本. 而是选择提供到算术类型的转换, 如 operator int(), 如果用户需要使用算术运算符, 如+,-,*,/等, 使用转换操作符转换你所定义的类类型为算术类型, 而后使用内置操作符.
            </li>
            <li>
            不要定义转换到一个以上算术类型的转换, 例如, 同时提供到int和double的转换.
            </li>
        </ul>
    </ul>
</ol>
<br><img src ="http://www.cppblog.com/summericeyl/aggbug/140996.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/summericeyl/" target="_blank">夏冰</a> 2011-03-02 17:26 <a href="http://www.cppblog.com/summericeyl/archive/2011/03/02/140996.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ Primer 第13章笔记</title><link>http://www.cppblog.com/summericeyl/archive/2011/02/28/140831.html</link><dc:creator>夏冰</dc:creator><author>夏冰</author><pubDate>Mon, 28 Feb 2011 09:29:00 GMT</pubDate><guid>http://www.cppblog.com/summericeyl/archive/2011/02/28/140831.html</guid><wfw:comment>http://www.cppblog.com/summericeyl/comments/140831.html</wfw:comment><comments>http://www.cppblog.com/summericeyl/archive/2011/02/28/140831.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/summericeyl/comments/commentRss/140831.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/summericeyl/services/trackbacks/140831.html</trackback:ping><description><![CDATA[<p>第13章 复制控制 </p>
<ol>
    <li>复制构造函数: 特殊的构造函数, 具有单个形参, 该形参(常用const修饰)是对该类类型的引用. 用该类对象对一个新的该类对象初始化时, 则显式使用该复制构造函数; 当将该类对象传递给函数或者从函数返回该类的对象时, 则隐式使用复制构造函数. 在初始化顺序容器中的元素, 以及根据元素初始化式列表初始化数组元素时, 也会隐式使用复制构造函数.  </li>
    <li>析构函数: 当该类对象超出作用域或动态分配的对象被删除时, 将自动应用析构函数. 用于释放构造对象时或在对象生命期中所获取的资源. 无论类是否定义了自己的析构函数, 编译器都自动执行类中非 static 数据成员的析构函数.  </li>
    <li>赋值操作符: 该操作符可通过指定不同类型的右操作数而重载. 如果没有写右操作数为该类类型的版本, 则编译器将为我们合成一个.  </li>
    <li>复制构造函数, 析构函数, 赋值操作符总称为复制控制(copy control). 实现复制控制最困难的部分, 在于识别何时需要覆盖默认版本. 当类具有指针成员时, 应当定义自己的复制控制成员.  </li>
    <li>直接初始化和复制初始化的区别: 直接初始化直接调用与实参匹配的构造函数, 复制初始化总是使用复制构造函数, 使用"="符号实现, 例如:
    <pre>string null_book = "9-999-99999-9";	// 复制初始化, 首先调用接受C风格字符串的string构造函数创建临时对象, 而后用该对象调用复制构造函数
    string dots(10, '.');				// 直接初始化
    string empty_copy = string();		// 复制初始化, 默认构造函数创建了临时对象, 而后调用复制构造函数
    string empty_direct;				// 直接初始化, 默认构造函数</pre>
    </li>
    <li>当形参和返回值为非引用的类类型时, 使用复制构造函数作为形参或返回值.
    </li>
    <li>初始化顺序容器元素, 使用了默认构造函数和复制构造函数
    <pre>vector&lt;string&gt; svec(5);	// 首先使用默认构造函数创建临时对象, 而后用该临时对象调用复制构造函数复制到svec的每个元素中.</pre>
    </li>
    <li>使用常规的花括号括住的数组元素初始化列表提供了显式元素初始化式, 此时使用复制初始化来初始化每个元素.
    <pre>Sales_item primer_eds[] = { string("0-201-16487-6"),
    string("0-201-54848-8"),
    string("0-201-82470-1"),
    Sales_item
    };</pre>
    </li>
    <li>编译器合成的复制构造函数, 即使我们定义了其他构造函数, 编译器也会合成复制构造函数. 其操作为逐个初始化成员, 将新的对象初始化为原对象的副本. 内置类型成员直接复制其值, 类类型成员则使用该类的复制构造函数进行复制, 如类里有数组数据成员, 则复制数组的每一个元素. 例如:
    <pre>class Sales_item {
    // 其他数据成员和构造函数
    private:
    std::string isbn;
    int units_sold;
    double revenue;
    };
    // 则编译器合成的复制构造函数如下
    Sales_item::Sales_item(const Sales_item &amp;orig) :
    isbn(orig.isbn),
    units_sold(orig.units_sold),
    revenue(orig.revenue) {}</pre>
    </li>
    <li>复制构造函数一般不应设置为 explicit, 对于只包含类类型成员和内置类型成员的类, 无需显示地定义复制构造函数. 对于数据成员类型为指针的类, 或者有成员在构造函数中分配其他资源, 或者一些类需要做一些特定工作的类, 需要显示地定义复制构造函数.
    </li>
    <li>为了防止复制构造函数用于复制该类, 可以将复制构造函数设置为 private, 为了防止类的友元和成员进行复制, 可以在类中只声明复制构造函数而不定义它. 这个不允许复制的类只能作为引用传递给函数或者从函数返回. 如果定义了复制构造函数, 就必须定义默认构造函数, 这是由于定义了构造函数, 编译器将不会自动合成默认构造函数.
    </li>
    <li>重载操作符, 当操作符为成员函数时, 它的第一个操作符隐式绑定到 this 指针. 赋值操作符的返回类型为右操作数类型的引用. 例如:
    <pre>class Sales_item{
    public:
    // 其他成员
    // 等同合成赋值操作符
    Sales_item&amp; operator=(const Sales_item &amp;);
    };</pre>
    </li>
    <li>编译器合成的赋值操作符和合成复制构造函数一样, 会逐个成员赋值(memberwise assignment), 右操作数对象的每个成员赋值给左操作数对象的对应成员, 对于数组, 给每个数组元素赋值. 例如:
    <pre>Sales_item&amp; Sales_item::operator=(const Sales_item &amp;rhs)
    {
    isbn = rhs.isbn;
    units_sold = rhs.units_sold;
    revenus = rhs.revenus;
    return *this;
    }</pre>
    </li>
    <li>可以将复制构造函数和赋值操作符看成一个单元, 如果需要其中一个, 我们几乎也肯定需要另一个.
    </li>
    <li>撤销一个容器(无论是标准库容器还是内置数组)时, 也会运行容器中的类类型元素的析构函数, 其按照容器元素的逆序调用析构函数, 如总是从容器的最后一个元素开始, 直到第一个元素调用析构函数完成所有的撤销工作.
    <pre>{
    Sales_item *p = new Sales_item[10];
    vector&lt;Sales_item&gt; vec(p, p+10);
    // ...
    delete [] p;	// 删除数组元素
    }	// vector 容量到了作用域范围之外, 开始撤销其内的元素.</pre>
    </li>
    <li>析构函数常用来释放构造函数或在对象生命期内获取的资源. 如果类需要析构函数, 则它也需要赋值操作符和复制构造函数, 即三法则规则(role of three)
    </li>
    <li>合成析构函数, 系统总是为我们合成一个析构函数, 按照数据成员声明的逆序撤销每个非static成员. 在执行外用于定义的析构函数之后, 编译器总会执行这个合成析构函数.
    </li>
    <li>编写自己的复制构造函数时, 必须显式复制需要复制的任意成员. 显式定义的复制构造函数不会进行任何自动复制.
    </li>
    <li>赋值操作符通常要做复制构造函数和析构函数也要完成的工作. 注意, 赋值操作符首先要删除左操作数, 所以需要完成析构函数的工作, 而后进行复制构造函数操作. 所以在赋值操作符的重载中, 要检查右操作数的类型是否等于当前类型.
    </li>
    <li>当一个类里有指针数据成员, 如果使用默认的合成复制构造函数, 则新对象和旧对象的指针数据成员指向同一个对象. 这里提供了两个选择方法: (1)使用智能指针, 该智能指针类里有使用计数, 由带指针数据成员的类控制智能指针类对象的创建和删除, 复制构造函数将指针从旧对象复制到新对象时, 使用计数加1; 赋值操作符将左边的使用计数减1, 右操作数的计数加1, 而后右操作数复制到左操作数. 析构函数将使用计数减1. (2) 每一次复制都通过创建一个新的对象, 将该对象的指针赋值给指针数据成员. 这样每个类的对象都有一个副本, 相互之间不影响. </li>
</ol><img src ="http://www.cppblog.com/summericeyl/aggbug/140831.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/summericeyl/" target="_blank">夏冰</a> 2011-02-28 17:29 <a href="http://www.cppblog.com/summericeyl/archive/2011/02/28/140831.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ Primer 第12章笔记</title><link>http://www.cppblog.com/summericeyl/archive/2011/02/27/140753.html</link><dc:creator>夏冰</dc:creator><author>夏冰</author><pubDate>Sun, 27 Feb 2011 11:30:00 GMT</pubDate><guid>http://www.cppblog.com/summericeyl/archive/2011/02/27/140753.html</guid><wfw:comment>http://www.cppblog.com/summericeyl/comments/140753.html</wfw:comment><comments>http://www.cppblog.com/summericeyl/archive/2011/02/27/140753.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/summericeyl/comments/commentRss/140753.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/summericeyl/services/trackbacks/140753.html</trackback:ping><description><![CDATA[<p>第12章 类 </p>
<ul>
    <li>类中 const 成员不能改变其所操作的对象的数据成员, const 必须同时出现在声明和定义中.  </li>
    <li>如果一个类具有多个同一类型的数据成员, 则这些成员可以在一个成员声明中指定  </li>
    <li>类可以定义自己的局部类型名字, 例如:
    <pre>class Screen{
    public:
    typedef std::string::size_type index;
    // ...
    };</pre>
    </li>
    <li>在类内部定义的成员函数, 将自动作为 inline 处理. 不在类体内定义的 inline 成员函数, 其定义通常应该放在有类定义的同一头文件中, 注意如不在类体内定义, 则应在类体内声明时和类体外定义时加上 inline 关键字
    </li>
    <li>声明一个类而不定义它, 称为前向声明, 一个类在声明之后, 定义之前是一个不完全类型(incompete type), 不完全类型只能用于指向该类型的指针和引用的定义, 或者使用该类型作为形参或返回值的声明. 类的前向声明一般用来编写相互依赖的类.
    </li>
    <li>在创建类的对象时, 必须已经完整地定义了该类. 在使用引用或指针访问该类的成员之前, 也必须已经完整地定义了该类. 如果类已经完整地定义了, 则之后的类可以使用该类类型作为数据成员的类型, 如果是不完整类型, 则只能使用该类型的指针或引用作为数据成员的类型. 因此类不能具有自身类型的数据成员, 但类的数据成员可以是指向自身类型的指针或引用.
    </li>
    <li>定义了一个类之后, 有两种方式使用该类: (1) 类的名字直接作为类型名. (2) 指定关键字class或struct, 后面跟着类的名字.
    <pre>Sales_item item1;
    class Sales_item item1;		// 两者等价</pre>
    </li>
    <li>何时使用 this, 当成员函数返回调用该函数的对象的引用时. 例如: 这样的函数返回值可以使得一些操作连接成一个单独的表达式, 如下
    <pre>myScreen.move(4, 0).set('#');</pre>
    </li>
    <li>普通的非const成员函数中, this的类型是一个指向类类型的const指针类型, 在const成员函数中, this为指向const类类型的const指针.
    </li>
    <li>基于const的重载, 可以定义两个同名的成员重载函数, 可以根据是否为const成员函数重载, 也可以根据形参指针是否指向const重载. 例如:
    <pre>class Screen
    {
    //...
    Screen&amp; display(std::ostream &amp;os);
    const Screen&amp; dispaly(std::ostream &amp;os) const;
    //...
    };</pre>
    </li>
    <li>可变数据成员 mutable, const成员函数可以改变 multable成员.
    </li>
    <li>类定义体外的成员函数返回值类型不一定在类作用域中, 如果返回类型使用由类定义的类型, 则必须使用完全限定名. 例如:
    <pre>// 其中index是在Screen类里定义的类型
    inline Screen::index Screen::get_cursor() const
    {
    return cursor;
    }</pre>
    </li>
    <li>查找在类成员声明中用到的名称,
    <ul>
        <li>检查出现在名称使用之前的类成员的声明
        </li>
        <li>如果没有成功, 则检查包含类定义的作用域中出现的声明, 以及出现在类定义之前的声明 </li>
    </ul>
    </li>
    <li>编译器按照成员声明在类中出现的次序来处理它们
    </li>
    <li>查找成员函数的函数体中出现的名称
    <ul>
        <li>首先检查成员函数局部作用域中的声明
        </li>
        <li>如果不成功, 则检查对所有类成员的声明
        </li>
        <li>如果还不成功, 则检查在此成员函数定义之前的作用域中出现的声明. </li>
    </ul>
    </li>
    <li>构造函数不能被声明为 const, 从概念上讲, 可以认为构造函数分两个阶段进行: (1)初始化阶段.(初始化列表) (2)普通的计算阶段(构造函数函数体内).
    </li>
    <li>对于非类类型的数据成员进行赋值或使用初始化式在结果和性能上都是等价的. (例如内置类型成员int, double)
    </li>
    <li>必须对任何 const 或引用类型成员以及没有默认构造函数的类类型的任何成员使用初始化式. 因为没有其他的办法初始化这些数据成员.
    </li>
    <li>成员被初始化的次序就是其在类中声明的顺序, 和初始化式列表的顺序没有关系. 所以按照与成员声明一致的次序编写构造函数初始化式列表是个好主意. 类类型的数据成员在初始化式中可以使用该类的任意构造函数.
    </li>
    <li>一个类哪怕只定义了一个构造函数, 编译器也不会再生成默认构造函数. 只有当一个类没有定义构造函数时, 编译器才会自动生成一个默认构造函数.
    </li>
    <li>默认合成构造函数中, 具有类类型的数据成员会运行各自的默认构造函数进行初始化. 内置类型和复合类型(数组和指针)的成员, 只有当该类的对象为全局变量时, 才进行初始化, 当类的对象为局部变量时, 不进行初始化.
    </li>
    <li>如果一个类没有默认构造函数, (1)则该类作为其他类的数据成员类型时, 必须在其他类的构造函数初始化式列表中显示初始化. (2)该类类型不能用作动态分配数组的元素类型. (3)静态分配数组必须为每个元素提供一个显示的初始化式. (4)在容器中, 如vector中, 不能使用仅有容器大小而没有提供一个元素初始化式的构造函数.
    </li>
    <li>使用默认构造函数定义一个对象的错误方式:
    <pre>Sales_item myobj();		// 这不会看成一个对象, 而是看成一个函数</pre>
    定义一个对象正确的方式
    <pre>Sales_item myobj;
    Sames_item myobj = Sales_item();</pre>
    </li>
    <li>隐式类类型转换, 只有一个形参的构造函数定义了从形参类型到该类类型的隐式转换, 例如:
    <pre>// 下面的类定义了string到Sales_item和istream到Sales_item的隐式转换
    class Sales_item{
    public:
    Sales_item(const std::string &amp;book = ""):isbn(book), units_sold(0), revenue(0.0) {}
    Sales_item(std::istream &amp;is);
    };
    // 假设Sales_item有成员函数same_isbn, 其形参类型为 const Sales_item&amp;
    string null_book = "9-999-99999-9";
    item.same_isbn(null_book);	// 实现了 string 到 Sales_item 的隐式转换</pre>
    </li>
    <li>关键字 explicit 用于避免隐式转换的产生, 只能用于类内部的函数声明上, 在类的定义体外部所做的定义不再重复它. 除非有明显的理由想要定义隐式转换, 否则, 单形参构造函数应该为 explicit.
    <pre>class Sales_item{
    public:
    explicit Sales_item(const std::string &amp;book = ""):isbn(book), units_sold(0), revenue(0.0) {}
    explicit Sales_item(std::istream &amp;is);
    };	</pre>
    </li>
    <li>另外一种避免隐式转换的方法是显示地使用构造函数, 例如:
    <pre>item.same_isbn(Sales_item(null_book));</pre>
    </li>
    <li>类成员的显式初始化, 对于没有定义构造函数并且全体数据成员均为 public 的类, 可以采用与初始化数组元素相同的方式初始化其成员, 根据数据成员的声明次序来使用初始化式. 例如:
    <pre>struct Data{
    int iVal;
    char *ptr;
    };
    Data val1 = { 0, 0 9};
    Data val2 = { 1024, "Anna Livia Plurabelle" };</pre>
    </li>
    <li>友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类, 只能出现在类定义的内部. 友元可以是普通的非成员函数, 或前面定义的其他类的成员函数, 或整个类. 将一个类设为友元, 友元类的所有成员函数都可以访问授予友元关系的那个类的非公有成员.
    </li>
    <li>用友元引入的类名和函数, 就已经预先声明了该类和函数, 后面可以使用该类和函数.
    </li>
    <li>类静态成员 static, 独立于该类的任意对象存在, static 成员函数没有 this形参, static 成员函数可以直接访问所属类的static成员, 但不能直接使用非static成员.
    <pre>class Account{
    public:
    void applyint() ( amount += amount * interestRate; )
    static double rate() { return interestRate; }
    static void rate(double);	// 设置新的 rate
    private:
    std::string owner;
    double amount;
    static double interestRate;
    static double initRate();
    };</pre>
    </li>
    <li>可以通过作用域操作符从类直接调用 static 成员, 或者通过对象, 引用或指向该类类型对象的指针间接调用. 类的成员函数可以不用作用域操作符来引用类的static成员.
    <pre>Account ac1;
    Account *ac2 = &amp;ac1;
    double rate;
    rate = ac1.rate();
    rate = ac2-&gt;rate();
    rate = Account::rate();</pre>
    </li>
    <li>static 成员函数在类定义体外定义的时候, 无需重复指定 static保留字. 该保留字只出现在类定义体内部的声明处.
    </li>
    <li>static 成员函数不能被声明为 const, 也不能被声明为虚函数.
    </li>
    <li>static 数据成员必须在类定义体的外部定义一次, 在定义时进行初始化, 最好将 static数据成员的定义放在包含类的非内敛成员函数定义的文件中.
    <pre>// 当成员名出现时, static成员的定义就在类作用域中, 所以 initRate()前不需要加上类作用域.
    double Account::interestRate = initRate();</pre>
    </li>
    <li>const static 数据成员可以在类定义体内初始化, 但该数据成员仍然要在类定义体外定义一次, 此时可不需要再指定初始值. 用常量值初始化的const static数据成员是一个常量表达式, 可以用在任何需要常量表达式的地方, 例如数组. 例如:
    <pre>class Account{
    public:
    static double rate() { return interestRate; }
    static double rate(double);
    private:
    static const int period = 30;
    double daily_tbl[period];
    };
    const int Account::period;</pre>
    </li>
    <li>static 的特殊用法: static数据成员的类型可以是所属类的类型. static 数据成员可以作为默认实参. </li>
</ul><img src ="http://www.cppblog.com/summericeyl/aggbug/140753.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/summericeyl/" target="_blank">夏冰</a> 2011-02-27 19:30 <a href="http://www.cppblog.com/summericeyl/archive/2011/02/27/140753.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ Primer 第15章笔记</title><link>http://www.cppblog.com/summericeyl/archive/2011/02/26/140710.html</link><dc:creator>夏冰</dc:creator><author>夏冰</author><pubDate>Sat, 26 Feb 2011 09:54:00 GMT</pubDate><guid>http://www.cppblog.com/summericeyl/archive/2011/02/26/140710.html</guid><wfw:comment>http://www.cppblog.com/summericeyl/comments/140710.html</wfw:comment><comments>http://www.cppblog.com/summericeyl/archive/2011/02/26/140710.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/summericeyl/comments/commentRss/140710.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/summericeyl/services/trackbacks/140710.html</trackback:ping><description><![CDATA[<p>
第15章 面向对象编程
</p>
<ul>
    <li>
    定义为 virtual 的函数是基类期待派生类重新定义的函数, 希望派生类继承的函数不能定义为虚函数.
    </li>
    <li>
    C++中, 通过基类的引用(或指针)调用虚函数时, 发生动态绑定. 该虚函数在运行时确定, 由引用(或指针)所指对象实际类型所定义的.
    </li>
    <li>
    对非虚函数的调用在编译时确定, 任意非static成员函数都可以是虚函数, 保留字 virtual 只在类内部的成员函数声明中出现, 不能用在类定义体外部出现的函数定义上.
    </li>
    <li>
    一个派生类对象只能访问其基类的protected成员, 但不能访问其他基类对象的protected成员, 注意对象二字. 另该派生类对象可以访问其他派生类对象的protected成员
    </li>
    <li>
    如果需要声明(但不实现)一个派生类, 则声明包含类名但不包含派生列表. 例如:<br>
    <pre>class Bulk_item;	// 基类<br>class item_base;	// 派生类<br></pre>
    </li>
    <li>
    可以用基类类型的指针或引用来引用派生类型对象, 即动态绑定. 如果调用非虚函数, 无论实际对象是什么类型, 都执行基类类型所定义的函数, 即使派生类实现了该函数的自己版本, 也会调用基类中的函数.
    </li>
    <li>
    覆盖虚函数机制, 可以使用作用域操作符覆盖虚函数机制并强制函数调用虚函数的特定版本, 例如:<br>
    <pre>Item_base *baseP = &amp;derived;	// derived 为派生类对象<br>double d = baseP-&gt;Item_base::net_price(42);		// 如果不使用作用域操作符, 则会默认使用派生类的net_price()函数<br></pre>
    </li>
    <li>
    使用覆盖函数机制, 常用在派生类函数中调用基类同名函数, 用于完成基类版本中的公共任务.
    </li>
    <li>
    虚函数和默认实参的关系, 如果虚函数具有默认实参, 调用该虚函数时, 所用的默认实参由调用该函数的类型所决定, 与对象的动态类型无关. 例如基类的指针或引用调用该虚函数时, 默认实参为在基类虚函数声明中指定的值.
    </li>
    <li>
    公用, 私有和受保护的继承, 公用继承中, 基类成员保持自己的访问级别. 受保护继承,
    基类的public和protected成员在派生类中为protected成员. 私有继承中, 基类的所有成员在派生类中为private成员.
    派生类的这三种访问标号控制的是派生类的用户对从基类继承而来的成员访问权限.
    </li>
    <li>
    public派生类继承让派生类具有和基类一样的接口(公有成员). 而private和protected继承使得派生类不具有基类的接口, 称之为实现继承.
    </li>
    <li>
    派生类可以恢复继承成员的访问级别, 但不能比基类中的访问级别更严格或更宽松. 使用 using 声明恢复访问级别. 例如:<br>
    <pre>class Base{<br>public:<br>	std::size_t size() const { return n; }<br>protected:<br>	std::size_t n;<br>};<br><br>class Derived : private Base{<br>public:<br>	using Base::size;<br>protected:<br>	using Base::n;<br>	// ...<br>};<br></pre>
    </li>
    <li>
    默认继承保护级别, class 默认为 private 继承, struct 默认为 public 继承, 且 class 默认的成员保护级别为private, struct 为public.<br>
    <pre>class Base { /* ... */ };<br>struct D1 : Base { /* ... */ };	// public 继承<br>class D2 : Base { /* ... */ }; // private 继承77<br></pre>
    </li>
    <li>
    友元可以访问类的 private 和 protected 数据, 友元关系不能继承.
    </li>
    <li>
    如果基类定义了 static 成员, 则在整个继承层次中只有这样一个成员, 不会产生拷贝. 既可以通过基类访问 static 成员,
    也可以通过派生类访问 static 成员. 既可以通过作用域操作符"::"也可以使用点或箭头成员访问操作符访问 static 成员.
    </li>
    <li>
    如有一个派生类的对象, 则可使用它的地址赋值或初始化一个基类的指针. 也可以使用该派生类的引用或对象初始化基类类型的引用.
    </li>
    <li>
    引用转换和对象转换的区别: 将对象传递给接受引用参数的函数, 引用直接绑定到该对象, 对象并未复制. 派生类对象传递给接受基类引用参数的函数, 该对象仍为派生类对象. 如果将派生类对象传递给接受基类参数的函数, 则派生类的基类部分被复制到形参.
    </li>
    <li>
    用派生类对象对基类对象进行初始化和赋值, 两种情况: 1) 基类显式定义了派生类型对象复制或赋值给基类对象的函数.(少见) 2) 将派生类对象引用转换为基类对象引用, 而后调用基类的复制构造函数或赋值操作符.
    </li>
    <li>
    如果派生类是通过public继承基类, 则用户代码和后代类可以使用派生类到基类的转换; 如果派生类是protected继承,
    则用户代码不能将派生类对象转换为基类对象, 但后代类可以使用派生类到基类的转换; 如果是private继承,
    则用户代码和后代类派生类对象不能转换为基类对象. 由于派生类成员可以访问基类的public成员, 所以无论什么继承,
    派生类成员函数和友元总是可以访问派生类到基类的转换.
    </li>
    <li>
    不存在基类对象到派生类对象的自动转换. 基类指针或引用绑定到派生类对象时, 其指针或引用转换也存在限制. 如果知道基类到派生类的转换是安全的,
    则使用 &lt;static_cast&gt;强制转换. 也可以使用 &lt;dynamic_cast&gt; 申请运行时进行检查.
    </li>
    <li>
    构造函数和复制控制成员不能继承.
    </li>
    <li>
    派生类构造函数
    </li>
    <ul>
        <li>
        合成的派生类默认构造函数, 首先调用基类的默认构造函数, 而后用常规变量初始化规则初始化派生类的数据成员.
        </li>
        <li>
        定义默认构造函数, 首先调用基类的默认构造函数, 而后调用派生类的默认构造函数
        </li>
        <li>
        向基类构造函数传递实参, 在构造函数的初始化列表中首先调用基类的构造函数, 然后根据声明次序初始化派生类的成员.
        </li>
    </ul>
    <li>
    一个类只能初始化自己的直接基类. 派生类构造函数不能初始化基类的成员且不应该对基类成员赋值.
    </li>
    <li>
    复制控制的合成版本, 复制派生类对象时, 调用合成的基类复制构造函数复制基类部分的数据成员, 而后复制派生部分的数据成员.
    </li>
    <li>
    如果派生类定义了自己的复制构造函数, 该复制构造函数一般应显式使用基类复制构造函数初始化对象的基类部分. 例如<br>
    <pre>class Base {/* ... */}<br>class Derived: public Base {<br>public:<br>	Derived(const Derived&amp; d):<br>		Base(d) {/* ... */}<br>}<br></pre>
    </li>
    <li>
    如果省略了基类的复制构造函数, 则会调用基类的默认构造函数初始化对象的基类部分
    </li>
    <li>
    派生类赋值操作符, 该操作符必须对基类部分进行显式赋值, 例如<br>
    <pre>Derived &amp;Derived::operator=(const Derived &amp;rhs)<br>{<br>	if(this != rhs)<br>	{<br>		Base::operator=(rhs);<br>		// ...<br>	}<br>}<br></pre>
    </li>
    <li>
    派生类的析构函数只负责清除自己的成员, 不负责撤销基类对象的成员. 撤销顺序首先运行派生类析构函数, 而后依次向上调用各基类析构函数.
    </li>
    <li>
    虚析构函数, 基类的析构函数最好为虚拟函数, 当基类指针指向派生类对象时, 删除该对象则可调用正确的析构函数.<br>
    <pre>Base* item = new Derived;<br>delete item;	// 如果析构函数为虚拟函数, 则可调用Derived类的析构函数.<br></pre>
    </li>
    <li>
    三法则: 如果类需要析构函数, 则它也需要赋值操作符和复制构造函数.
    </li>
    <li>
    基类析构函数是三法则的一个重要例外, 如果基类的析构函数为虚函数, 且为空函数, 则不一定需要赋值操作符或者复制构造函数.
    </li>
    <li>
    构造函数不能定义为虚函数, 赋值操作符设为虚函数会令人混淆, 而且不会有什么用处.
    </li>
    <li>
    构造函数和析构函数内部中的虚函数, 由于在基类的构造函数和析构函数中, 派生类对象都不会完整. 所以如果在构造函数和析构函数中调用虚函数, 运行的是构造函数和析构函数自身类型(所在类)的版本
    </li>
    <li>
    在继承情况下, 派生类的作用域嵌套在基类作用域中.
    </li>
    <li>
    对象, 引用或指针的静态类型决定了对象能够完成的行为. 例如一个基类指针指向一个派生类对象, 该指针只能访问该对象的基类部分, 而不能访问该类的派生类部分.
    </li>
    <li>
    与基类成员同名的派生类成员将屏蔽对基类成员的直接访问, 除非使用作用域操作符访问被屏蔽的基类成员. 设计派生类时, 只要可能, 最好避免与基类成员的名字冲突.
    </li>
    <li>
    基类和派生类中使用同一名字的成员函数时, 即使函数原型不同, 派生类也会屏蔽基类的同名函数. 与局部作用域中声明的函数不会重载全局作用域中的函数一样, 派生类中定义的函数也不会重载基类中定义的成员.
    </li>
    <li>
    如果派生类重定义了基类的重载成员函数, 则只能访问派生类重定义的重载函数, 不能访问基类的重载函数. 所以如果想在派生类中用所有的重载版本,派生类要么重定义所有的重载版本, 要么一个也不重定义.
    </li>
    <li>
    如果不想重定义所有的重载成员, 可以使用 using声明, 为基类成员函数名称而做的using声明将该函数的所有重载实例添加到派生类的作用域, 而后派生类只需重定义那些需要的版本即可.
    </li>
    <li>
    如果基类定义了一个虚函数,  派生类没有重定义该虚函数, 而定义了其他版本的同名函数, 则在派生类中屏蔽了该基类的虚拟同名函数,
    但派生类对象还是继承了该基类的同名虚函数, 这样派生类就有了数个该同名函数版本, 此时用基类的指针指向派生类对象, 可以调用被屏蔽的同名虚函数.
    </li>
    <li>
    如何确定函数调用的步骤:
    </li>
    <ul>
        <li>
        首先确定进行函数调用的对象,引用或指针的静态类型
        </li>
        <li>
        在该类中查找函数, 如果没有查找到, 则在直接基类中查找, 如此循着类的继承往上查找, 直到找到该函数或者查找完最后一个类.
        </li>
        <li>
        找到该函数后, 进行常规类型检查, 即检查实参类型与形参类型是否匹配.
        </li>
        <li>
        如果函数调用合法, 则编译器生成代码, 如函数为虚函数且通过引用或指针调用, 则根据对象的动态类型以运行哪个函数版本.
        </li>
    </ul>
    <li>
    纯虚函数, 在函数形参表后面写上"=0"以指定纯虚函数, 含有(或继承)一个或多个纯虚函数的类是抽象基类(abstract base class), 除了作为抽象基类的派生类的对象的组成部分, 不能创建抽象基类的对象.
    </li>
    <li>
    使用容器保存对象, 如果容器保存基类类型, 则派生类对象会被切掉派生部分, 且没有从基类到派生类的标准转换.唯一可行的选择是使用容器保存对象的指针, 但需要用户管理对象和指针, 必须保证容器存在, 被指向的对象就存在.
    </li>
    <li>
    包装(cover)类或句柄(handle)类, 存储和管理基类指针, 用户通过句柄类访问继承层次的操作. 定义句柄类,
    三个构造函数-默认构造函数, 复制构造函数, 接受基类对象的构造函数. 第三个构造函数复制基类对象, 只要句柄类存在, 该副本就存在,
    用计数来管理副本, 该计数数据成员为指针类型.还有解引用操作符和箭头操作符的定义. 为了实现第三个构造函数,
    基类和派生类应该有虚函数clone()用于返回当前对象的副本.
    </li>
    <li>
    在关联容器中自动排序元素, 可以通过指定一个函数用作比较函数, 代替&lt;操作符. <br>
    <pre>inline<br>bool compare(const Sales_item&amp; lhs, const Sales_item&amp; rhs)<br>{<br>	return lhs-&gt;book() &lt; rhs-&gt;book();<br>}<br></pre>
    </li>
    <li>
    关联容器中注意指定比较器类型, 毕竟比较器对象存放在容器中, 以保证对每一个操作使用同一个比较函数<br>
    <pre>// 定义比较器类型<br>typedef bool (*Comp)(const Sales_item&amp;, const Sales_item&amp;);<br>// 关联容器保存比较器对象<br>std::multiset&lt;Sales_item, Comp&gt; items(compare);<br></pre>
    </li>
</ul>
<br> <img src ="http://www.cppblog.com/summericeyl/aggbug/140710.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/summericeyl/" target="_blank">夏冰</a> 2011-02-26 17:54 <a href="http://www.cppblog.com/summericeyl/archive/2011/02/26/140710.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>第一次破解了一个Crack Me</title><link>http://www.cppblog.com/summericeyl/archive/2008/12/28/70545.html</link><dc:creator>夏冰</dc:creator><author>夏冰</author><pubDate>Sat, 27 Dec 2008 22:34:00 GMT</pubDate><guid>http://www.cppblog.com/summericeyl/archive/2008/12/28/70545.html</guid><wfw:comment>http://www.cppblog.com/summericeyl/comments/70545.html</wfw:comment><comments>http://www.cppblog.com/summericeyl/archive/2008/12/28/70545.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/summericeyl/comments/commentRss/70545.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/summericeyl/services/trackbacks/70545.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 算出序列号开始最重要的还是要找出算法所在处&nbsp;&nbsp;<a href='http://www.cppblog.com/summericeyl/archive/2008/12/28/70545.html'>阅读全文</a><img src ="http://www.cppblog.com/summericeyl/aggbug/70545.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/summericeyl/" target="_blank">夏冰</a> 2008-12-28 06:34 <a href="http://www.cppblog.com/summericeyl/archive/2008/12/28/70545.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>