﻿<?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++博客-Richard He-文章分类-[转]</title><link>http://www.cppblog.com/richardhe/category/6665.html</link><description>学无止境!永远学下去!</description><language>zh-cn</language><lastBuildDate>Sun, 20 Jul 2008 02:55:46 GMT</lastBuildDate><pubDate>Sun, 20 Jul 2008 02:55:46 GMT</pubDate><ttl>60</ttl><item><title>简介Boost.Regex</title><link>http://www.cppblog.com/richardhe/articles/56662.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Sun, 20 Jul 2008 02:31:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/56662.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/56662.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/56662.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/56662.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/56662.html</trackback:ping><description><![CDATA[<table align="center" bgcolor="#efefef" border="0" cellpadding="4" cellspacing="0" width="99%">
    <tbody>
        <tr>
            <td id="zoom" style="font-size: 10.5pt;" width="89%">
            <p><font size="1"><font style="background-color: #0000ff;"><font color="#000000"><font style="background-color: #ffffff;"><strong>简介<sup>1</sup></strong></font><br></font></font>&nbsp;&nbsp;&nbsp;&nbsp;正
            则表达式（Regular
            expression）是一种在文本处理中经常用到的模式匹配的形式，可能许多用户熟悉Unix下的grep,sed,awk等工具或者perl语言，它
            们都广泛地用到了正则表达式。传统的C++用户还受限于用POSIX C API's（Portable operateing system
            interface standard)来操作正则表达式，而regex++已经提供了这些API's，虽然它不一定是使用POSIX C
            正则表达式库的最好方法。比如regex++能处理宽字符字符串（wide character
            strings），或者搜索、替换操作（在某种意义上类似于sed或perl），这些在传统的C 库中是不能实现的。<br>类boost::reg_expression是regex++库中的关键类，它表示&#8220;机器可读&#8221;的正则表达式，reg_expression 是在string的基础之上构建的，可以认为它是一个具有string的功能，外加这个正则表达式算法所需要的状态机。<br>像std::basic_string一样，它提供了两个针对char和wchar_t的特化版本：<br><br>namespace boost{<br>template &lt;class charT, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class traits = regex_traits&lt;charT&gt;, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class Allocator = std::allocator&lt;charT&gt; &gt;<br>class basic_regex;<br>typedef basic_regex&lt;char&gt; regex;<br>typedef basic_regex&lt;wchar_t&gt; wregex;<br>}</font></p>
            <p><font size="1">&nbsp;&nbsp;&nbsp;&nbsp;要知道regex++库到底有什么用？可以试想我们要写一个信用卡处理程序。信用卡通常有16位数字组成的
            号码，其中每四位一组，用空格或连字号隔开。在将这些信用卡号码存入数据库之前，我们难道不要检验这些数字是否符合正确格式么？为了匹配任何一个数字我们
            可以用正则表达式[0-9]，数字串的宽度可以用[[:digit:]],当然这些是POSIX标准。在regex++和perl中可简化为\d（注意许
            多老的库倾向于硬编码到C-locale，因此这不是什么问题）。下面的正则表达式可以检验信用卡号码的格式。<br>(\d{4}[- ]){3}\d{4}<br>&nbsp;&nbsp;&nbsp;&nbsp;()标记子表达式，{4}表示重复4次。这仅是一个perl,awk,egrep的正则表达式的例子。regex++也支持那些sed,grep用到的比较老的&#8220;基本&#8221;语法，虽然它们很少用到，除非你需要重用一些已有的基本正则表达式。</font></p>
            <p><font size="1">&nbsp;&nbsp;&nbsp;&nbsp;现在让我们把这个表达式置于C++代码中来检验信用卡号码的格式：<br><br>bool validate_card_format(const std::string s)<br>{<br>&nbsp;&nbsp; static const boost::regex e("(</font><a  href="file://%5c%5cd%7b4/"><font size="1">\\d{4</font></a><font size="1">}[- ]){3}\\d{4}");<br>&nbsp;&nbsp; return regex_match(s, e);<br>}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;注意我们以前是如何将某些额外的转义序列（或者翻译成：转义字符）添加到表达式的：要知道，正则表达式引擎处理转义字符前，该转义字符只能被 C++编译器识别一次，因此，在C++代码中嵌入正则表达式的转义字符必须双写（写两次）。<br>&nbsp;&nbsp;&nbsp;&nbsp;还要注意到你的编译器必须支持Koening lookup 2（比如VC6就不支持），否则你需要附加一些boost::prefixes到某些函数引用中。<br>&nbsp;&nbsp;&nbsp;&nbsp;那
            些熟悉信用卡操作的人，可能还会想到上面的那种格式适合于人的阅读，并不表示网上信用卡系统要求的格式（可能是16或15个没有间隔符的数字串）。我们需
            要一种简单的转换方法，我们需要2个字符串，1个是正则表达式，一个是格式字符串（提供一种被匹配的内容的描述）。在regex++中，search和
            replace都能完成regex_merge算法，对于信用卡的例子我们给出了下面2个算法用于格式转换：<br><br>// match any format with the regular expression:<br>const boost::regex e("</font><a  href="file://%5c%5ca%28%5c%5cd%7b3,4/"><font size="1">\\A(\\d{3,4</font></a><font size="1">})[- ]?(</font><a  href="file://%5c%5cd%7b4/"><font size="1">\\d{4</font></a><font size="1">})[- ]?(</font><a  href="file://%5c%5cd%7b4/"><font size="1">\\d{4</font></a><font size="1">})[- ]?(</font><a  href="file://%5c%5cd%7b4%7d%29%5c%5cz/"><font size="1">\\d{4})\\z</font></a><font size="1">");<br>const std::string machine_format("</font><a  href="file://%5c%5c1%5c%5c2%5c%5c3%5c%5c4/"><font size="1">\\1\\2\\3\\4</font></a><font size="1">");<br>const std::string human_format("</font><a  href="file://%5c%5c1-%5c%5c2-%5c%5c3-%5c%5c4/"><font size="1">\\1-\\2-\\3-\\4</font></a><font size="1">");</font></p>
            <p><font size="1">std::string machine_readable_card_number(const std::string s)<br>{<br>&nbsp;&nbsp; return regex_merge(s, e, machine_format, boost::match_default | boost::format_sed);<br>}</font></p>
            <p><font size="1">std::string human_readable_card_number(const std::string s)<br>{<br>&nbsp;&nbsp; return regex_merge(s, e, human_format, boost::match_default | boost::format_sed);<br>}</font></p>
            <p><br><font size="1">&nbsp;&nbsp;&nbsp;&nbsp;这儿，我们用正则表达式中的子式把号码分为4块，format string 用类似sed的语法把被匹配的内容替换为规定格式。<br>&nbsp;&nbsp;&nbsp;&nbsp;上面的例子中，我们还没有直接操作匹配结果，匹配结果包括全体匹配和一些子式的匹配。当需要正则表达式的匹配结果时，就需要用到class match_results的实例，下面是常用类型的特化版本：<br><br>namespace boost{<br>typedef match_results&lt;const char*&gt; cmatch;<br>typedef match_results&lt;const wchar_t*&gt; wcmatch;<br>typedef match_results&lt;std::string::const_iterator&gt; smatch;<br>typedef match_results&lt;std::wstring::const_iterator&gt; wsmatch; <br>}</font></p>
            <p><font size="1">&nbsp;&nbsp;&nbsp;&nbsp;regex_search和regex_grep算法都使用到match_result。<br>&nbsp;&nbsp;&nbsp;&nbsp;注意这些算法并不局限于一般的C-strings,任何双向迭代器（bidirectional iterator）类型都能被搜索，这为无缝搜索任何类型数据提供了可能性。</font></p>
            <p><font size="1">&nbsp;&nbsp;&nbsp;&nbsp;对于那些不喜欢模板（templates）的人还可以使用class RegEx,它是对模板代码的高层次包装，它为那些使用不到库的整个功能的人提供了简单的接口，当然它仅支持窄字符（narrow character）和&#8220;扩展&#8221;的正则表达式语法。<br>&nbsp;&nbsp;&nbsp;&nbsp;对于想兼容POSIX的人可以使用POSIXAPI函数：regcomp,regexec,regfree,regerror,这些对narrow character和Unicode都适用。</font></p>
            <p><font size="1">&nbsp;&nbsp;&nbsp;&nbsp;最后注意，这个库现在支持运行时本地化（run-time localization），它能完全识别POSIX正则表达式语法，包括一些多字符的元素比较和同等类型的高级特性，它还能兼容其它一些正则表达式库包括GNU、BSD4的regex包。</font></p>
            <p><font size="1"><strong>安装和配置</strong></font></p>
            <p><font size="1">&nbsp;&nbsp;&nbsp;&nbsp;首先当你从zip文件解压本库时必须保留它的内部结构，如果你没这样做，那只好删除你解压的文件，重现来一次。<br>&nbsp;&nbsp;&nbsp;&nbsp;由于支持大多数常见的编译器/标准库/平台，这个库不需要作使用前的配置。如果你碰到配置问题，或想测试你的编译器的配置信息，可以参考 配置文档（这和boost的其它所有的库的处理过程都一样）。</font></p>
            <p><font size="1">&nbsp;&nbsp;&nbsp;&nbsp;由于本库混合了模板代码（头文件中）和静态代码数据（cpp文件中），所以在你使用之前，必须将库支持的代码 生成到库内和档案文件中。以下是几个具体平台的操作步骤：<br>Borland C++ Builder<br><font color="#008000">略</font><br>Microsoft Visual C++ 6 and 7<br>如果你使用VC5，你可能要找一下本库的以前版本。<br>打开命令提示符（其MSVC环境变量必须已定义，如果没有可运行Vcvars32.bat，位于&lt;VC6&gt;\bin），进入&lt;boost&gt;\libs\regex\build 目录<br>选择正确的makefile,VC6++的是vc6.mak,支持STLPort的是vc6-stlport.mak<br>如下调用<br>nmake -fvc6.mak<br>如果想在VC6子目录包含所有的lib、dll文件，lib文件拷在&lt;VC6&gt;\lib，dll文件拷在&lt;VC6&gt;\bin，可使用<br>nmake -fvc6.mak install<br>删除生成过程中所有的临时文件，可使用<br>nmake -fvc6.mak clean</font></p>
            <p><font size="1">最后只需添加&lt;boost&gt;根目录到你的工程所包含的目录列表中。没有必要手动将*.lib文件加到工程中，因为在头文件会选择正确的.lib文件。<br>注意：如果想静态地链入regex库，可定义BOOST_REGEX_STATIC_LINK（在release版中生效）。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果想直接使用源文件，可定义BOOST_REGEX_NO_LIB，这样自动选择库将失效。</font></p>
            <p><font size="1">1.简介出处：</font><a  href="http://www.boost.org/libs/regex/doc/introduction.html"><font size="1">http://www.boost.org/libs/regex/doc/introduction.html</font></a><br><font size="1">2.Koening
            lookup：When a function is called, in order to determine if that
            function is visible in the current scope, the namespaces in which the
            functions parameters reside must be taken into account.</font></p>
            </td>
            <td width="5%">&nbsp;</td>
        </tr>
        <tr>
            <td align="right">&nbsp;          </td>
            <td align="right">          <br></td>
        </tr>
    </tbody>
</table><img src ="http://www.cppblog.com/richardhe/aggbug/56662.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-07-20 10:31 <a href="http://www.cppblog.com/richardhe/articles/56662.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>boost::regex</title><link>http://www.cppblog.com/richardhe/articles/56653.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Sun, 20 Jul 2008 01:45:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/56653.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/56653.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/56653.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/56653.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/56653.html</trackback:ping><description><![CDATA[<div id="art" style="margin: 15px;">
<div>
<h4 class="TextColor1" id="subjcns!D831FDBD876F27A2!173" style="margin-bottom: 0px;">boost::regex</h4>
<div id="msgcns!D831FDBD876F27A2!173">
<ul>
    <li>boost::regex的用法</li>
</ul>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bool validate_card_format(const std::string s)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static const boost::regex e("(\\d{4}[- ]){3}\\d{4}");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return regex_match(s, e);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<ul>
    <li>boost::regex的默认正则表达式语法是perl语法</li>
</ul>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;boost::regex支持perl regular表达式、POSIX-Extended
regular表达式和POSIX-Basic
Regular表达式，但默认的表达式语法是perl语法，如果要使用其余两种语法需要在构造表达式的时候明确指定。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;例如，下面两种方法效果相同<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// e1 is a case sensitive Perl regular expression:&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// since Perl is the default option there's no need to explicitly specify the syntax used here:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;boost::regex e1(my_expression);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// e2 a case insensitive Perl regular expression:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;boost::regex e2(my_expression, boost::regex::perl|boost::regex::icase);</p>
<ul>
    <li>perl正则表达式语法</li>
</ul>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perl正则表达式语法可参见《perl语言入门》第7、8、9章或boost的文档。这里列出的语法是不全面的，而且部分说明可能并不清楚。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;. 任意字符;使用match_no_dot_null标志时不匹配NULL字符; 使用match_not_dot_newline时不匹配换行字符</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;^ 匹配行的开始<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$ 匹配行的结束<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* 重复零次或则更多,例如a*b可匹配b,ab,aab,aaaaaaab<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ 重复一次以上，例如a+b可匹配ab,aab,aaaaaaaab。但不能匹配b了<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;? 零次或则一次，例如ca?b匹配cb,cab但不匹被caab&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a{n} 匹配字符'a'重复n次<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a{n,}，字符a重复n次以上（含n次）<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a{n,m} a重复n到m次（含）</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*?&nbsp;&nbsp; 匹配前一个原子零次以上<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+?&nbsp;&nbsp; 匹配前一个原子一次以上<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;??&nbsp;&nbsp; 匹配前一个原子零次以上<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{n,}?&nbsp; 匹配前一个原子n次以上(含)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{n,m?&nbsp; 匹配前一个原子n到m次(含)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;| 或操作，例如ab(d|ef)匹配abd或则abef<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[] 字符集操作，例如[abc]将匹配任何单个字符'a'，'b'，'c'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[a-d]，表示a、b、c、d<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;^否操作，例如[^a-c]表示a至c之外的所有字符<br></p>
<ul>
    <li>boost::regex对unicode编码的支持</li>
</ul>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;boost::regex使用ICU来实现对unicode及unicode变种的支持，这需要在编译boost的时候指出是否使
用ICU以及ICU所在的目录。否则编译出来的boost::regex不支持unicode编码。其中boost::wregex支持unicode编
码的搜索，如果要搜索UTF-8、UTF-16、UFT-32编码的字符串，则要用boost::u32regex。注意boost::wregex只能
支持unicode编码，不能支持uft编码。</p>
<ul>
    <li>搜索时如何忽略大小写</li>
</ul>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果要在搜索时忽略大小写（即大小写不敏感），则要用到表达式选项boost::regex::icase，例
如：&nbsp;boost::regex e2(my_expression,
boost::regex::perl|boost::regex::icase);</p>
</div>
</div>
</div><img src ="http://www.cppblog.com/richardhe/aggbug/56653.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-07-20 09:45 <a href="http://www.cppblog.com/richardhe/articles/56653.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++中宏的使用</title><link>http://www.cppblog.com/richardhe/articles/56328.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 16 Jul 2008 10:10:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/56328.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/56328.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/56328.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/56328.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/56328.html</trackback:ping><description><![CDATA[关于## 和 #及#@的用法<br><br><br>## 是连接符号 连接两个宏,##被称为连接符（concatenator），用来将两个Token连接为一个Token。注意这里连接的对象是Token就行，而不一定是宏的变 <br>量。比如你要做一个菜单项命令名和函数指针组成的结构体的数组，并且希望在函数名和菜单项命令名之间有直观的、名字上的关系。那就可以使用：宏参数## <br>固定部分。当然还可以n个##符号连接 n+1个Token，这个特性也是#符号所不具备的。 <br>#define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d<br>typedef struct _record_type LINK_MULTIPLE(name,company,position,salary);<br>// 这里这个语句将展开为：<br>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typedef struct _record_type name_company_position_salary;<br><br><br>#@功能是将其后面的宏参数进行字符化。<br>#define makechar(x)&nbsp; <a  href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#35;&#64;&#120;&#97;">#@x<br>a</a> = makechar(b);<br>//a = 'b';<br><br><br>#是把名字代替成字符串,宏体中，#的功能是将其后面的宏参数进行字符串化操作（Stringfication），简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个 <br>双引号。<br>#define WARN_IF(EXP)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do{ if (EXP)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr, "Warning: " #EXP "\n"); }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(0)<br>那么实际使用中会出现下面所示的替换过程： <br>WARN_IF (divider == 0);<br>被替换为<br>do {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (divider == 0)<br>fprintf(stderr, "Warning" "divider == 0" "\n");<br>} while(0);<br>这样每次divider（除数）为0的时候便会在标准错误流上输出一个提示信息。 <br><br><br>
<font style="font-size: 12pt;" face="#ce_temp_font#"><dt><strong>!IF </strong><span class="parameter">constantexpression</span>
</dt><dd>
<p>如果 <span class="parameter">constantexpression</span> 计算结果为非零值，则处理 <strong>!IF</strong> 和下一个 <strong>!ELSE</strong> 或 <strong>!ENDIF</strong> 之间的语句。<br><strong>!ENDIF</strong> </p>
</dd>
<dd>
<p>标记 <strong>!IF</strong>、<strong>!IFDEF</strong> 或 <strong>!IFNDEF</strong> 块的结尾。同一行上 <strong>!ENDIF</strong> 后面的所有文本被忽略。</p>
</dd></font><img src ="http://www.cppblog.com/richardhe/aggbug/56328.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-07-16 18:10 <a href="http://www.cppblog.com/richardhe/articles/56328.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>extern "C"</title><link>http://www.cppblog.com/richardhe/articles/55817.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Thu, 10 Jul 2008 09:27:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/55817.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/55817.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/55817.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/55817.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/55817.html</trackback:ping><description><![CDATA[<p>前些天，编程序是用到了很久以前写的C程序，想把里面的函数利用起来，连接发现出现了找不到具体函数的错误：</p>
<p>以下是假设旧的C程序库</p>
<p>C的头文件</p>
<pre><em><font color="#999999">/*-----------c.h--------------*/</font></em><font color="#000099"><br>#ifndef _C_H_<br>#define _C_H_<br></font><font color="#990000">extern</font><font color="#ff6633"> int</font> add<strong><font color="#663300">(</font></strong><font color="#ff6633">int</font> x<strong><font color="#663300">,</font></strong><font color="#ff6633"> int</font> y<strong><font color="#663300">);</font></strong><font color="#000099"><br>#endif</font></pre>
<p>C的源文件</p>
<pre><em><font color="#999999">/*-----------c.c--------------*/</font></em><font color="#ff6633"><br>int</font> add<strong><font color="#663300">(</font></strong><font color="#ff6633">int</font> x<strong><font color="#663300">,</font></strong><font color="#ff6633"> int</font> y<strong><font color="#663300">){</font></strong><font color="#ff0000"><br>	return</font> x<strong><font color="#663300">+</font></strong>y<strong><font color="#663300">;<br>}</font></strong><br></pre>
<p>C++的调用</p>
<pre><em><font color="#999999">/*-----------cpp.cpp--------------*/</font></em><font color="#000099"><br>#include "c.h"<br></font><font color="#ff6633">void</font><font color="#990000"> main</font><strong><font color="#663300">()<br>{</font></strong><br>	add<strong><font color="#663300">(</font></strong><font color="#999900">1</font><strong><font color="#663300">,</font></strong><font color="#999900"> 0</font><strong><font color="#663300">);<br>}</font></strong><br></pre>
<p>这样编译会产生错误cpp.obj : error LNK2001: unresolved external symbol "int __cdecl add(int,int)" (<a  href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#63;&#97;&#100;&#100;&#64;&#64;&#89;&#65;&#72;&#72;&#72;&#64;&#90;">?add@@YAHHH@Z</a>)，原因是找不到add的目标模块</p>
<p>这才令我想起C++重载的函数命名方式和C函数的命名方式，让我们回顾一下：C中函数编译后命名会在函数名前加以"_",比如add函数编译成obj文件时的实际命名为_add，而c++命名则不同，为了实现函数重载同样的函数名add因参数的不同会被编译成不同的名字</p>
<p>例如</p>
<p>int add(int , int)==&gt;<font color="#0000ff"><u>add@@YAHHH@Z,</u></font></p>
<p><font color="#0000ff"><font color="#000000">float add(float , float )==&gt;</font></font><font color="#0000ff"><u>add@@YAMMM@Z</u></font><font color="#000000">,</font></p>
<p>以上是VC6的命名方式，不同的编译器会不同，总之不同的参数同样的函数名将编译成不同目标名，以便于函数重载是调用具体的函数。</p>
<p>编译cpp.cpp中编译器在cpp文件中发现add<strong><font color="#663300">(</font></strong><font color="#999900">1</font><strong><font color="#663300">,</font></strong><font color="#999900"> 0</font><strong><font color="#663300">);</font></strong>的调用而函数声明为<font color="#990000">extern</font><font color="#ff6633"> int</font> add<strong><font color="#663300">(</font></strong><font color="#ff6633">int</font> x<strong><font color="#663300">,</font></strong><font color="#ff6633"> int</font> y<font color="#663300"><strong>);</strong></font><font color="#000000">编译器就决定去找</font><font color="#0000ff"><a  href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#97;&#100;&#100;&#64;&#64;&#89;&#65;&#72;&#72;&#72;&#64;&#90;">add@@YAHHH@Z</a></font><font color="#000000">，可惜他找不到，因为C的源文件把<font color="#990000">extern</font><font color="#ff6633"> int</font> add<strong><font color="#663300">(</font></strong><font color="#ff6633">int</font> x<strong><font color="#663300">,</font></strong><font color="#ff6633"> int</font> y<font color="#663300"><strong>);</strong></font></font><font color="#000000">编译成_add了；</font></p>
<p>为了解决这个问题C++采用了extern "C",这就是我们的主题，想要利用以前的C程序库，那么你就要学会它，我们可以看以下标准头文件你会发现，很多头文件都有以下的结构</p>
<pre><font color="#000099">#ifndef __H<br>#define __H<br>#ifdef __cplusplus<br></font><font color="#990000">extern</font><font color="#009900"> "C"</font><strong><font color="#663300"> {</font></strong><font color="#000099"><br>#endif<br></font><font color="#990000"><br>extern</font><font color="#ff6633"> int</font> f1<strong><font color="#663300">(</font></strong><font color="#ff6633">int</font><strong><font color="#663300">,</font></strong><font color="#ff6633"> int</font><strong><font color="#663300">);</font></strong><font color="#990000"><br>extern</font><font color="#ff6633"> int</font> f2<strong><font color="#663300">(</font></strong><font color="#ff6633">int</font><strong><font color="#663300">,</font></strong><font color="#ff6633"> int</font><strong><font color="#663300">);</font></strong><font color="#990000"><br>extern</font><font color="#ff6633"> int</font> f3<strong><font color="#663300">(</font></strong><font color="#ff6633">int</font><strong><font color="#663300">,</font></strong><font color="#ff6633"> int</font><strong><font color="#663300">);</font></strong><font color="#000099"><br><br>	<br>#ifdef __cplusplus<br></font><strong><font color="#663300">}</font></strong><font color="#000099"><br>#endif<br><br>#endif /*__H*/<br></font></pre>
<p>如果我们仿制该头文件可以得到</p>
<pre><font color="#000099">#ifndef _C_H_<br>#define _C_H_<br>#ifdef __cplusplus<br></font><font color="#990000">extern</font><font color="#009900"> "C"</font><strong><font color="#663300"> {</font></strong><font color="#000099"><br>#endif<br></font><font color="#990000"><br>extern</font><font color="#ff6633"> int</font> add<strong><font color="#663300">(</font></strong><font color="#ff6633">int</font><strong><font color="#663300">,</font></strong><font color="#ff6633"> int</font><strong><font color="#663300">);</font></strong><font color="#000099"><br><br>#ifdef __cplusplus<br></font><strong><font color="#663300">}</font></strong><font color="#000099"><br>#endif<br><br>#endif /* _C_H_ */ <br></font></pre>
<p><font color="#000099"><font color="#000000">这样编译</font></font></p>
<p><font color="#000099"><font color="#999999"><em>/*-----------c.c--------------*/</em></font><font color="#ff6633"><br>int</font><font color="#000000"> add</font><strong><font color="#663300">(</font></strong><font color="#ff6633">int</font><font color="#000000"> x</font><strong><font color="#663300">,</font></strong><font color="#ff6633"> int</font><font color="#000000"> y</font><strong><font color="#663300">){</font></strong><font color="#ff0000"><br>return</font><font color="#000000"> x</font><strong><font color="#663300">+</font></strong><font color="#000000">y</font><strong><font color="#663300">;<br>}</font></strong><br></font></p>
<p><font color="#000099"><font color="#000000">这时源文件为*.c，</font><font color="#000099">__cplusplus没有被定义，<font color="#990000">extern</font><font color="#009900"> "C"</font><font color="#663300"><strong> {}</strong></font><font color="#000000">这时没有生效对于C他看到只是<font color="#990000">extern</font><font color="#ff6633"> int</font> add<strong><font color="#663300">(</font></strong><font color="#ff6633">int</font><strong><font color="#663300">,</font></strong><font color="#ff6633"> int</font><strong><font color="#663300">);</font></strong><font color="#000099"><br></font>add函数编译成_add(int, int);</font></font></font></p>
<p><font color="#000099"><font color="#000099"><font color="#000000">而编译c++源文件</font></font></font></p>
<p><font color="#000099"><font color="#000099"><font color="#999999"><em>/*-----------cpp.cpp--------------*/</em></font><font color="#000099"><br>#include "c.h"<br></font><font color="#ff6633">void</font><font color="#990000"> main</font><strong><font color="#663300">()<br>{</font></strong><br><font color="#000000">add</font><strong><font color="#663300">(</font></strong><font color="#999900">1</font><strong><font color="#663300">,</font></strong><font color="#999900"> 0</font><strong><font color="#663300">);<br>}</font></strong><br><font color="#000000">这时源文件为*.cpp,</font><font color="#000099">__cplusplus被定义,对于C++他看到的是<font color="#990000">extern</font><font color="#009900"> "C"</font><font color="#663300"><strong> {</strong><font color="#990000">extern</font><font color="#ff6633"> int</font><font color="#000000"> add</font><font color="#663300"><strong>(</strong></font><font color="#ff6633">int</font><font color="#663300"><strong>,</strong></font><font color="#ff6633"> int</font><strong><font color="#663300">);</font>}</strong></font><font color="#000000">编译器就会知道 add<strong><font color="#663300">(</font></strong><font color="#999900">1</font><strong><font color="#663300">,</font></strong><font color="#999900"> 0</font><font color="#663300"><strong>);调用的C风格的函数，就会知道去c.obj中找_add(int, int)而不是</strong><a  href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#97;&#100;&#100;&#64;&#64;&#89;&#65;&#72;&#72;&#72;&#64;&#90;">add@@YAHHH@Z</a>；</font></font></font></font></font></p>
<font color="#000099"><font color="#000099"><font color="#000099"><font color="#663300">这也就为什么DLL中常看见<font color="#990000">extern</font><font color="#009900"> "C"</font><font color="#663300"><strong> {}，</strong>windows是采用C语言编制他首先要考虑到C可以正确调用这些DLL，而用户可能会使用C++而<font color="#990000">extern</font><font color="#009900"> "C"</font><font color="#663300"><strong> {}就会发生作用</strong></font></font></font></font></font></font><img src ="http://www.cppblog.com/richardhe/aggbug/55817.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-07-10 17:27 <a href="http://www.cppblog.com/richardhe/articles/55817.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>推荐--丰富的游戏开发的站点</title><link>http://www.cppblog.com/richardhe/articles/55714.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 09 Jul 2008 07:11:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/55714.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/55714.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/55714.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/55714.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/55714.html</trackback:ping><description><![CDATA[按自已认为的站点内容的好坏排了个序的：<br>
<br>
<a  href="http://www.gameres.com/">http://www.gameres.com</a> &nbsp;&nbsp;&nbsp; 属于游戏人的中文网络平台<br>
<a  href="http://www.chinagamedev.net/">http://www.chinagamedev.net</a> &nbsp;&nbsp; 中国游戏开发者<br>
<a  href="http://www.gamedevelop.net/">http://www.gamedevelop.net</a> &nbsp;&nbsp; 中国游戏开发技术资讯网
<a  href="http://www.ogdev.net/">http://www.ogdev.net</a> &nbsp;&nbsp;&nbsp; 中国网游研发中心<br>
<a  href="http://www.chaosstars.com/">http://www.chaosstars.com</a> &nbsp;&nbsp; 中国游戏开发基地<br>
<a  href="http://gamedev.91.com/">http://gamedev.91.com</a>&nbsp;&nbsp;&nbsp; 游戏制作联盟<br>
<a  href="http://www.chinadv.com/">http://www.chinadv.com</a>  &nbsp;&nbsp; 中国数码视频在线<br>
<a  href="http://www.u9u6.com/">http://www.u9u6.com</a> &nbsp;&nbsp;&nbsp; u9u6游戏网<br>
<a  href="http://www.amanpage.com/">http://www.amanpage.com</a> &nbsp;&nbsp; 游戏开发站点<br>
<a  href="http://www.gamecollege.org/">http://www.gamecollege.org</a>  &nbsp;&nbsp; 游戏学院<br>
<br>
手机随便列的：<br>
<a  href="http://soft.yesky.com/SoftChannel/72348977504190464/20031215/1753502.shtml">http://soft.yesky.com/SoftChannel/72348977504190464/20031215/1753502.shtml</a> Java&nbsp;&nbsp; 手机游戏开发专辑<br>
<br>
国外站点：<br>
<a  href="http://www.gdse.com/">http://www.gdse.com</a>&nbsp;&nbsp;&nbsp; Game Development Search Engine<br>
<a  href="http://www.gamedev.net/">http://www.gamedev.net</a><br>
<a  href="http://www.igda.org/">http://www.igda.org</a>&nbsp;&nbsp;&nbsp; International Game Developers Association<br>
<a  href="http://www.gameinstitute.com/">http://www.gameinstitute.com</a>&nbsp;&nbsp; Game Institute<br>
<a  href="http://www.gametutorials.com/">http://www.gametutorials.com</a>&nbsp;&nbsp; Game Tutorials<br>
<a  href="http://www.sagamedev.co.za/">http://www.sagamedev.co.za</a>  &nbsp;&nbsp; South African Game Development<br>
<a  href="http://lgdc.sunsite.dk/">http://lgdc.sunsite.dk</a>&nbsp;&nbsp;&nbsp; Linux Game Development Center<br>
<br>
个人的及专题的：<br>
<a  href="http://gd.91.com/zt/ogre/index.asp">http://gd.91.com/zt/ogre/index.asp</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OGRE引擎研究站<br>
<a  href="http://www.gameres.com/Topics/Technic/OGRE">http://www.gameres.com/Topics/Technic/OGRE</a>  &nbsp;&nbsp;&nbsp;&nbsp; OGRE引擎教程<img src ="http://www.cppblog.com/richardhe/aggbug/55714.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-07-09 15:11 <a href="http://www.cppblog.com/richardhe/articles/55714.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Boost.Spirit写了一个term下c++词法高亮</title><link>http://www.cppblog.com/richardhe/articles/55612.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Tue, 08 Jul 2008 03:49:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/55612.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/55612.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/55612.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/55612.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/55612.html</trackback:ping><description><![CDATA[出自http://hi.baidu.com/yesbaba/blog/item/79c3eb13215da0d2f7039ec9.html<br>
<p><font size="3">1.1版，命令行下使用如1.0版，</font></p>
<p><font size="3">&nbsp;&nbsp;&nbsp;             增加更易用的方法：点击coco.exe，将文件拖入窗口，高亮文件输出到同名txt文件中</font></p>
<p><font size="3">&nbsp;&nbsp;&nbsp;             转成html的功能以后不懒的时候再写吧<br>
</font></p>
<p><font size="3"><br>
</font></p>
<p><font size="3">1.0版：命令行下使用，输出到stdout，</font></p>
<p><font size="3"> 可以重定向到文件，彩色复制就行了<br>
coco.exe xxx.cpp &gt; whateverfile<br>
记事本打开whateverfile，复制，在bbs上彩色粘贴<br>
编译这个大概需要2百多M内存</font></p>
<p><font size="3">想直接现在用的，可以在这下载，这是水木的附件，不知道能有效到什么时候</font></p>
<p><font size="3">http://www.newsmth.net/att.php?p.335.193566.17417.exe</font></p>
<p><font size="3">论坛可以用它来实现代码高亮显示<br>
以后再改进</font></p>
<p><font size="3">正确的代码coco应该不会分析错，能分析正确的不一定是正确的代码</font></p>
<p><font size="3">读代码可以先看spirit最基础的ch_p,str_p等，还有spirit文档中的utility</font></p>
<p><font size="3">再看如何分析文件</font></p>
<p><font size="3"><a  href="http://hi.baidu.com/yesbaba/blog/item/091ca995d0fe6e49d1135e8b.html">http://hi.baidu.com/yesbaba/blog/item/091ca995d0fe6e49d1135e8b.html</a></font></p>
<p><font size="3">gnu source highlight就可以实现这个功能了，我做这个一来是学spirit练练手，</font></p>
<p><font size="3">二来是要做个课程设计，三来这个很实用，平常在水木帖代码也能用的上<br>
</font></p>
<font size="3" face="fixedsys"><font size="3" face="fixedsys">
<p><font face="fixedsys"> <font color="#0000ff">///////////////////////////////////////////////////////////////////////////////</font><br>
<font color="#0000ff">//</font><br>
<font color="#0000ff">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;             highlight c++ source code in ansi code format</font><br>
<font color="#0000ff">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;             COCO (COlorful COde) version 1.1</font><br>
<font color="#0000ff">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;             ibe@newsmth.net</font><br>
<font color="#0000ff">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;             [ 2007-08-13 ]</font><br>
<font color="#0000ff">//</font><br>
<font color="#0000ff">///////////////////////////////////////////////////////////////////////////////</font><br>
<br>
<font color="#a020f0">#include </font><font color="#ff00ff">&lt;boost/spirit/core.hpp&gt;</font><br>
<font color="#a020f0">#include </font><font color="#ff00ff">&lt;boost/spirit/iterator/file_iterator.hpp&gt;</font><br>
<font color="#a020f0">#include </font><font color="#ff00ff">&lt;boost/spirit/utility/confix.hpp&gt;</font><br>
<font color="#a020f0">#include </font><font color="#ff00ff">&lt;boost/spirit/utility/escape_char.hpp&gt;</font><br>
<font color="#a020f0">#include </font><font color="#ff00ff">&lt;iostream&gt;</font><br>
<font color="#a020f0">#include </font><font color="#ff00ff">&lt;fstream&gt;</font><br>
<font color="#0000ff">///////////////////////////////////////////////////////////////////////////////</font><br>
<font color="#804040"><strong>using</strong></font> <font color="#2e8b57"><strong>namespace</strong></font> boost::spirit;<br>
<font color="#804040"><strong>using</strong></font> <font color="#2e8b57"><strong>namespace</strong></font> std;<br>
<br>
<font color="#0000ff">////////////////////////////////////////////////////////////////////////////</font><br>
<font color="#0000ff">//</font><br>
<font color="#0000ff">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;             Types</font><br>
<font color="#0000ff">//types needed for file parsing</font><br>
<font color="#0000ff">////////////////////////////////////////////////////////////////////////////</font><br>
<font color="#2e8b57"><strong>typedef</strong></font> <font color="#2e8b57"><strong>char</strong></font> char_t;<br>
<font color="#2e8b57"><strong>typedef</strong></font> file_iterator &lt; char_t &gt; iterator_t;<br>
<font color="#2e8b57"><strong>typedef</strong></font> scanner &lt; iterator_t &gt; scanner_t;<br>
<font color="#2e8b57"><strong>typedef</strong></font> rule &lt; scanner_t &gt; rule_t;<br>
<br>
<font color="#0000ff">////////////////////////////////////////////////////////////////////////////</font><br>
<font color="#0000ff">//To ansi，action函数，匹配后输出成ansi代码的彩色控制符，</font><br>
<font color="#0000ff">//可参考各大高校bbs的asciiart版精华区，可以输出成需要的格式，如html</font><br>
<font color="#0000ff">//</font><br>
<font color="#0000ff">////////////////////////////////////////////////////////////////////////////</font><br>
ofstream outfile;<br>
<font color="#2e8b57"><strong>namespace</strong></font> to_ansi<br>
{<br>
&nbsp;&nbsp;<font color="#2e8b57"><strong>void</strong></font><br>
&nbsp;&nbsp;            black (iterator_t first, iterator_t <font color="#2e8b57"><strong>const</strong></font> &amp;last)<br>
&nbsp;&nbsp;            {<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[1;30m"</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>while</strong></font> (first != last)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; *first++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[m"</font>;<br>
&nbsp;&nbsp;            }<br>
<br>
&nbsp;&nbsp;<font color="#2e8b57"><strong>void</strong></font><br>
&nbsp;&nbsp;            red (iterator_t first, iterator_t <font color="#2e8b57"><strong>const</strong></font> &amp;last)<br>
&nbsp;&nbsp;            {<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[1;31m"</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>while</strong></font> (first != last)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; *first++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[m"</font>;<br>
&nbsp;&nbsp;            }<br>
<br>
&nbsp;&nbsp;<font color="#2e8b57"><strong>void</strong></font><br>
&nbsp;&nbsp;            green (iterator_t first, iterator_t <font color="#2e8b57"><strong>const</strong></font> &amp;last)<br>
&nbsp;&nbsp;            {<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[1;32m"</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>while</strong></font> (first != last)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; *first++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[m"</font>;<br>
&nbsp;&nbsp;            }<br>
<br>
&nbsp;&nbsp;<font color="#2e8b57"><strong>void</strong></font><br>
&nbsp;&nbsp;            yellow (iterator_t first, iterator_t <font color="#2e8b57"><strong>const</strong></font> &amp;last)<br>
&nbsp;&nbsp;            {<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[1;33m"</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>while</strong></font> (first != last)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; *first++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[m"</font>;<br>
&nbsp;&nbsp;            }<br>
<br>
&nbsp;&nbsp;<font color="#2e8b57"><strong>void</strong></font><br>
&nbsp;&nbsp;            blue (iterator_t first, iterator_t <font color="#2e8b57"><strong>const</strong></font> &amp;last)<br>
&nbsp;&nbsp;            {<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[1;34m"</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>while</strong></font> (first != last)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; *first++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[m"</font>;<br>
&nbsp;&nbsp;            }<br>
<br>
&nbsp;&nbsp;<font color="#2e8b57"><strong>void</strong></font><br>
&nbsp;&nbsp;            magenta (iterator_t first, iterator_t <font color="#2e8b57"><strong>const</strong></font> &amp;last)<br>
&nbsp;&nbsp;            {<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[1;35m"</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>while</strong></font> (first != last)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; *first++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[m"</font>;<br>
&nbsp;&nbsp;            }<br>
<br>
&nbsp;&nbsp;<font color="#2e8b57"><strong>void</strong></font><br>
&nbsp;&nbsp;            cyan (iterator_t first, iterator_t <font color="#2e8b57"><strong>const</strong></font> &amp;last)<br>
&nbsp;&nbsp;            {<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[1;36m"</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>while</strong></font> (first != last)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; *first++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[m"</font>;<br>
&nbsp;&nbsp;            }<br>
<br>
&nbsp;&nbsp;<font color="#2e8b57"><strong>void</strong></font><br>
&nbsp;&nbsp;            white (iterator_t first, iterator_t <font color="#2e8b57"><strong>const</strong></font> &amp;last)<br>
&nbsp;&nbsp;            {<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[1;37m"</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>while</strong></font> (first != last)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; *first++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\033</font><font color="#ff00ff">[m"</font>;<br>
&nbsp;&nbsp;            }<br>
<br>
&nbsp;&nbsp;<font color="#2e8b57"><strong>void</strong></font><br>
&nbsp;&nbsp;            echo (iterator_t first, iterator_t <font color="#2e8b57"><strong>const</strong></font> &amp;last)<br>
&nbsp;&nbsp;            {<br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>while</strong></font> (first != last)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            outfile &lt;&lt; *first++;<br>
&nbsp;&nbsp;            }<br>
}<br>
<font color="#0000ff">////////////////////////////////////////////////////////////////////////////</font><br>
<font color="#0000ff">//</font><br>
<font color="#0000ff">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;             cpp lex</font><br>
<font color="#0000ff">//c++的词法描述，有了comment_p就方便多了，识别函数名还没实现</font><br>
<font color="#0000ff">////////////////////////////////////////////////////////////////////////////</font><br>
<font color="#2e8b57"><strong>namespace</strong></font> cpp_lex<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;            rule_t comment = comment_p (<font color="#ff00ff">"/*"</font>, <font color="#ff00ff">"*/"</font>) <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | comment_p (<font color="#ff00ff">"//"</font>)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            ;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            rule_t whitespace = space_p<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            ;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            rule_t include = str_p (<font color="#ff00ff">"#include"</font>) &gt;&gt; *space_p &gt;&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            (comment_p (<font color="#ff00ff">"&lt;"</font>, <font color="#ff00ff">"&gt;"</font>) | comment_p (<font color="#ff00ff">"</font><font color="#6a5acd">\"</font><font color="#ff00ff">"</font>, <font color="#ff00ff">"</font><font color="#6a5acd">\"</font><font color="#ff00ff">"</font>))<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            ;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            rule_t preprocessor = (include | <font color="#ff00ff">"##"</font> | <font color="#ff00ff">"#define"</font> | <font color="#ff00ff">"#error"</font> | (<font color="#ff00ff">"#if"</font> &gt;&gt; space_p)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"#ifdef"</font> | <font color="#ff00ff">"#ifndef"</font> | <font color="#ff00ff">"#else"</font> | <font color="#ff00ff">"#elif"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"#endif"</font> | <font color="#ff00ff">"#line"</font> | <font color="#ff00ff">"#pragma"</font> | <font color="#ff00ff">"#undef"</font> | <font color="#ff00ff">"#"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"__LINE__"</font> | <font color="#ff00ff">"__FILE__"</font> | <font color="#ff00ff">"__DATE__"</font> | <font color="#ff00ff">"__TIME__"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"_cplusplus"</font> | <font color="#ff00ff">"__STDC__"</font>)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            &gt;&gt; space_p<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            ;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            rule_t keyword_ = str_p (<font color="#ff00ff">"asm"</font>) | <font color="#ff00ff">"auto"</font> | <font color="#ff00ff">"bool"</font> | <font color="#ff00ff">"break"</font> | <font color="#ff00ff">"case"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"catch"</font> | <font color="#ff00ff">"char"</font> | <font color="#ff00ff">"class"</font> | <font color="#ff00ff">"const"</font> | <font color="#ff00ff">"const_cast"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"continue"</font> | <font color="#ff00ff">"default"</font> | <font color="#ff00ff">"delete"</font> | <font color="#ff00ff">"do"</font> | <font color="#ff00ff">"double"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"dynamic_cast"</font> | <font color="#ff00ff">"else"</font> | <font color="#ff00ff">"enum"</font> | <font color="#ff00ff">"explicit"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"extern"</font> | <font color="#ff00ff">"false"</font> | <font color="#ff00ff">"float"</font> | <font color="#ff00ff">"for"</font> | <font color="#ff00ff">"friend"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"goto"</font> | <font color="#ff00ff">"if"</font> | <font color="#ff00ff">"inline"</font> | <font color="#ff00ff">"int"</font> | <font color="#ff00ff">"long"</font> | <font color="#ff00ff">"mutable"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"namespace"</font> | <font color="#ff00ff">"new"</font> | <font color="#ff00ff">"operator"</font> | <font color="#ff00ff">"private"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"protected"</font> | <font color="#ff00ff">"public"</font> | <font color="#ff00ff">"register"</font> | <font color="#ff00ff">"reinterpret_cast"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"return"</font> | <font color="#ff00ff">"short"</font> | <font color="#ff00ff">"signed"</font> | <font color="#ff00ff">"sizeof"</font> | <font color="#ff00ff">"static"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"static_cast"</font> | <font color="#ff00ff">"struct"</font> | <font color="#ff00ff">"switch"</font> | <font color="#ff00ff">"template"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"this"</font> | <font color="#ff00ff">"throw"</font> | <font color="#ff00ff">"true"</font> | <font color="#ff00ff">"try"</font> | <font color="#ff00ff">"typedef"</font> | <font color="#ff00ff">"typeid"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"typename"</font> | <font color="#ff00ff">"union"</font> | <font color="#ff00ff">"unsighed"</font> | <font color="#ff00ff">"using"</font> | <font color="#ff00ff">"virtual"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | <font color="#ff00ff">"void"</font> | <font color="#ff00ff">"volatile"</font> | <font color="#ff00ff">"wchar_t"</font> | <font color="#ff00ff">"while"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            ;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            rule_t keyword = keyword_ &gt;&gt; space_p<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            ;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            rule_t identifer = (alpha_p | <font color="#ff00ff">'_'</font>) &gt;&gt; *(alnum_p | <font color="#ff00ff">'_'</font>)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            ;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            rule_t operators = punct_p - <font color="#ff00ff">'`'</font> - <font color="#ff00ff">'@'</font> - <font color="#ff00ff">'$'</font> - <font color="#6a5acd">'\\'</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            ;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            rule_t number = real_p<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            ;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            rule_t str = confix_p (<font color="#ff00ff">"</font><font color="#6a5acd">\"</font><font color="#ff00ff">"</font>, *c_escape_ch_p, <font color="#ff00ff">"</font><font color="#6a5acd">\"</font><font color="#ff00ff">"</font>)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            ;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            rule_t charcter = confix_p(<font color="#ff00ff">"</font><font color="#6a5acd">\'</font><font color="#ff00ff">"</font>, *c_escape_ch_p, <font color="#ff00ff">"</font><font color="#6a5acd">\'</font><font color="#ff00ff">"</font>)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            ;<br>
&nbsp;&nbsp;&nbsp;&nbsp;            rule_t constant = number<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | str<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | charcter<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            ;<br>
<br>
};<br>
<br>
<br>
<br>
<font color="#0000ff">////////////////////////////////////////////////////////////////////////////</font><br>
<font color="#0000ff">//</font><br>
<font color="#0000ff">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;             Main program</font><br>
<font color="#0000ff">//</font><br>
<font color="#0000ff">////////////////////////////////////////////////////////////////////////////</font><br>
<font color="#2e8b57"><strong>int</strong></font><br>
main (<font color="#2e8b57"><strong>int</strong></font> argc, <font color="#2e8b57"><strong>char</strong></font> *argv[])<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            string filepath;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>if</strong></font> (<font color="#ff00ff">2</font> &gt; argc)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            {<font color="#0000ff">//把要处理的文件拖到窗口内</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            cout &lt;&lt; <font color="#ff00ff">"drag file to this windows</font><font color="#6a5acd">\n</font><font color="#ff00ff">"</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            string filepath_input;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            getline(cin, filepath_input);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            filepath_input.erase(filepath_input.end()-<font color="#ff00ff">1</font>); <font color="#0000ff">//去掉结尾的'\"'</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            filepath_input.erase(filepath_input.begin());&nbsp;&nbsp;             <font color="#0000ff">//去掉开头"</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>for</strong></font> (<font color="#2e8b57"><strong>int</strong></font> i = <font color="#ff00ff">0</font>; filepath_input[i] != <font color="#ff00ff">0</font>; i++) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            filepath.push_back(filepath_input[i]);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>if</strong></font> (filepath_input[i] == <font color="#6a5acd">'\\'</font>)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            filepath.push_back(<font color="#6a5acd">'\\'</font>);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            }<font color="#804040"><strong>else</strong></font>{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">// for console usage</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            filepath = argv[<font color="#ff00ff">1</font>];<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            iterator_t first (filepath);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>if</strong></font> (!first)&nbsp;&nbsp;            {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            std::cerr &lt;&lt; <font color="#ff00ff">"Unable to open file!</font><font color="#6a5acd">\n</font><font color="#ff00ff">"</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>return</strong></font> -<font color="#ff00ff">1</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            }<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">// Create an EOF iterator</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            iterator_t last = first.make_end ();<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            string filepath_output = filepath+<font color="#ff00ff">".txt"</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            outfile.open(filepath_output.c_str());<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">// A simple rule词法对应的颜色在这里改就可以</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            rule_t r = *(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            cpp_lex::comment[&amp;to_ansi::cyan]<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | cpp_lex::constant[&amp;to_ansi::yellow]<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | cpp_lex::preprocessor[&amp;to_ansi::red]<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | cpp_lex::keyword[&amp;to_ansi::green]<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | cpp_lex::whitespace[&amp;to_ansi::echo]<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | cpp_lex::operators[&amp;to_ansi::magenta]<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            | cpp_lex::identifer[&amp;to_ansi::white]<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            )<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            ;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">//&nbsp;&nbsp;             </font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">// Parse</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">/*</font><font color="#0000ff">The parse_info structure</font><br>
<font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;             The functions above return a parse_info structure parameterized by the iterator type passed in. </font><br>
<font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;             The parse_info struct has these members:parse_info</font><br>
<font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stop&nbsp;&nbsp;&nbsp;&nbsp; Points to the final parse
position (i.e The parser recognized and processed the input up to this
point)</font><br>
<font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hit&nbsp;&nbsp;&nbsp;&nbsp; True if parsing is successful.
This may be full: the parser consumed all the input, or partial: the
parser consumed only a portion of the input.</font><br>
<font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;             full&nbsp;&nbsp;&nbsp;&nbsp;            True when we have a full match (i.e The parser consumed all the input).</font><br>
<font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; length&nbsp;&nbsp;&nbsp;&nbsp; The number of characters
consumed by the parser. This is valid only if we have a successful
match (either partial or full). </font><br>
<font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#0000ff">*/</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            parse_info &lt; iterator_t &gt; info = parse (first, last, r);<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">// This really shouldn't fail...</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>if</strong></font> (info.full)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            std::cout &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\n</font><font color="#ff00ff">Parse succeeded!</font><font color="#6a5acd">\n</font><font color="#ff00ff">"</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>else</strong></font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;            std::cout &lt;&lt; <font color="#ff00ff">"</font><font color="#6a5acd">\n</font><font color="#ff00ff">Parse failed!</font><font color="#6a5acd">\n</font><font color="#ff00ff">"</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
</font><font face="fixedsys"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;         std::cout &lt;&lt; <font color="#ff00ff">"highlight file saved in "</font> &lt;&lt; filepath_output &lt;&lt; endl;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;        string end;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;        getline (cin, end);<font color="#0000ff">//按回车键推出窗口</font><br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#804040"><strong>return</strong></font> <font color="#ff00ff">0</font>;<br>
}</font></p>
<p> </p>
<div forimg="1"> </div>
<font face="fixedsys"><br>
这只是个词法高亮程序，还不能识别语法，如函数等，用正则也能做，但boost.spirit更简单</font></font></font><br><br><img src ="http://www.cppblog.com/richardhe/aggbug/55612.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-07-08 11:49 <a href="http://www.cppblog.com/richardhe/articles/55612.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>内存分区</title><link>http://www.cppblog.com/richardhe/articles/55395.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Sat, 05 Jul 2008 05:39:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/55395.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/55395.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/55395.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/55395.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/55395.html</trackback:ping><description><![CDATA[来自http://www.cppblog.com/road420/archive/2008/07/05/55388.html<br>
<p><strong>五大内存分区</strong><br>&nbsp;&nbsp;&nbsp; 在C++中，内存分成5个区，他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。<br>&nbsp;&nbsp;&nbsp; 栈，就是那些由编译器在需要的时候分配，在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。<br>&nbsp;&nbsp;&nbsp; 堆，就是那些由new分配的内存块，他们的释放编译器不去管，由我们的应用程序去控制，一般一个new就要对应一个delete。如果程序员没有释放掉，那么在程序结束后，操作系统会自动回收。<br>&nbsp;&nbsp;&nbsp; 自由存储区，就是那些由malloc等分配的内存块，他和堆是十分相似的，不过它是用free来结束自己的生命的。<br>&nbsp;&nbsp;&nbsp; 全局/静态存储区，全局变量和静态变量被分配到同一块内存中，在以前的C语言中，全局变量又分为初始化的和未初始化的，在C++里面没有这个区分了，他们共同占用同一块内存区。<br>&nbsp;&nbsp;&nbsp; 常量存储区，这是一块比较特殊的存储区，他们里面存放的是常量，不允许修改（当然，你要通过非正当手段也可以修改，而且方法很多）<br><strong>明确区分堆与栈<br></strong>&nbsp;&nbsp;&nbsp; 在bbs上，堆与栈的区分问题，似乎是一个永恒的话题，由此可见，初学者对此往往是混淆不清的，所以我决定拿他第一个开刀。<br>&nbsp;&nbsp;&nbsp; 首先，我们举一个例子：<br>&nbsp;&nbsp;&nbsp; void f() { int* p=new int[5]; } <br>&nbsp;&nbsp;&nbsp;
这条短短的一句话就包含了堆与栈，看到new，我们首先就应该想到，我们分配了一块堆内存，那么指针p呢？他分配的是一块栈内存，所以这句话的意思就是：
在栈内存中存放了一个指向一块堆内存的指针p。在程序会先确定在堆中分配内存的大小，然后调用operator
new分配内存，然后返回这块内存的首地址，放入栈中，他在VC6下的汇编代码如下：<br>&nbsp;&nbsp;&nbsp; 00401028&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 14h<br>&nbsp;&nbsp;&nbsp; 0040102A&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; operator new (00401060)<br>&nbsp;&nbsp;&nbsp; 0040102F&nbsp;&nbsp; add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,4<br>&nbsp;&nbsp;&nbsp; 00401032&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ebp-8],eax<br>&nbsp;&nbsp;&nbsp; 00401035&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,dword ptr [ebp-8]<br>&nbsp;&nbsp;&nbsp; 00401038&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ebp-4],eax<br>&nbsp;&nbsp;&nbsp; 这里，我们为了简单并没有释放内存，那么该怎么去释放呢？是delete p么？澳，错了，应该是delete []p，这是为了告诉编译器：我删除的是一个数组，VC6就会根据相应的Cookie信息去进行释放内存的工作。<br>&nbsp;&nbsp;&nbsp; 好了，我们回到我们的主题：堆和栈究竟有什么区别？ <br>&nbsp;&nbsp;&nbsp; 主要的区别由以下几点：<br>&nbsp;&nbsp;&nbsp; 1、管理方式不同；<br>&nbsp;&nbsp;&nbsp; 2、空间大小不同；<br>&nbsp;&nbsp;&nbsp; 3、能否产生碎片不同；<br>&nbsp;&nbsp;&nbsp; 4、生长方向不同；<br>&nbsp;&nbsp;&nbsp; 5、分配方式不同；<br>&nbsp;&nbsp;&nbsp; 6、分配效率不同；<br>&nbsp;&nbsp;&nbsp; 管理方式：对于栈来讲，是由编译器自动管理，无需我们手工控制；对于堆来说，释放工作由程序员控制，容易产生memory leak。<br>&nbsp;&nbsp;&nbsp; 空间大小：一般来讲在32位系统下，堆内存可以达到4G的空间，从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲，一般都是有一定的空间大小的，例如，在VC6下面，默认的栈空间大小是1M（好像是，记不清楚了）。当然，我们可以修改：&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 打开工程，依次操作菜单如下：Project-&gt;Setting-&gt;Link，在Category 中选中Output，然后在Reserve中设定堆栈的最大值和commit。<br>注意：reserve最小值为4Byte；commit是保留在虚拟内存的页文件里面，它设置的较大会使栈开辟较大的值，可能增加内存的开销和启动时间。<br>&nbsp;&nbsp;&nbsp;
碎片问题：对于堆来讲，频繁的new/delete势必会造成内存空间的不连续，从而造成大量的碎片，使程序效率降低。对于栈来讲，则不会存在这个问题，
因为栈是先进后出的队列，他们是如此的一一对应，以至于永远都不可能有一个内存块从栈中间弹出，在他弹出之前，在他上面的后进的栈内容已经被弹出，详细的
可以参考数据结构，这里我们就不再一一讨论了。<br>&nbsp;&nbsp;&nbsp; 生长方向：对于堆来讲，生长方向是向上的，也就是向着内存地址增加的方向；对于栈来讲，它的生长方向是向下的，是向着内存地址减小的方向增长。<br>&nbsp;&nbsp;&nbsp; 分配方式：堆都是动态分配的，没有静态分配的堆。栈有2种分配方式：静态分配和动态分配。静态分配是编译器完成的，比如局部变量的分配。动态分配由alloca函数进行分配，但是栈的动态分配和堆是不同的，他的动态分配是由编译器进行释放，无需我们手工实现。<br>&nbsp;&nbsp;&nbsp;
分配效率：栈是机器系统提供的数据结构，计算机会在底层对栈提供支持：分配专门的寄存器存放栈的地址，压栈出栈都有专门的指令执行，这就决定了栈的效率比
较高。堆则是C/C++函数库提供的，它的机制是很复杂的，例如为了分配一块内存，库函数会按照一定的算法（具体的算法可以参考数据结构/操作系统）在堆
内存中搜索可用的足够大小的空间，如果没有足够大小的空间（可能是由于内存碎片太多），就有可能调用系统功能去增加程序数据段的内存空间，这样就有机会分
到足够大小的内存，然后进行返回。显然，堆的效率比栈要低得多。<br>&nbsp;&nbsp;&nbsp;
从这里我们可以看到，堆和栈相比，由于大量new/delete的使用，容易造成大量的内存碎片；由于没有专门的系统支持，效率很低；由于可能引发用户态
和核心态的切换，内存的申请，代价变得更加昂贵。所以栈在程序中是应用最广泛的，就算是函数的调用也利用栈去完成，函数调用过程中的参数，返回地
址，EBP和局部变量都采用栈的方式存放。所以，我们推荐大家尽量用栈，而不是用堆。<br>&nbsp;&nbsp;&nbsp; 虽然栈有如此众多的好处，但是由于和堆相比不是那么灵活，有时候分配大量的内存空间，还是用堆好一些。<br>&nbsp;&nbsp;&nbsp;
无论是堆还是栈，都要防止越界现象的发生（除非你是故意使其越界），因为越界的结果要么是程序崩溃，要么是摧毁程序的堆、栈结构，产生以想不到的结果,就
算是在你的程序运行过程中，没有发生上面的问题，你还是要小心，说不定什么时候就崩掉，那时候debug可是相当困难的：）<br>&nbsp;&nbsp;&nbsp; 对了，还有一件事，如果有人把堆栈合起来说，那它的意思是栈，可不是堆，呵呵，清楚了？<br><strong>static用来控制变量的存储方式和可见性<br></strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
函数内部定义的变量，在程序执行到它的定义处时，编译器为它在栈上分配空间，函数在栈上分配的空间在此函数执行结束时会释放掉，这样就产生了一个问题:
如果想将函数中此变量的值保存至下一次调用时，如何实现？
最容易想到的方法是定义一个全局的变量，但定义为一个全局变量有许多缺点，最明显的缺点是破坏了此变量的访问范围（使得在此函数中定义的变量，不仅仅受此
函数控制）。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部，对外不可见。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static的内部机制：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用，所以静态数据成员不能在任何函数内分配空间和初始化。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这样，它的空间分配有三个可能的地方，一是作为类的外部接口的头文件，那里有类声明；二是类定义的内部实现，那里有类的成员函数定义；三是应用程序的main（）函数前的全局数据声明和定义处。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
静态数据成员要实际地分配空间，故不能在类的声明中定义（只能声明数据成员）。类声明只声明一个类的&#8220;尺寸和规格&#8221;，并不进行实际的内存分配，所以在类声
明中写成定义是错误的。它也不能在头文件中类声明的外部定义，因为那会造成在多个使用该类的源文件中，对其重复定义。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static被引入以告知编译器，将变量存储在程序的静态存储区而非栈上空间，静态<br>数据成员按定义出现的先后顺序依次初始化，注意静态成员嵌套时，要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static的优势：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
可以节省内存，因为它是所有对象所公有的，因此，对多个对象来说，静态数据成员只存储一处，供所有对象共用。静态数据成员的值对每个对象都是一样，但它的
值是可以更新的。只要对静态数据成员的值更新一次，保证所有对象存取更新后的相同的值，这样可以提高时间效率。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 引用静态数据成员时，采用如下格式：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;类名&gt;::&lt;静态成员名&gt;<br>&nbsp;&nbsp;&nbsp; 如果静态数据成员的访问权限允许的话(即public的成员)，可在程序中，按上述格式<br>来引用静态数据成员。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PS:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (1)类的静态成员函数是属于整个类而非类的对象，所以它没有this指针，这就导致<br>了它仅能访问类的静态数据和静态成员函数。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (2)不能将静态成员函数定义为虚函数。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (3)由于静态成员声明于类中，操作于其外，所以对其取地址操作，就多少有些特殊<br>，变量地址是指向其数据类型的指针 ，函数地址类型是一个&#8220;nonmember函数指针&#8221;。</p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (4)由于静态成员函数没有this指针，所以就差不多等同于nonmember函数，结果就<br>产生了一个意想不到的好处：成为一个callback函数，使得我们得以将C++和C-based X W<br>indow系统结合，同时也成功的应用于线程函数身上。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (5)static并没有增加程序的时空开销，相反她还缩短了子类对父类静态成员的访问<br>时间，节省了子类的内存空间。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (6)静态数据成员在&lt;定义或说明&gt;时前面加关键字static。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (7)静态数据成员是静态存储的，所以必须对它进行初始化。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (8)静态成员初始化与一般数据成员初始化不同:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 初始化在类体外进行，而前面不加static，以免与一般静态变量或对象相混淆；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 初始化时不加该成员的访问权限控制符private，public等；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 初始化时使用作用域运算符来标明它所属类；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所以我们得出静态数据成员初始化的格式：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;数据类型&gt;&lt;类名&gt;::&lt;静态数据成员名&gt;=&lt;值&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
(9)为了防止父类的影响，可以在子类定义一个与父类相同的静态变量，以屏蔽父类的影响。这里有一点需要注意：我们说静态成员为父类和子类共享，但我们有
重复定义了静态成员，这会不会引起错误呢？不会，我们的编译器采用了一种绝妙的手法：name-mangling 用以生成唯一的标志。<br><br><img src ="http://www.cppblog.com/richardhe/aggbug/55395.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-07-05 13:39 <a href="http://www.cppblog.com/richardhe/articles/55395.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>回调函数</title><link>http://www.cppblog.com/richardhe/articles/55396.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Sat, 05 Jul 2008 05:39:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/55396.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/55396.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/55396.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/55396.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/55396.html</trackback:ping><description><![CDATA[来自http://www.cppblog.com/road420/archive/2008/07/05/55390.html<br>
<p><strong>简介<br><br></strong>　　对于很多初学者来说，往往觉得回调函数很神秘，很想知道回调函数的工作原理。本文将要解释什么是回调函数、它们有什么好处、为什么要使用它们等等问题，在开始之前，假设你已经熟知了函数指针。 <br><br>　　<strong>什么是回调函数？</strong><br><br>　　简而言之，回调函数就是一个通过函数指针调用的函数。如果你把函数的指针（地址）作为参数传递给另一个函数，当这个指针被用为调用它所指向的函数时，我们就说这是回调函数。<br><br>　　<strong>为什么要使用回调函数？</strong><br><br>　　因为可以把调用者与被调用者分开。调用者不关心谁是被调用者，所有它需知道的，只是存在一个具有某种特定原型、某些限制条件（如返回值为int）的被调用函数。<br><br>
如果想知道回调函数在实际中有什么作用，先假设有这样一种情况，我们要编写一个库，它提供了某些排序算法的实现，如冒泡排序、快速排序、shell排
序、shake排序等等，但为使库更加通用，不想在函数中嵌入排序逻辑，而让使用者来实现相应的逻辑；或者，想让库可用于多种数据类型（int、
float、string），此时，该怎么办呢？可以使用函数指针，并进行回调。<br><br>　　回调可用于通知机制，例如，有时要在程序中设置一个
计时器，每到一定时间，程序会得到相应的通知，但通知机制的实现者对我们的程序一无所知。而此时，就需有一个特定原型的函数指针，用这个指针来进行回调，
来通知我们的程序事件已经发生。实际上，SetTimer()
API使用了一个回调函数来通知计时器，而且，万一没有提供回调函数，它还会把一个消息发往程序的消息队列。<br><br>　　另一个使用回调机制的
API函数是EnumWindow()，它枚举屏幕上所有的顶层窗口，为每个窗口调用一个程序提供的函数，并传递窗口的处理程序。如果被调用者返回一个
值，就继续进行迭代，否则，退出。EnumWindow()并不关心被调用者在何处，也不关心被调用者用它传递的处理程序做了什么，它只关心返回值，因为
基于返回值，它将继续执行或退出。<br><br>　　不管怎么说，回调函数是继续自C语言的，因而，在C++中，应只在与C代码建立接口，或与已有的回调接口打交道时，才使用回调函数。除了上述情况，在C++中应使用虚拟方法或函数符（functor），而不是回调函数。<br><br>　　<strong>一个简单的回调函数实现</strong><br><br>
下面创建了一个sort.dll的动态链接库，它导出了一个名为CompareFunction的类型--typedef int
(__stdcall *CompareFunction)(const byte*, const
byte*)，它就是回调函数的类型。另外，它也导出了两个方法：Bubblesort()和Quicksort()，这两个方法原型相同，但实现了不同
的排序算法。<br><br>
<table width="90%" align="center" bgcolor="#e7e9e9" border="1" bordercolor="#cccccc">
    <tbody>
        <tr>
            <td>void DLLDIR __stdcall Bubblesort(byte* array,int size,int elem_size,CompareFunction cmpFunc);<br><br>void DLLDIR __stdcall Quicksort(byte* array,int size,int elem_size,CompareFunction cmpFunc);</td>
        </tr>
    </tbody>
</table>
<br>　　这两个函数接受以下参数：<br><br>　　&#183;byte * array：指向元素数组的指针（任意类型）。<br><br>　　&#183;int size：数组中元素的个数。<br><br>　　&#183;int elem_size：数组中一个元素的大小，以字节为单位。<br><br>　　&#183;CompareFunction cmpFunc：带有上述原型的指向回调函数的指针。<br><br>
这两个函数的会对数组进行某种排序，但每次都需决定两个元素哪个排在前面，而函数中有一个回调函数，其地址是作为一个参数传递进来的。对编写者来说，不
必介意函数在何处实现，或它怎样被实现的，所需在意的只是两个用于比较的元素的地址，并返回以下的某个值（库的编写者和使用者都必须遵守这个约定）：<br><br>　　&#183;-1：如果第一个元素较小，那它在已排序好的数组中，应该排在第二个元素前面。<br><br>　　&#183;0：如果两个元素相等，那么它们的相对位置并不重要，在已排序好的数组中，谁在前面都无所谓。 <br><br>　　&#183;1：如果第一个元素较大，那在已排序好的数组中，它应该排第二个元素后面。<br><br>　　基于以上约定，函数Bubblesort()的实现如下，Quicksort()就稍微复杂一点：<br><br>
<table width="90%" align="center" bgcolor="#e7e9e9" border="1" bordercolor="#cccccc">
    <tbody>
        <tr>
            <td>void DLLDIR __stdcall Bubblesort(byte* array,int size,int elem_size,CompareFunction cmpFunc)<br>{<br>　for(int i=0; i &lt; size; i++)<br>　{<br>　　for(int j=0; j &lt; size-1; j++)<br>　　{<br>　　　//回调比较函数<br>　　　if(1 == (*cmpFunc)(array+j*elem_size,array+(j+1)*elem_size))<br>　　　{<br>　　　　//两个相比较的元素相交换<br>　　　　byte* temp = new byte[elem_size];<br>　　　　memcpy(temp, array+j*elem_size, elem_size);<br>　　　　memcpy(array+j*elem_size,array+(j+1)*elem_size,elem_size);<br>　　　　memcpy(array+(j+1)*elem_size, temp, elem_size);<br>　　　　delete [] temp;<br>　　　}<br>　　}<br>　}<br>}</td>
        </tr>
    </tbody>
</table>
<br>　　注意：因为实现中使用了memcpy()，所以函数在使用的数据类型方面，会有所局限。<br><br>　　对使用者来说，必须有一个回调函数，其地址要传递给Bubblesort()函数。下面有二个简单的示例，一个比较两个整数，而另一个比较两个字符串：<br><br>
<table width="90%" align="center" bgcolor="#e7e9e9" border="1" bordercolor="#cccccc">
    <tbody>
        <tr>
            <td>int __stdcall CompareInts(const byte* velem1, const byte* velem2)<br>{<br>　int elem1 = *(int*)velem1;<br>　int elem2 = *(int*)velem2;<br><br>　if(elem1 &lt; elem2)<br>　　return -1;<br>　if(elem1 &gt; elem2)<br>　　return 1;<br><br>　return 0;<br>}<br><br>int __stdcall CompareStrings(const byte* velem1, const byte* velem2)<br>{<br>　const char* elem1 = (char*)velem1;<br>　const char* elem2 = (char*)velem2;<br>　return strcmp(elem1, elem2);<br>}</td>
        </tr>
    </tbody>
</table>
<br>　　下面另有一个程序，用于测试以上所有的代码，它传递了一个有5个元素的数组给Bubblesort()和Quicksort()，同时还传递了一个指向回调函数的指针。<br><br>
<table width="90%" align="center" bgcolor="#e7e9e9" border="1" bordercolor="#cccccc">
    <tbody>
        <tr>
            <td>int main(int argc, char* argv[])<br>{<br>　int i;<br>　int array[] = {5432, 4321, 3210, 2109, 1098};<br><br>　cout &lt;&lt; "Before sorting ints with Bubblesort\n";<br>　for(i=0; i &lt; 5; i++)<br>　　cout &lt;&lt; array[i] &lt;&lt; '\n';<br><br>　Bubblesort((byte*)array, 5, sizeof(array[0]), &amp;CompareInts);<br><br>　cout &lt;&lt; "After the sorting\n";<br>　for(i=0; i &lt; 5; i++)<br>　　cout &lt;&lt; array[i] &lt;&lt; '\n';<br><br>　const char str[5][10] = {"estella","danielle","crissy","bo","angie"};<br><br>　cout &lt;&lt; "Before sorting strings with Quicksort\n";<br>　for(i=0; i &lt; 5; i++)<br>　　cout &lt;&lt; str[i] &lt;&lt; '\n';<br><br>　Quicksort((byte*)str, 5, 10, &amp;CompareStrings);<br><br>　cout &lt;&lt; "After the sorting\n";<br>　for(i=0; i &lt; 5; i++)<br>　　cout &lt;&lt; str[i] &lt;&lt; '\n';<br><br>　return 0;<br>}</td>
        </tr>
    </tbody>
</table>
<br>　　如果想进行降序排序（大元素在先），就只需修改回调函数的代码，或使用另一个回调函数，这样编程起来灵活性就比较大了。</p>
<p><strong>调用约定<br><br></strong>　　上面的代码中，可在函数原型中找到__stdcall，因为它以双下划线打头，所
以它是一个特定于编译器的扩展，说到底也就是微软的实现。任何支持开发基于Win32的程序都必须支持这个扩展或其等价物。以__stdcall标识的函
数使用了标准调用约定，为什么叫标准约定呢，因为所有的Win32
API（除了个别接受可变参数的除外）都使用它。标准调用约定的函数在它们返回到调用者之前，都会从堆栈中移除掉参数，这也是Pascal的标准约定。但
在C/C++中，调用约定是调用者负责清理堆栈，而不是被调用函数；为强制函数使用C/C++调用约定，可使用__cdecl。另外，可变参数函数也使用
C/C++调用约定。<br><br>　　Windows操作系统采用了标准调用约定（Pascal约定），因为其可减小代码的体积。这点对早期的Windows来说非常重要，因为那时它运行在只有640KB内存的电脑上。<br><br>　　如果你不喜欢__stdcall，还可以使用CALLBACK宏，它定义在windef.h中：<br><br>
<table width="90%" align="center" bgcolor="#e7e9e9" border="1" bordercolor="#cccccc">
    <tbody>
        <tr>
            <td>#define CALLBACK __stdcallor<br><br>#define CALLBACK PASCAL //而PASCAL在此被#defined成__stdcall</td>
        </tr>
    </tbody>
</table>
<br>　　<strong>作为回调函数的C++方法</strong><br><br>　　因为平时很可能会使用到C++编写代码，也许会想到把回调函数写成类中的一个方法，但先来看看以下的代码：<br><br>
<table width="90%" align="center" bgcolor="#e7e9e9" border="1" bordercolor="#cccccc">
    <tbody>
        <tr>
            <td>class CCallbackTester<br>{<br>　public:<br>　int CALLBACK CompareInts(const byte* velem1, const byte* velem2);<br>};<br><br>Bubblesort((byte*)array, 5, sizeof(array[0]),<br>&amp;CCallbackTester::CompareInts);</td>
        </tr>
    </tbody>
</table>
<br>　　如果使用微软的编译器，将会得到下面这个编译错误：<br><br>
<table width="90%" align="center" bgcolor="#e7e9e9" border="1" bordercolor="#cccccc">
    <tbody>
        <tr>
            <td>error
            C2664: 'Bubblesort' : cannot convert parameter 4 from 'int (__stdcall
            CCallbackTester::*)(const unsigned char *,const unsigned char *)' to
            'int (__stdcall *)(const unsigned char *,const unsigned char *)' There
            is no context in which this conversion is possible</td>
        </tr>
    </tbody>
</table>
<br>　　这是因为非静态成员函数有一个额外的参数：this指针，这将迫使你在成员函数前面加上static。当然，还有几种方法可以解决这个问题，但限于篇幅，就不再论述了。</p>
<br><br><img src ="http://www.cppblog.com/richardhe/aggbug/55396.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-07-05 13:39 <a href="http://www.cppblog.com/richardhe/articles/55396.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Boost.Singals 教程</title><link>http://www.cppblog.com/richardhe/articles/55334.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Fri, 04 Jul 2008 05:23:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/55334.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/55334.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/55334.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/55334.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/55334.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 来自http://www.cppblog.com/jinq0123/archive/2008/06/30/boostsignalstutorial.htmlBoost.Singals 教程                                    Home            Libraries            People   ...&nbsp;&nbsp;<a href='http://www.cppblog.com/richardhe/articles/55334.html'>阅读全文</a><img src ="http://www.cppblog.com/richardhe/aggbug/55334.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-07-04 13:23 <a href="http://www.cppblog.com/richardhe/articles/55334.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[摘录]回调函数与转移表</title><link>http://www.cppblog.com/richardhe/articles/55333.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Fri, 04 Jul 2008 05:18:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/55333.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/55333.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/55333.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/55333.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/55333.html</trackback:ping><description><![CDATA[出自http://www.cppblog.com/zhaoyg/archive/2008/07/03/55206.html<br>
<p style="font-size: 10pt;">摘录自 《C和指针》<strong><br>1.回调函数</strong><br>这里有一个简单的函数,它用于在一个单链表中查找一个值,它的参数是一个指向链表第一个节点的指针以及那个需要查找的值.<br>Node* search_list(Node* node,int const value)<br>{&nbsp; <br>&nbsp;&nbsp; while(node!=NULL)<br>&nbsp; {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; if(node-&gt;value==value)&nbsp; break;<br>&nbsp;&nbsp;&nbsp; node=node-&gt;link;<br>&nbsp; }<br>&nbsp; return node;<br>}<br>&nbsp;这
个函数看上去相当简单,但它只适用于值为整数的链表,如果你需要在一个字符串链表中查找,你不得不另外编写一个函数,这个函数和上面那个函数的绝大部分代
码相同,只是第二个参数的类型以及节点值的比较方法不同.一种更为通用的方法是查找函数与类型无关,这样它就能用于任何类型的值的链表,我们必须对函数的
两个方面进行修改,使它与类型无关.<br>首先我们必须改变比较的执行方式,这样函数就可以对任何类型的值进行比较.这个目标听上去好象不可能,如果你
编写语句用于比较整型值,它怎么还可能用于其他类型如字符串的比较呢?解决方案就是使用函数指针,调用者编写一个函数,用于比较两个值,然后把一个指向这
个函数的指针作为参数传递给查找函数.然后查找函数调用这个函数来执行值的比较,使用这种方法,任何类型的值都可以进行比较.我们必须修改的第二个方面是
向函数传递一个指向值的指针而不是本身.函数由一个void
*形参,用于接收这个参数,然后指向这个值的指针便传递给比较函数,这个修改使字符串和数组对象也可以被使用,字符串和数组无法作为参数传递给函数,但指
向它们的指针可以.<br>使用这种技巧的函数叫"回调函数"(callback
function);因为用户把一个函数指针作为参数传递给其他函数,后者将"回调"用户的函数.任何时候,如果你所编写的函数必须能够在不同的时刻执行
不同类型的工作或执行只能由函数调用者定义的工作,你都可以使用这个技巧.许多窗口系统使用回调函数连接多个动作,如拖拽鼠标和点击按钮来指定用户程序中
的某个特定函数.我们无法在这个上下文环境中为回调函数编写一个准确的原型,因为我们并不知道进行比较的值的类型.事实上,我们需要查找函数能作用于任何
类型的值,解决这个难题的方法是把参数类型声明为"void *",表示"一个指向未知类型的指针".</p>
<p style="font-size: 10pt;"><br>/***在一个单链表中查找一个指定值的函数,它的参数是一个指向链表第一个节点<br>&nbsp;&nbsp; **的指针,一个指向我们需要查找的值的指针和一个函数指针,它所指向的函数<br>&nbsp;&nbsp; **用于比较存储于此链表中的类型的值.<br>*/<br>#include "node.h"<br>Node* search_list(Node *node,void&nbsp; const *value, int(*compare)(void const*,void const*)) //函数声明;<br>{&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp; while&nbsp;&nbsp; (node!=NULL)<br>&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; if(compare(&amp;node-&gt;value,value)==0)&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp; node=node-&gt;link;<br>&nbsp; }<br>&nbsp; return node;<br>}<br>同时注意虽然函数不会修改参数node所指向的任何节点,但node并未声明为const。如果node被声明为const,函数不得不返回一个const结果，这将限制调用程序，它便无法修改查找函数所找到的节点。<br>&nbsp;在一个特定的链表中进行查找时，用户需要编写一个适当的比较函数，并把指向该函数的指针和指向需要查找的值的指针传递给查找函数。<br>例如，下面是一个比较函数，它用于在一个整数链表中进行查找。<br>int compare_ints(void const* a,void const* b)<br>{<br>&nbsp;&nbsp;&nbsp; if(*(int*)a==*(int*)b)&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>&nbsp;&nbsp;&nbsp; else&nbsp;&nbsp;&nbsp;&nbsp; return 1;<br>}<br>这个函数将像下面这样使用：<br>desired_node=search_list(root,&amp;desired_value,compare_ints);</p>
<p style="font-size: 10pt;"><strong>2.转换表（jump table)</strong><br>&nbsp;转移表最好用个例子来解释。下面的代码段取自一个程序，它用于实现一个袖珍式计算器。程序的其他部分已经读入两个数（op1和op2）和一个操作符（oper)。下面的代码对操作符进行测试，最后决定调用哪个函数。<br>switch(oper)<br>{<br>&nbsp; case ADD:&nbsp;&nbsp; result=add(op1,op2);break;<br>&nbsp; case SUB:&nbsp;&nbsp;&nbsp; result=sub(op1,op2);break;<br>&nbsp; case MUL:&nbsp;&nbsp;&nbsp; result=mul(op1,op2);break;<br>&nbsp; case DIV:&nbsp;&nbsp;&nbsp;&nbsp; result=div(op1,op2);break;<br>&nbsp; ......<br>}</p>
<p style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
对于一个新奇的具有上百个操作符的计算器，这条switch语句将会非常之长。为什么要调用函数来执行这些操作呢？把具体操作和选择操作的代码分开是一种
良好的设计方案。更为复杂的操作将肯定以独立的函数来实现，因为它们的长度可能很长。但即使是简单的操作也可能具有副作用，例如保存一个常量值用于以后的
操作。<br>为了使用switch语句，表示操作符的代码必须是整数。如果它们是从零开始连续的整数，我们可以使用转换表来实现相同的任务。转换表就是一个函数指针数组。<br>创建一个转换表需要两个步骤。首先，声明并初始化一个函数指针数组。唯一需要留心之处就是确保这些函数的原型出现在这个数组的声明之前。</p>
<p style="font-size: 10pt;">double add(double,double);<br>double sub(double,double);<br>double mul(double,double);<br>double div(double,double);</p>
<p style="font-size: 10pt;">double (*oper_func[])(double,double)={add,sub,mul,div,...}；</p>
<p style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 初始化列表中各个函数名的正确顺序取决于程序中用于表示每个操作符的整型代码。这个例子假定ADD是0，SUB是1，MUL是2，接下去以此类推。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第二个步骤是用下面这条语句替换前面整条switch语句！<br>result=oper_func[oper](op1,op2);<br>oper从数组中选择正确的函数指针，而函数调用操作符将执行这个函数。</p>
<br><br><img src ="http://www.cppblog.com/richardhe/aggbug/55333.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-07-04 13:18 <a href="http://www.cppblog.com/richardhe/articles/55333.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MD5算法的C++实现</title><link>http://www.cppblog.com/richardhe/articles/54567.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 25 Jun 2008 09:12:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/54567.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/54567.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/54567.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/54567.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/54567.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 来自http://www.cppblog.com/ant/archive/2007/09/11/31886.htmlMD5算法的C++实现1. IntroductionMD5算法是一种消息摘要算法(Message Digest Algorithm)，此算法以任意长度的信息(message)作为输入进行计算，产生一个128-bit(16-byte)的指纹或报文摘要(finge...&nbsp;&nbsp;<a href='http://www.cppblog.com/richardhe/articles/54567.html'>阅读全文</a><img src ="http://www.cppblog.com/richardhe/aggbug/54567.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-06-25 17:12 <a href="http://www.cppblog.com/richardhe/articles/54567.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 基于C++有限状态机的实现技术(调查报告)</title><link>http://www.cppblog.com/richardhe/articles/53134.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Fri, 13 Jun 2008 07:01:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/53134.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/53134.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/53134.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/53134.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/53134.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 一.引言言有限状态机是一种用来进行对象行为建模的工具，其作用主要是描述对象在它的生命周期内所经历的状态序列，以及如何响应来自外界的各种事件。在面向对象的软件系统中，一个对象无论多么简单或者多么复杂，都必然会经历一个从开始创建到最终消亡的完整过程，这通常被称为对象的生命周期。一般说来，对象在其生命期内是不可能完全孤立的，它必须通过发送消息来影响其它对象，或者通过接受消息来改变自身。在...&nbsp;&nbsp;<a href='http://www.cppblog.com/richardhe/articles/53134.html'>阅读全文</a><img src ="http://www.cppblog.com/richardhe/aggbug/53134.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-06-13 15:01 <a href="http://www.cppblog.com/richardhe/articles/53134.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>有限状态机的实现</title><link>http://www.cppblog.com/richardhe/articles/53131.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Fri, 13 Jun 2008 06:48:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/53131.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/53131.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/53131.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/53131.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/53131.html</trackback:ping><description><![CDATA[有限状态机（Finite State Machine或者Finite State Automata)是软件领域中一种重要的工具，很多东西的模型实际上就是有限状态机。<br><br>最近看了一些游戏编程AI的材料，感觉游戏中的AI，第一要说的就是有限状态机来实现精灵的AI，然后才是A*寻路，其他学术界讨论比较多的神经网络、模糊控制等问题还不是很热。<br><br>FSM的实现方式：<br>1） switch/case或者if/else<br>这无意是最直观的方式，使用一堆条件判断，会编程的人都可以做到，对简单小巧的状态机来说最合适，但是毫无疑问，这样的方式比较原始，对庞大的状态机难以维护。<br><br>2） 状态表<br>维护一个二维状态表，横坐标表示当前状态，纵坐标表示输入，表中一个元素存储下一个状态和对应的操作。这一招易于维护，但是运行时间和存储空间的代价较大。<br><br>3） 使用State Pattern<br>使
用State Pattern使得代码的维护比switch/case方式稍好，性能上也不会有很多的影响，但是也不是100％完美。不过Robert
C. Martin做了两个自动产生FSM代码的工具，for java和for
C++各一个，在http://www.objectmentor.com/resources/index上有免费下载，这个工具的输入是纯文本的状态
机描述，自动产生符合State
Pattern的代码，这样developer的工作只需要维护状态机的文本描述，每必要冒引入bug的风险去维护code。<br><br>4） 使用宏定义描述状态机<br>一般来说，C++编程中应该避免使用#define，但是这主要是因为如果用宏来定义函数的话，很容易产生这样那样的问题，但是巧妙的使用,还是能够产生奇妙的效果。MFC就是使用宏定义来实现大的架构的。<br>在实现FSM的时候，可以把一些繁琐无比的if/else还有花括号的组合放在宏中，这样，在代码中可以3）中状态机描述文本一样写，通过编译器的预编译处理产生1）一样的效果，我见过产生C代码的宏，如果要产生C++代码，己软MFC可以，那么理论上也是可行的。<img src ="http://www.cppblog.com/richardhe/aggbug/53131.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-06-13 14:48 <a href="http://www.cppblog.com/richardhe/articles/53131.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用C++标准库中文件流对象，如何获得文件的大小</title><link>http://www.cppblog.com/richardhe/articles/52241.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Thu, 05 Jun 2008 06:17:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/52241.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/52241.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/52241.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/52241.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/52241.html</trackback:ping><description><![CDATA[C++标准库中的文件流类提供的各种操作中没有直接获得正在操作的文件的大小的函数。要获得文件大小得转个弯，用如下的方法<br>&nbsp;&nbsp;&nbsp; 假设我们有了一个已经打开的文件对象ifile。<br>&nbsp;&nbsp;&nbsp; 先将文件内的位置指针移到文件尾<br>&nbsp;&nbsp;&nbsp; ifile.seekg( 0, ios::end );<br>&nbsp;&nbsp;&nbsp; 再读取当前位置，这就是文件的大小了。<br>&nbsp;&nbsp;&nbsp; long filelength = ifile.tellg();<br>出自:http://www.cppblog.com/walkspeed/archive/2007/05/02/23336.html<br><img src ="http://www.cppblog.com/richardhe/aggbug/52241.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-06-05 14:17 <a href="http://www.cppblog.com/richardhe/articles/52241.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>接口设计的要点（接口不应被使用者直接销毁）</title><link>http://www.cppblog.com/richardhe/articles/52163.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 04 Jun 2008 07:39:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/52163.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/52163.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/52163.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/52163.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/52163.html</trackback:ping><description><![CDATA[<div class="postTitle">
<a  href="http://www.cppblog.com/walkspeed/archive/2007/08/07/29457.html" id="viewpost1_TitleUrl" class="postTitle2">接口设计的要点（接口不应被使用者直接销毁）</a>
</div>
接口在使用后，不需要也不允许用户销毁<br><br>接口用来通讯的。虽然在C++中，接口的一般用指针实现，但它代表的是一种通讯方式，而不是资源的<br>位置。请求者在使用完后不应去直接销毁这个指针，而应由接口的提供者去管理。<br><br>接口的提供者要管理接口关联的实体的生命周期，而且还有知道接口的使用情况才能管理接口。<br><br>接口于实体之间有关联，这表明实体要在接口前创建，要在接口后销毁。设计中接口提供者通过接口的<br>计数，来管理接口的状态。有一个请求者就将接口计数增加一，接口每被赋值一次接口计数也增加一次<br>。<br><br>接口的使用者在使用完后，要将接口返回给接口提供者，而不是自己直接销毁。因为接口不是被使用者<br>申请的资源，而是用户要求另一个对象通讯的通道。所以用完后要返还给接口提供者。接口提供者每接<br>到一个接口的返还，就减少这个接口的计数一次。<br><br>在要销毁接口关联的实体时，先要检查这个接口是否还有使用者，如果有，就要通知使用者，要求它们<br>返还接口。在接口没有使用者的情况下常能销毁实体（这样比较安全，当然也可以强行销毁）。<br><br>同一个接口可能对应多个实体，不过用户是感觉不到的。但是接口提供者一定要管理这种接口于不同的<br>实体之间的关联关系，保证对不同的实体销毁时，要收回与这个实体关联的所有接口。<img src ="http://www.cppblog.com/richardhe/aggbug/52163.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-06-04 15:39 <a href="http://www.cppblog.com/richardhe/articles/52163.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在C++中侦测内嵌类型的存在</title><link>http://www.cppblog.com/richardhe/articles/52160.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 04 Jun 2008 07:23:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/52160.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/52160.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/52160.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/52160.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/52160.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 现在学校里假如不止学生教师，还有工人，警卫等其它人员。如果他们不会在类内部typedef任何东西，则Register需要一种机制以确定T内部是否typedef了某个标识符（例如person_tag）。如果没有，就默认处理。如果有，则再进行更详细的分类。动机(Motivation)假设一所大学的注册系统提供了一个注册函数：&nbsp;template&lt;class T&gt;...&nbsp;&nbsp;<a href='http://www.cppblog.com/richardhe/articles/52160.html'>阅读全文</a><img src ="http://www.cppblog.com/richardhe/aggbug/52160.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-06-04 15:23 <a href="http://www.cppblog.com/richardhe/articles/52160.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>boost源码剖析之：泛型编程精灵type_traits</title><link>http://www.cppblog.com/richardhe/articles/52159.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 04 Jun 2008 07:22:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/52159.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/52159.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/52159.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/52159.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/52159.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 本文仅介绍一些技术性较强的traits。由于traits的定义往往重复代码较多，所以必要时本文仅剖析其底层机制。所有源码均摘自相应头文件中，为使源码简洁，所有的宏均已展开。由于traits技巧与编译平台息息相关，某些平台可能不支持模板偏特化。动机使用traits的动机一般有三种，分派、效率、使某些代码通过编译。&nbsp;分派下面有一个模板函数，假设一个动物收容组织提供了它，他...&nbsp;&nbsp;<a href='http://www.cppblog.com/richardhe/articles/52159.html'>阅读全文</a><img src ="http://www.cppblog.com/richardhe/aggbug/52159.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-06-04 15:22 <a href="http://www.cppblog.com/richardhe/articles/52159.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>boost源码剖析之：boost::multi_array</title><link>http://www.cppblog.com/richardhe/articles/52157.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 04 Jun 2008 07:21:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/52157.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/52157.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/52157.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/52157.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/52157.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 本文只是将multi_array最基本的功能代码做了一个扼要的分析，正如文章开始所说，multi_array还有许多很有用的特性，如果读者想充分了解multi_array的运作机制与实现技巧，就请深入完整地分析multi_array的代码吧，相信一定会大有收获的！动机&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C++是一门自由的语言，允许你自由的表达自己的意图，对...&nbsp;&nbsp;<a href='http://www.cppblog.com/richardhe/articles/52157.html'>阅读全文</a><img src ="http://www.cppblog.com/richardhe/aggbug/52157.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-06-04 15:21 <a href="http://www.cppblog.com/richardhe/articles/52157.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>boost源码剖析之：泛型指针类any之海纳百川</title><link>http://www.cppblog.com/richardhe/articles/52158.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 04 Jun 2008 07:21:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/52158.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/52158.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/52158.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/52158.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/52158.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 首先，any类里面一定要提供一个模板构造函数和模板operator=操作符。其次，数据的存放之所是个问题，显然你不能将它保存在any类中，那会导致any类成为模板类，后者是明确不被允许的。结论是：为容器准备一个非泛型的基类，而让指针指向该基类。动机C++是强类型语言，所有强类型语言对类型的要求都是苛刻的，类型一有不合编译器就会抱怨说不能将某某类型转换为某某类型，当然如果在类型之间提供了转换...&nbsp;&nbsp;<a href='http://www.cppblog.com/richardhe/articles/52158.html'>阅读全文</a><img src ="http://www.cppblog.com/richardhe/aggbug/52158.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-06-04 15:21 <a href="http://www.cppblog.com/richardhe/articles/52158.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>boost源码剖析之：Tuple Types</title><link>http://www.cppblog.com/richardhe/articles/52156.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 04 Jun 2008 07:20:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/52156.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/52156.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/52156.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/52156.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/52156.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 了解tuple的设计目标十分重要。下面两个细节设计目标才是真正需要和体现技术的地方，容我向你阐述它们：tuple中的数据成员的个数应该具有某种动态特性；tuple 必须提供某种途径以获取它内部保存的数值。动机[1]假设你有这样一个函数：它接受两个整型数据并返回它们整除的结果，像这样：&nbsp;int DevideInts(int n,int d){&nbsp;&nbs...&nbsp;&nbsp;<a href='http://www.cppblog.com/richardhe/articles/52156.html'>阅读全文</a><img src ="http://www.cppblog.com/richardhe/aggbug/52156.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-06-04 15:20 <a href="http://www.cppblog.com/richardhe/articles/52156.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>刘未鹏之《boost源码剖析》系列多重回调机制signal</title><link>http://www.cppblog.com/richardhe/articles/52155.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 04 Jun 2008 07:19:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/52155.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/52155.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/52155.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/52155.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/52155.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 上篇：架构篇引入所谓&#8220;事件&#8221;机制，简而言之，就是用户将自己的一个或多个回调函数挂钩到某个&#8220;事件&#8221;上，一旦&#8220;事件&#8221;被触发，所有挂钩的函数都被调用。&nbsp;毫无疑问，事件机制是个十分有用且常用的机制，不然C#也不会将它在语言层面实现了。&nbsp;但是C++语言并无此种机制。 &nbsp;幸运的...&nbsp;&nbsp;<a href='http://www.cppblog.com/richardhe/articles/52155.html'>阅读全文</a><img src ="http://www.cppblog.com/richardhe/aggbug/52155.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-06-04 15:19 <a href="http://www.cppblog.com/richardhe/articles/52155.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Boost.Bind的基础使用</title><link>http://www.cppblog.com/richardhe/articles/52150.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 04 Jun 2008 07:01:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/52150.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/52150.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/52150.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/52150.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/52150.html</trackback:ping><description><![CDATA[<div class="postTitle">
<a  href="http://www.cppblog.com/walkspeed/archive/2007/07/20/28448.html" id="viewpost1_TitleUrl" class="postTitle2">Boost.Bind的基础使用</a>
</div>
当我们使用函数时习惯于C函数的格式,即如下形式<br>resulttype funname( arglist );<br>返回值类型 函数名( 参数列表 );<br><br>在Boost.Function中，我们可以方便的定义定义函数对象。不过在定义用来表示类成员函数的函数对象时<br>第一个参数是类指针。而且在调用时，要传入一个类实例的指针。这样用起来并不是很方便，因为调用者<br>要知道类实例。这实际上没有实现解耦。而解耦是我们使用回调或委托设计的一个目标。<br><br>为了解决这个问题，我们要使用Boost.Bind库<br><br>Boost.Bind是一个函数对象工厂。他用来产生我们需要的函数对象。好了，有了它，你可以在你设计中大<br>量使用Boost.Function。不用再去定义类成员函数形式的函数对象啦，只用定义普通函数对象。<br><br>一个简单的例子<br><br>class CExample<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; bool printstr( const std::string &amp;str )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "CExample::printstr" &lt;&lt; str &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>//定义一个函数对象<br>boost::function&lt; bool ( const std::string&amp; ) &gt; printstr;<br><br>//用Boost.Bind创建一个函数对象，赋给printstr<br>CExample example;<br>printstr = boost::bind( &amp;CExample::printstr, &amp;example, _1 );<br><br>好了，我们创建了一个函数对象，而且调用时不再需要类实例拉。用Boost.Function和Boost.Bind大大<br>的简化了Command模式的实现。<br><br>在上面的例子中要个古怪的对象"_1"。这个叫做站位符，他代表这个位置有个参数，但现在还不知道参<br>数是什么。_1代表参数列表中的第一个位置上的参数。Boost.Bind一共定义了9个站位符对象。如下<br>_1,_2,_3,_4,_5,_6,_7,_8,_9。分别代表参数列表中位子。<br><br>Boost.Bind产生的函数对象可以直接使用，利用上面的例子。<br><br>bool b = boost::bind( &amp;CExample::printstr, &amp;example, _1 )( "Hello World" );<img src ="http://www.cppblog.com/richardhe/aggbug/52150.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-06-04 15:01 <a href="http://www.cppblog.com/richardhe/articles/52150.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Boost源码剖析：C++泛型函数指针类</title><link>http://www.cppblog.com/richardhe/articles/52148.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 04 Jun 2008 06:50:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/52148.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/52148.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/52148.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/52148.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/52148.html</trackback:ping><description><![CDATA[<strong>前奏 </strong><br><br>　　如你所知，Boost库是个特性完备，且具备工业强度的库，众多C++权威的参与使其达到了登峰造极的程度。尤其<a  href="http://www.yesky.com/key/171/85171.html" class="bluekey" target="_blank">泛型</a>的强大威力在其中被发挥得淋漓尽致，令人瞠目结舌。<br><br>　　然而弱水三千，我们只取一瓢饮。下面，我试图从最单纯的世界开始，一步一步带领你进入<a  href="http://www.yesky.com/key/747/45747.html" class="bluekey" target="_blank">源码</a>的世界，去探究boost::function(下文简称function)内部的精微结构。<br><br>　　通常 ，在单纯的情况下，对函数的<a  href="http://www.yesky.com/key/1698/86698.html" class="bluekey" target="_blank">调用</a>简单而且直观，像这样：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>int fun(int someVal);<br><br>int main(){<br>　fun(10);<br>}</td>
        </tr>
    </tbody>
</table>
<br>　　然而你可能需要在某个时刻将<a  href="http://www.yesky.com/key/2595/17595.html" class="bluekey" target="_blank">函数指针</a>保存下来，并在以后的另一个时刻调用它，像这样：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>int fun(int);<br><a  href="http://www.yesky.com/key/1664/6664.html" class="bluekey" target="_blank">typedef</a> int (*func_handle)(int);<br><br>int main(){<br>　func_handle fh=fun;<br>　... //do something<br>　fh(10);<br>}</td>
        </tr>
    </tbody>
</table>
<br>　　但是，如果fun形式为<a  href="http://www.yesky.com/key/3728/98728.html" class="bluekey" target="_blank">void</a> fun(int)呢？如你所见，fun可能有无数种形式，如果对fun的每一个形式都typedef一个对应的func_handle，则程序员会焦头烂额，不胜其扰，代码也可能变得臃肿和丑陋不堪，甚至如果fun是仿函数呢？<br><br>　　幸运的是C++泛型可以使代码变得<a  href="http://www.yesky.com/key/984/165984.html" class="bluekey" target="_blank">优雅</a>精致，面对无数种的可能，泛型是最好的选择。 因此，你只是需要一个能够保存函数<a  href="http://www.yesky.com/key/2157/22157.html" class="bluekey" target="_blank">指针</a>的泛型<a  href="http://www.yesky.com/key/1023/71023.html" class="bluekey" target="_blank">模板类</a>(对应于Command模式)，因为泛型<a  href="http://www.yesky.com/key/1967/91967.html" class="bluekey" target="_blank">编程</a>有一个先天性的优势——可以借助<a  href="http://www.yesky.com/key/1922/91922.html" class="bluekey" target="_blank">编译器</a>的力量在编译期根据用户提供的型别信息化身千万(具现化)，所以一个泛型的类可以有无限个具现体，也就是说可以保存无限多种可能型别的函数或类似函数的东西(如，仿函数)。这个类(在Boost库中的类名为function)与函数指针相比应该有以下一些优势：<br><br>　　&#168; 同一个function对象应能够接受与它形式兼容的所有函数和仿函数,例如：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>int f1(int); //这是个函数，形式为 int(int)<br>short f2(<a  href="http://www.yesky.com/key/3008/213008.html" class="bluekey" target="_blank">double</a>); //这个函数形式为 short(double)<br><br><a  href="http://www.yesky.com/key/2457/102457.html" class="bluekey" target="_blank">struct</a> functor //这是个仿函数类，形式为int(int)<br>{<br>　int <a  href="http://www.yesky.com/key/4821/109821.html" class="bluekey" target="_blank">operator</a>()(int){}<br>};<br><br>functor f3; //创建仿函数对象<br><br>boost::function＜int(int)＞ func; // int(int)型的函数或仿函数<br>func = f1; //接受f1<br>func(10); //调用f1(10)<br>func = f2; //也能接受short(double)型的f2<br>func(10); //调用f2(10)<br>func = f3; //也能接受仿函数f3<br>func(10); //调用f3(10)</td>
        </tr>
    </tbody>
</table>
<br>　　&#168; function应能够和参数<a  href="http://www.yesky.com/key/2899/92899.html" class="bluekey" target="_blank">绑定</a>以及其它function-construction库<a  href="http://www.yesky.com/key/603/280603.html" class="bluekey" target="_blank">协同工作</a>。例如，function应该也能够接受std::bind1st返回的仿函数。这一点其实由第一点已经有所保证。<br><br>　　&#168; 当接受的一个空的仿函数对象被调用的时候function应该有可预期的行为。<br><br>　　显然，第一点是我们的重点，所谓形式兼容，就是说，对于：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>R1 (T0,<a  href="http://www.yesky.com/key/2069/102069.html" class="bluekey" target="_blank">T1</a>,T2,...,TN) =＞ FunctionType1<br><a  href="http://www.yesky.com/key/2934/182934.html" class="bluekey" target="_blank">R2</a> (P0,P1,<a  href="http://www.yesky.com/key/4629/109629.html" class="bluekey" target="_blank">P2</a>,...,PN) =＞ FunctionType2</td>
        </tr>
    </tbody>
</table>
<br>　　两种类型的函数（广义），只要满足：<br><br>　　1. R2能够隐式转换为R1<br><br>　　2. 所有Ti都能够隐式转换为<a  href="http://www.yesky.com/key/3065/108065.html" class="bluekey" target="_blank">Pi</a> (i取0,1,2,...)<br><br>
那么就说，boost::function＜FunctionType1＞可以接受FunctionType2类型的函数(注意，反之不行)。支持这一
论断的理由是，只要Ti能够隐式转型为Pi，那么参数被转发给真实的函数调用就是安全的，并且如果R2能够隐式转型为R1，那么返回真实函数调用所返回的
值就是安全的。这里安全的含义是，C++类型系统认为隐式转换不会丢失信息，或者会给出编译警告，但能够通过编译。<br><br>　　后面你会看到，boost::function通过所谓的invoker非常巧妙地实现了这点，并且阻止了被形式不兼容的函数赋值的操作。<br><strong>探险</strong><br><br>　　好吧，准备好，我们要出发了，进行深入源码世界的探险。<br><br>　　先看一个function的最简单的使用：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>int g(int); //为了让代码简单，假设g有定义，以后的代码都会如此<br>function＜int(int)＞ f(g);<br>f(0);</td>
        </tr>
    </tbody>
</table>
<br>　　<strong>间奏——R(T1,T2,...)函数类型</strong><br><br>　　虽然这个间奏未免早了点儿，但是为了让你以后不会带着迷惑，这显然是必要的。请保持耐心。<br><br>
或许你会对模板参数int(int)感到陌生，其实它是个函数型别——函数g的确切型别就是int(int)，而我们通常所看到的函数指针型别int
(*)(int)则是&amp;g的型别。它们的区别与联系在于：当把g作为一个值进行拷贝的时候（例如，按值传参），其类型就会由int(int)退化
为int(*)(int)，即从函数类型退化为函数指针类型——因为从语义上说，函数不能被&#8220;按值拷贝&#8221;，但身为函数指针的地址值则是可以被拷贝的。另一
方面，如果g被绑定到引用，则其类型不会退化，仍保持函数类型。例如：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>template＜class T＞<br><br>void test_func_type(T ft) //按值传递，类型退化<br>{<br>　static_cast＜int＞(ft); //引发编译错误，从而看出ft的类型为退化后的函数指针<br>}<br><br>int g(int); //函数g，名字g的类型为int(int)<br>test_func_type(g); //注意，并非&amp;g，参数g的类型将会退化为函数指针类型<br>int (&amp;ref_f)(int) = g; //注意，并非&#8220;= &amp;g&#8221;，因为绑定到引用，类型并不退化</td>
        </tr>
    </tbody>
</table>
<br>
当然，这样的代码不能通过编译，因为static_cast＜＞显然不会让一个函数指针转换为int，然而我们就是要它通不过编译，这样我们才能窥视到
按值传递的参数ft的类型到底是什么，从编译错误中我们看出，ft的类型是int(*)(int)，也就是说，在按值传递的过程中，g的类型退化为函数指
针类型，变得和&amp;g的类型一样了。而ref_t的类型则是引用，引用绑定则没有引起类型退化。<br><br>　　请注意，函数类型乃是个极其特殊的类型，在大多数时候它都会退化为函数指针类型，以便满足拷贝语义，只有面对引用绑定的时候，能够维持原来的类型。当然，对于boost::function，总是按值拷贝。<br><br>　　<strong>继续旅程</strong><br><br>　　好吧，回过神来，我们还有更多地带要去探究。<br><br>　　function＜int(int)＞实际上进行了模板偏特化，Boost库给function的类声明为：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>template＜typename Signature, //函数类型<br>typename Allocator = ...<br>＞ //Allocator并非重点，故不作介绍<br><br>class function;</td>
        </tr>
    </tbody>
</table>
<br>　　事实上function类只是个薄薄的外覆（wrapper），真正起作用的是偏特化版本。<br><br>
对于function＜R(T0)＞形式，偏特化版本的function源码像这样(实际上在boost源代码中你看不到模板参数T0的声明，也看不到
function1，它们被宏替换掉了，那些精巧的宏是为了减小可见的代码量，至于它们的细节则又是一个世界，以下代码可看作对将那些令人眼花缭乱的宏展
开后所得到的代码，具有更好的可读性)：<br><br>　　摘自：&#8221;boost/function/function_template.hpp&#8221;<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>template＜typename R,typename T0,typename Allocator＞<br>class function＜R(T0),Allocator＞ //对R(T0)函数类型的偏特化版本<br><br>:public function1＜R,T0,Allocator＞ //为R(T0)形式的函数准备的基类，在下面讨论<br>{<br>　typedef function1＜R,T0,Allocator＞ base_type;<br>　typedef function selftype;<br>　struct clear_type{}; //马上你会看到这个蹊跷的类型定义的作用<br>　public:<br>　　function() : base_type() {} //默认构造<br>　　template＜typename Functor＞ //模板化的构造函数，为了能够接受形式兼容的仿函数对象<br>　　function(Functor f, typename enable_if＜<br>　　　　(ice_not＜(is_same＜Functor, int＞::value)＞::value),<br>　　　　int<br>　　＞::type = 0) ：base_type(f){} <br><br>　function(clear_type*) : base_type() {} //这个构造函数的作用在下面解释<br>　self_type&amp; operator=(const self_type&amp; f) //同类型function对象之间应该能够赋值<br>　{<br>　　self_type(f).swap(*this); //swap技巧，细节见《Effective STL》<br>　　return *this;<br>　}<br>　...<br>};<br><br>enable_if<br></td>
        </tr>
    </tbody>
</table>
<br>　　你一定对模板构造函数中出现的那个冗长的enable_if＜...＞的作用心存疑惑，其实它的作用说穿了很简单，就是：当用户构造：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>function＜int(int)＞ f(0);</td>
        </tr>
    </tbody>
</table>
<br>　　的时候，将该（带有enable_if的）构造函数从重载决议的候选集中踢掉。使重载决议的结果为选中第三个构造函数：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>function(clear_type*):base_type(){}</td>
        </tr>
    </tbody>
</table>
<br>　　从
而进行缺省构造。
而说得冗长一点就是：当f的类型——Functor——不是int时，该构造函数就是&#8220;有效（enable）&#8221;的，会被重载决议选中。但如果用户提供了一
个0，用意是构造一个空(null)的函数指针，那么该函数就会由于&#8220;SFINAE&#8221;原则而被从重载决议的候选函数中踢掉。为什么要这样呢？因为该构造函
数负责把确切的f保存起来，它假定f并非0。那应该选择谁呢？第三个构造函数！其参数类型是clear_type*，当然，0可以被赋给任何指针，所以它
被选出，执行缺省的构造行为。<br><strong>基类 functionN</strong><br><br>　　function的骨架就这些。也许你会问，function作为一个仿函数类，怎么没有重
载operator()——这可是身为仿函数的标志啊！别急，function把这些烦人的任务都丢给了它的基类functionN，根据情况不同，N可
能为0，1，2...，说具体一点就是：根据用户使用function时给出的函数类型，function将会继承自不同的基类——如果用户给出的函数类
型为&#8220;R()&#8221;形式的，即仅有一个参数，则function继承自function0，而对于R(T0)形式的函数类型，则继承自function1，依
此类推。前面说过，function只是一层外覆，而所有的秘密都在其基类functionN中！<br><br>　　不知道你有没有发现，function的骨架中也几乎没有用到函数类型的信息，事实上，它也将这些信息一股脑儿抛给了基类。在这过程中，混沌一团的int(int)型别被拆解为两个单独的模板参数传给基类:<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>template＜typename R,typename T0,typename Allocator＞<br>class function＜R(T0),Allocator＞ //R(T0)整个为一型别<br>:public function1＜R,T0,Allocator＞ //拆解为两个模板参数R,T0传给基类</td>
        </tr>
    </tbody>
</table>
<br>　　好了，下面我们深入基类function1。真正丰富的宝藏在里面。<br><br>　　function1<br><br>　　function1的源代码像这样(与上面一样，事实上有些代码你是看不到的，为了不让你迷惑，我给出的是将宏展开后得到的代码):<br><br>　　摘自：&#8221;boost/function/function_template.hpp&#8221;<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>template＜typename R,typename T0,class Allocator = ...＞<br><br>class function1:public function_base //function_base负责管理内存<br>{<br>　...<br>　public:<br>　　typedef R result_type; //返回类型<br>　　typedef function1 self_type;<br>　　function1() : function_base(),invoker(0){}//默认构造<br>　　template＜typename Functor＞<br>　　function1(Functor const &amp; f, //模板构造函数<br>　　　typename enable_if＜...＞::type = 0) : <br>　　　function_base(),<br>　　　invoker(0)<br>　　　{<br>　　　　this-＞assign_to(f); //这儿真正进行赋值，assign_to的代码在下面列出<br>　　　}<br><br>　　function1(clear_type*) : function_base(), invoker(0){} //该构造函数上面解释过<br>　　function1(const function&amp; f) : //拷贝构造函数<br>　　function_base(),<br>　　invoker(0){<br>　　　this-＞assign_to_own(f); //专用于在function之间赋值的assignment<br>　　}<br><br>　　result_type operator()(T0 a0) const //身为仿函数的标志！<br>　　{<br>　　　//下面负责调用指向的函数<br><br>　　　if (this-＞empty())<br>　　　　boost::throw_exception(bad_function_call());<br>　　　　//这里进行真正的函数调用，使用invoker<br><br>　　　　internal_result_type result = invoker(function_base::functor,a0); <br>　　　　return static_cast＜result_type＞(result);<br>　　}<br><br>　　template＜typename Functor＞<br>　　void assign_to(Functor f) //所有的构造函数都调用它！具有多个重载版本。<br>　　{<br>　　　//以一个get_function_tag萃取出Functor的类别(category)!<br>　　　typedef typename detail::function::get_function_tag＜Functor＞::type tag;<br>　　　this-＞assign_to(f, tag());//根据不同类别的Functor采取不同的assign策略！<br>　　}</td>
        </tr>
    </tbody>
</table>
<br>　　get_function_tag＜＞能萃取出Functor的类别(category)，有下面几种类别<br><br>　　struct function_ptr_tag {}; //函数指针类别<br><br>　　struct function_obj_tag {}; //仿函数对象类别<br><br>　　struct member_ptr_tag {}; //成员函数类别<br><br>　　struct function_obj_ref_tag {};//以ref(obj)加以封装的类别，具有引用语义<br><br>　　struct stateless_function_obj_tag {}; //无状态函数对象<br><br>　　满足以下所有条件：<br><br>　　has_trivial_constructor<br><br>　　has_trivial_copy<br><br>　　has_trivial_destructor<br><br>　　is_empty<br><br>　　的仿函数对象称为stateless的<br><br>　　而对于不同的函数类别，assign_to有各个不同的重载版本，如下：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>template＜typename FunctionPtr＞ //如果是函数指针就调用这个版本<br><br>void assign_to(FunctionPtr f, function_ptr_tag) //这个版本针对函数指针<br>{ <br>　clear();<br>　if (f){<br>　　typedef typename detail::function::get_function_invoker1＜FunctionPtr,R,T0＞::type invoker_type;<br>　　invoker = &amp;invoker_type::invoke; //invoke是static成员函数<br><br>　　function_base::manager = //管理策略<br>　　　　 &amp;detail::function::functor_manager＜FunctionPtr, Allocator＞::manage;<br><br>　　function_base::functor = //交给function的函数指针或仿函数对象指针最终在这儿保存<br>　　　function_base::manager(<br>　　　　detail::function::make_any_pointer((void (*)())(f)),<br>　　　　detail::function::clone_functor_tag);//实际上拷贝了一份函数指针<br>　}<br>}<br><br>...<br><br>typedef internal_result_type (*invoker_type)(detail::function::any_pointer,T0);<br>invoker_type invoker; //重要成员，负责调用函数！<br><br>};</td>
        </tr>
    </tbody>
</table>
<br>　　你可能已经被这段&#8220;夹叙夹议&#8221;的代码弄得头昏脑涨了，但这才刚刚开始！<br><br>　　<strong>function的底层存储机制</strong><br><br>　　请将目光转向上面的代码段末尾的assign_to函数中，其中有两行深色的代码，分别对function_base里的manager和functor成员赋值。这两行代码肩负了保存各种函数指针的任务。<br><br>
manager是一个函数指针，它所指向的函数代表管理策略，例如，对于函数指针，仅仅作一次赋值，就保存完毕了，但是对于仿函数，得额外分配一次内
存，然后将仿函数拷贝到分配的内存中，这才完成了保存的任务。这些策略根据函数的类别而定，上面代码中的assign_to函数是针对函数指针类别的重载
版本，所以manager的策略是不作任何内存分配，直接返回被转型为&#8220;void(*)()&#8221;（利于在底层以统一的形式保存）的函数指针就行了，这从代码
中可以看出。<br><br>　　需要说明的是，对于函数指针，function_base并不知道也不关心它要保存的函数指针是什么确切的类型，只要是
函数指针就行，因为它总会把该函数指针f转型为&#8220;void (*)()&#8221;类型，然后保存在functor成员中，functor成员是一个union:<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>union any_pointer<br>{<br>void* obj_ptr; //任意仿函数对象指针都可以用static_cast＜＞转型为void*型<br>const void* const_obj_ptr; //为const仿函数准备的<br>void (*func_ptr)(); //任意函数指针都可以用reinterpret_cast＜＞转型为void(*)()型<br>char data[1];<br>};</td>
        </tr>
    </tbody>
</table>
<br>　　这个any_pointer可以通过安全转型保存所有形式的仿函数和函数指针，承载在底层保存数据的任务<br><br>　　<strong>function的调用机制——invoker</strong><br><br>　　我们把目光转到function1的定义的最底部，那儿定义了它最重要的成员invoker，它是一个函数指针，所指向的函数就是function的调用机制所在，invoker的类型为：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>typedef internal_result_type (*invoker_type)(any_pointer,T0);</td>
        </tr>
    </tbody>
</table>
<br>
前面已经说过，any_pointer是个union，可以保存任何类型的函数指针或函数对象，里面保存的是用户注册的函数或仿函数，T0为调用
any_pointer中的函数的参数的型别（对于不同情况，可能会有T1,T2等）。这也就是说，invoker负责调用保存在any_pointer
中的用户提供的函数或仿函数。<br><br>　　那么，invoker这个函数指针到底指向什么函数呢——也就是说，在什么时候invoker被赋值了呢？我们再次把目光转向assign_to函数，其中有一行对invoker成员赋值的语句，从这行语句出发我们可以揭露invoker的全部奥秘：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>invoker = &amp;invoker_type::invoke; //invoke是static成员函数</td>
        </tr>
    </tbody>
</table>
<br>
请不要把这个invoker_type和上面那个函数指针型别invoker_type混淆起来，这个invoker_type是位于
assign_to函数中的一个局部的typedef，所以隐藏了后者（即类作用域中的那个invoker_type——invoker成员的类型）。往
上一行，你就看到这个局部型别invoker_type的定义了：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>typedef typename get_function_invoker1＜<br><br>FunctionPtr,R,T0＞::type invoker_type;</td>
        </tr>
    </tbody>
</table>
<br>　　get_function_invoker1又是何物？很显然，这是个traits，其内嵌的::type会根据不同的模板参数表现为不同的类型，在本例中，::type的类型将会被推导为<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>function_invoker1＜int(*)(int),int,int＞</td>
        </tr>
    </tbody>
</table>
<br>　　而function_invoker1是个类模板，其定义为：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>template＜typename FunctionPtr,<br>typename R，typename T0＞ //注意这里的模板参数，后面会解释<br><br>struct function_invoker1<br>{<br>　static R invoke(any_pointer function_ptr,T0 a0)<br>　{<br>　　FunctionPtr f = reinterpret_cast＜FunctionPtr＞(function_ptr.func_ptr);<br>　　return f(a0);<br>　}<br>};</td>
        </tr>
    </tbody>
</table>
<br>　　所以对invoker的赋值最终相当于：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>invoker=&amp;function_invoker1＜int(*)(int),int,int＞::invoke;</td>
        </tr>
    </tbody>
</table>
<br>　　而function_invoker1＜int(*)(int),int,int＞::invoke是静态成员函数，它被实例化后相当于：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>static int invoke(any_pointer function_ptr,int a0)<br>{<br>　//先转型，再调用，注意，这一行语句还有一个额外的作用，在后面解释<br>　int (*f)(int) = reinterpret_cast＜int(*)(int)＞(function_ptr.func_ptr);<br><br>　//因为f指向的是用户保存在该function中的函数或仿函数，所以这一行语句进行了真实的调用！<br>　return f(a0);<br>}</td>
        </tr>
    </tbody>
</table>
<br>　　我们可以看出，在invoke函数中，真正的调用现身了。<br><br>　　如果接受的是仿函数，则有function_obj_invoker1与它对应，后者也是一个类似的模板，它的invoke静态成员函数的形式也是：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>static R invoke(any_pointer function_obj_ptr,T0 a0);</td>
        </tr>
    </tbody>
</table>
<br>　　其中function_obj_ptr是指向仿函数的指针，所以其invoke静态成员函数中对它的调用语句是这样的：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>FunctionObj* f = (FunctionObj*)(function_obj_ptr.obj_ptr);<br><br>return (*f)(a0); //调用用户的仿函数</td>
        </tr>
    </tbody>
</table>
<br>
最后一种可能：如果接受的是成员函数怎么办呢？简单的答案是：boost::function并没有为成员函数作任何特殊准备！理由也很简单，
boost::function只要先将成员函数封装为仿函数，然后将其作为一般的仿函数对待就行了，具体代码就不列了，STL中有一个函数模板
std::mem_fun就是用于封装成员函数指针的，它返回的是一个仿函数。boost中也对该函数模板做了扩充，使它可以接受任意多个参数的成员函
数。<br><strong>做一个，送一个——invoker的额外好处</strong><br><br>　　我们注意到function的构造和赋值函数及其基类的构造和赋值函数都
是模板函数，这是因为用户可能提供函数也可能提供函数模板，但最关键的还是，functiont提供一种能力：对于function＜double
(int)＞类型的泛型函数指针，用户可以给它一个int(int)类型的函数——是的，这是可行且安全的，因为其返回值类型int可以安全的转型为
double，而对于这种类型兼容性的检查就在上面分析的invoke静态成员函数中，这就是我们要说的额外好处——如果类型兼容，那么invoke函数
就能正常编译通过，但如果用户给出类型不兼容的函数，就会得到一个错误，这个错误是在编译器实例化invoke函数代码的时候给出的，例如，用户如果这样
写：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>RT1 f(P1,P2); // RT1(P1,P2)函数类型，这里的RT1,P1,P2假定已经定义，这是一般化的符号<br><br>function＜RT(P)＞ f_ptr; //RT(P)函数类型，同样假定RT,P已定义<br><br>f_ptr = &amp;f; //类型不兼容，错误！</td>
        </tr>
    </tbody>
</table>
<br>　　这就会导致编译错误，错误发生在invoke静态成员函数中。下面我就为你解释为什么。<br><br>　　我想你对function_invoker1考的三个模板参数仍然心存疑惑，我们再一次来回顾一下其声明：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>template＜typename FunctionPtr,typename R，typename T0＞ <br><br>struct function_invoker1</td>
        </tr>
    </tbody>
</table>
<br>　　我们还得把目光投向assign_to模板函数，其中使用function_invoker1的时候是这样的：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>typedef typename detail::function::get_function_invoker1＜<br><br>FunctionPtr,R,T0＞::type invoker_type;</td>
        </tr>
    </tbody>
</table>
<br>　　这里，给出的FunctionPtr,R,T0三个模板参数将会原封不动的传给function_invoker1，那么对于我们上面的错误示例，这三个模板参数各是什么呢？<br><br>　　首先，我们很容易看出，FunctionPtr就是assign_to模板函数的模板参数，也就是用户传递的函数或仿函数的类型，在我们的错误示例中，函数f的类型为RT1(P1,P2)，所以<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>FunctionPtr = RT1(*)(P1,P2)</td>
        </tr>
    </tbody>
</table>
<br>　　而R,T0则是用户在实例化function模板时给出的模板参数，我们写的是function＜RT(P)＞，于是:<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>R = RT<br><br>T0 = P</td>
        </tr>
    </tbody>
</table>
<br>　　所以，对于我们的错误示例，invoker_type的类型为：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>function_invoker1＜ RT1(*)(P1,P2),RT,P＞</td>
        </tr>
    </tbody>
</table>
<br>　　对于这样一个function_invoker1，其内部的invoke静态成员函数被实例化为：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>static RT invoke(any_pointer function_ptr,P a0)<br>{<br>　RT1 (*f)(P1,P2)= //FunctorPtr f =reinterpret_cast＜RT1(*)(P1,P2)＞(function_ptr.func_ptr);<br>　return f(a0); //错啦！瞧瞧f的型别，f接受一个P类型的参数吗？编译器在此打住。<br>　//这行语句的另一个隐含的检查是返回值类型匹配，f(...)返回RT1，而invoke须得返回RT<br>}</td>
        </tr>
    </tbody>
</table>
<br>　　看看最后一行语句，所有的检查都在那里了——我们最终把检查&#8220;委托&#8221;给了C++底层的类型系统。<br><br>　　很精妙不是吗？虽然在模板形式的assign_to函数中，看起来我们并不关心到底用户给的参数是何类型，看起来用户可以把任何函数或仿函数塞过来，但是一旦下面触及invoker的赋值，就得实例化invoke静态成员函数，其中的：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>return f(a0);</td>
        </tr>
    </tbody>
</table>
<br>　　一下就把问题暴露出来了！这种把类型检查延迟到最
后，不得不进行的时候，由C++底层的类型系统来负责检查的手法的确很奇妙——看起来我们没有在assign_to函数中及时利用类型信息进行类型检查，
但是我们却并没有丧失任何类型安全性，一切最终都逃不过C++底层的类型系统的考验！<br><br>　<strong>　function如何对待成员函数</strong><br><br>　　对于成员函数，assign_to的重载版本只有一行：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>this-＞assign_to(mem_fn(f));</td>
        </tr>
    </tbody>
</table>
<br>　　mem_fun(f)返回一个仿函数，它封装了成员函数f，之后一切皆与仿函数无异。<br><br>　　关于mem_fun的细节，这里就不多说了，大家可以参考STL中的实现，相信很容易看懂，这里只简单的提醒一下，成员函数封装的效果是这样的：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>R (C::*)(T0,T1,...) --＞ R (*)(C*,T0,T1,...) 或 R (*)(C&amp;,T0,T1,...)</td>
        </tr>
    </tbody>
</table>
<br>　　<strong>safe_bool惯用手法</strong><br><br>　　如你所知，对于函数指针fptr，我们可以这样测试它：if(fptr) ...，所以function也应该提供这一特性，然而如果直接重载operator 　bool()则会导致下面的代码成为合法的:<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>function＜int(int)＞ f;<br><br>bool b=f;</td>
        </tr>
    </tbody>
</table>
<br>　　这显然不妥，所以function用另一个巧妙的手法替代它，既所谓的safe_bool惯用手法，这在function定义内部的源码如下：<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>struct dummy { void nonnull(){};};<br><br>typedef void (dummy::*safe_bool)(); //确保safebool不能转型为任何其它类型！<br><br>operator safe_bool () const <br><br>{ return (this-＞empty())? 0 : &amp;dummy::nonnull; }</td>
        </tr>
    </tbody>
</table>
<br>
这样，当你写if(f)的时候，编译器会找到operator
safe_bool()，从而将f转型为safe_bool，这是个指针类型，if语句会正确判定它是否为空。而当你试图把f赋给其它类型的变量的时候则
会遭到编译期的拒绝——因为safe_bool无法向其它类型转换。<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>get_function_tag＜＞</td>
        </tr>
    </tbody>
</table>
<br>　　get_function_tag＜＞用于萃取出函数所属类别(category)，各个类别在源代码中已经列出，至于它到底是如何萃取的，这与本文关系不是很　大，有一点需要提醒一下：函数指针类型也是指针类型，这听起来完全是句废话，但是考虑这样的代码： <br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>template＜typename T＞ struct is_pointer{enum{value=0};};<br><br>template＜typename T＞ struct is_pointer＜T*＞{enum{value=1};};<br><br>std::cout＜＜is_pointer＜int(*)(int)＞::value; //这将输出 1</td>
        </tr>
    </tbody>
</table>
<br>　　也就是说int(*)(int)可以与T*形式匹配，匹配时T为int(int)。<br><br>　　<strong>最后一些细节</strong><br><br>　　1. 我没有给出function_base的源代码，实际上那很简单，它最主要的成员就是union any_pointer型的数据成员<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>detail::function::any_pointer functor; //用于统一保存函数指针及仿函数对象指针</td>
        </tr>
    </tbody>
</table>
<br>　　2. 我没有给出functor_manager的信息，实际上它与function的实现没有太大关系，它负责copy和delete函数对象，如果必要的话。所以我将它略去，它的源码在：&#8221;boost/function/function_base.hpp&#8221;里。<br><br>　　3. 我给出的源代码是将宏展开后的版本，实际的代码中充斥着让人眼花缭乱的宏，关于那些宏则又是一个奇妙的世界。Boost库通过那些宏省去了许多可见代码量。随着函数参数的不同，那些宏会扩展出function2,function3...各个版本。<br><br>　　本文只研究了int(int)型的情况，其它只是参数数目的改变而已。经过宏的扩展，function的偏特化版本将有:<br><br>
<table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
    <tbody>
        <tr>
            <td>template＜typename R,typename Allocator＞<br><br>class function＜R(),Allocator＞:public function0＜R,Allocator＞<br><br>{...};<br><br>template＜typename R,typename T0,typename Allocator＞<br><br>class function＜R(T0),Allocator＞:public function1＜R,T0,Allocator＞<br><br>{...};<br><br>template＜typename R,typename T0,typename T1,typename Allocator＞<br><br>class function＜R(T0,T1),Allocator＞:public function2＜R,T0,T1,Allocator＞<br><br>{...};</td>
        </tr>
    </tbody>
</table>
<br><br>
等更多版本，一共有BOOST_FUNCTION_MAX_ARGS+1个版本，BOOST_FUNCTION_MAX_ARGS为一个宏，表示最多能
够接受有多少个参数的函数及仿函数对象，你可以重新定义这个宏为一个新值，以控制function所能支持的函数参数个数的最大值。其中的
function0,function1,function2等名字也由宏扩展出。<br>&nbsp;&nbsp;阅读关于 <a  href="http://search.yesky.com/search?q=C++" target="_blank"><font color="red">C++</font></a> 的全部文章<br><img src ="http://www.cppblog.com/richardhe/aggbug/52148.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-06-04 14:50 <a href="http://www.cppblog.com/richardhe/articles/52148.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Boost.Function的基本使用</title><link>http://www.cppblog.com/richardhe/articles/52147.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 04 Jun 2008 06:45:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/52147.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/52147.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/52147.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/52147.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/52147.html</trackback:ping><description><![CDATA[<div class="postTitle">
<a  href="http://www.cppblog.com/walkspeed/archive/2007/07/18/28272.html" id="viewpost1_TitleUrl" class="postTitle2">Boost.Function的基本使用</a>
</div>
Boost.Function库用来提供一个对象化的函数指针。<br><br>函数指针对设计很有用。它使调用者可以延期调用，调用时机由调用者确定。而且可以改变<br>响应者，以应对不同的要求。<br><br>C中的函数指针只能用于自由函数。在C++中除了自由函数还有函数对象和类成员函数，这些<br>C的函数指针是无法用的。这要求能适应C++语言的函数指针。既然C++语言本身没有提供，<br>那就提供一个库。stl提供了，但是定义了很多类型，使用起来并不是很方便，而且函数参数<br>的个数被限定在两个以下，更能是备受限制。Boost.Function库提供了一个好的解决方案。<br><br>Boost.Function库可以支持自由函数，函数对象，类成员函数。而且参数个数多达10个。<br>Boost.Function库利用模板技术来实现。生成的代码有很高的运行效率。本库可以不用编译<br>直接使用。<br><br>Boost.Function的头文件。<br>function.hpp<br><br>定义一个Boost.Function的对象（是一个返回值类型为int，第一个参数是std::string类型<br>第二个参数是float类新）<br><br>boost::function&lt; int ( std::string, float ) &gt; funptr;<br><br>上面这个定义方式是一种容易理解的定义方式。但有些编译器不支持，如果想更多的编译器<br>支持，则用下面这种定义方式<br><br>boost::function2&lt; int, std::string, float &gt; funptr;<br><br>注意模板中有3个类型，而function类却是boost::function2。应为返回值类型不计算在参数<br>类型中（原因很简单，C++的编译器不会根据返回类型不同来区分函数定义的不同）。<br><br>int freefun( std::string str, float f )<br>{<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; str &lt;&lt; " : " &lt;&lt; f &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; return 0;<br>}<br><br>class CFun<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; int operator() ( std::string str, float f )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; str &lt;&lt; " : " &lt;&lt; f &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;&nbsp; <br>&nbsp;&nbsp;&nbsp; }<br>}<br><br>上面定义了一个自由函数和一个函数对象。下面将把他们付给function对象。<br><br>赋值为自由函数<br>funptr = &amp;freefun;<br><br>赋值为函数对象<br>CFun fun;<br>funptr = fun;<br><br>以上两种情况的调用方法一致，如下<br>funptr( "float =", 10.0 );<br><br>Boost.Function对象要能指向类型原函数，其定义要如下<br><br>class FreeClass<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; int out( std::string str, float f )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; str &lt;&lt; " : " &lt;&lt; f &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;&nbsp; <br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>boost::function&lt; int ( FreeClass*, std::string, float ) &gt; funptr;<br><br>跨平台的定义方法<br>boost::function3&lt; int, FreeClass*, std::string, float &gt; funptr;<br><br>赋值方法<br>funptr = &amp;FreeClass::out;<br><br>调用方法<br>FreeClass fc;<br><br>funptr( &amp;fc, "float =", 10.0 );<img src ="http://www.cppblog.com/richardhe/aggbug/52147.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-06-04 14:45 <a href="http://www.cppblog.com/richardhe/articles/52147.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>仿函数、绑定、桥接、委托相关讨论</title><link>http://www.cppblog.com/richardhe/articles/52015.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Tue, 03 Jun 2008 03:39:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/52015.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/52015.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/52015.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/52015.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/52015.html</trackback:ping><description><![CDATA[flipcode<br>仿函数、绑定、桥接、委托相关讨论:<br>以下随便讨论下，没突出的中心论点，个中理论只代表我个人观点，难免有错:)，欢迎指正。<br>一。需求:<br>在事件处理常常会碰到这样的情况：<br>1。接口分离。即invokers(调用者)与(receivers)接收者分离。<br>2。时间分离。<br><br>比如说：UI相关元素（按钮、菜单等）就是一个invokers。<br>receivers则是响应命令的对象（如对话框或应用程序本身）。<br>这需要我们要先将UI相关元素的事件响应的接收者在初始化时先保存起来。<br>待后用户按下按钮等再触发（即invokers通过调用对应先前保存的receivers来执行命令）<br>嗯，在delphi、java、vcl、.net中有相关的实现。而vc则需要自己来弄。<br><br>二。仿函数的实现:<br>在说仿函数前先说说我们应该怎么保存这些操作相关函数的呢？<br>// 一般的函数我们可以这么存:<br>void (*fun)() = Test;<br>(*fun)();<br>// 而类成员函数可以这么做:<br>void (CTest::*mfn)(); // 或用 typedef void (CTest::*MFN_TEST)(); MFN_TEST mfn;<br>mfn = CTest::Test;<br>CTest a, *p=new CTest;<br>(a.*mfn)(); // 调用方法1<br>(p-&gt;*mfn)(); // 调用方法2<br>如上所述可见为了处理前面所述的事件响应情况，我们通常会用回调函数，<br>就是把类成员函数定义为静态函数，在初始时保存函数地址（与一般函数处理类同）及对应的对象指针，<br>在事件触发时调用对应的静态函数，而该函数中在把指针强制转化为对应类型对象地址，<br>得以操纵该对象的成员变量(嗯，理论上跟成员函数的实现差不多，成员函数会由编译器安插一个<br>this指针作为第1个参数传给函数，以便可以操作该this对象的成员)。<br><br>回调函数应用的具体代码如下:<br>1). 回调接口(静态函数法):<br>//======================================================<br>#include "stdafx.h"<br>#include <br><br>typedef void(*KEY_RESPOND)(void* /*,param*/);<br>struct CListener<br>{<br>void* pThis;<br>KEY_RESPOND pfn;<br>CListener() : pThis(0), pfn(0){}<br>};<br>class CInput<br>{<br>std::list m_listListener;<br>public:<br>void AddListener( CListener* pListener ){<br>m_listListener.push_back( pListener );<br>}<br>void RemoveListerner( CListener* pListener ){<br>std::list::iterator iter;<br>for( iter = m_listListener.begin(); iter!= m_listListener.end();iter++ ){<br>if( pListener == (*iter) ){<br>m_listListener.erase( iter ); break;<br>}<br>}<br>}<br>void HitOneKey(){<br>std::list::iterator iter;<br>for( iter = m_listListener.begin(); iter!= m_listListener.end();iter++ ){<br>if( (*iter)-&gt;pfn &amp;&amp; (*iter)-&gt;pThis ){<br>(*(*iter)-&gt;pfn)( (*iter)-&gt;pThis );<br>}<br>}<br>}<br>void clearListener(){<br>m_listListener.clear();<br>}<br>};<br>class CUI<br>{<br>public:<br>static void InputProc(void* pThis/*,param*/){<br>__asm int 3 // 下个断点测试下（某些编译器不能这么写，vc可以）<br>}<br>};<br><br>CUI ui;<br>CInput input;<br>int _tmain(int argc, _TCHAR* argv[])<br>{<br>// 初始:<br>CListener* pListener = new CListener;<br>pListener-&gt;pfn = &amp;CUI::InputProc;<br>pListener-&gt;pThis = &amp;ui;<br>// 触发:<br>input.AddListener( pListener ); // input即为invokers（调用者，但叫触发者好点）<br>input.HitOneKey(); // 某处事件触发,内部呼叫receivers(这里是原先保存的CUI对象)来真正处理该事件(InputProc(...)方法)。<br>// 清除<br>input.clearListener();<br>if( pListener )<br>{<br>delete pListener;<br>pListener = NULL;<br>}<br>return 0;<br>}<br>//======================================================<br><br>// 第2种方法: 回调类(虚函数多态法):<br>//======================================================<br>class IWillBack<br>{<br>public:<br>virtual void InputProc(/*参数略...*/){}<br>};<br>class CInput<br>{<br>public:<br>void RegisterListener(IWillBack* pListener){<br>m_pListener = pListener; // 这里用list存起来才好，这里只作测试<br>}<br>void OnInputEvent(){<br>m_pListener-&gt;InputProc(/*参数略...*/);<br>}<br><br>private:<br>IWillBack* m_pListener;<br>};<br>class CUI : public IWillBack<br>{<br>public:<br>void InputProc(/*参数略...*/){ /*..实际处理代码..*/}<br>private:<br>};<br><br>int _tmain(int argc, _TCHAR* argv[])<br>{<br>CInput aa;<br>CUI bb;<br>aa.RegisterListener(&amp;bb);<br>aa.OnInputEvent();<br>return 0;<br>}<br>//======================================================<br><br>但是第1种静态函数用法是不直观的，第2种需要派生增加了之间的联系，而为了方便我们通常会将成员函数指针转化为函数对象来处理,即仿函数(一般是指重载了()操作符的类)来实现。<br><br>类似于这样的操作，stl提供了mem_fun、mem_fun_ref、binder1st、binder2nd简单操作。<br>但stl的方法相对比较原始而受限制，比如说std::mem_fun需要成员函数有返回值,<br>std::mem_fun最多只能支持成员函数有一个参数等，<br>下面来看std:mem_fun_ref不支持成员函数返回值为void的一个例子:<br>//======================================================<br>#include <br>class CFoo<br>{<br>public:<br>void test() // 只有将void改成别的类型才可以，如:int<br>{<br>return 0;<br>}<br>};<br>void main()<br>{<br>CFoo t;<br>std::mem_fun_ref(&amp;CFoo::test)(t);<br>}<br>//======================================================<br>上述代码只有将void改成别的类型（如int）才可以，<br>那么为什么不可以处理返回void的函数呢? stl的实现究竟是怎么样的呢?<br>嗯，stl简单实现了mem_fun_ref及mem_fun,其中mem_fun_ref以引用方式处理函数所属对象，<br>而mem_fun以指针方式处理函数所属对象。<br>现在让我们从vc的stl挖出部份代码来看看，<br><br>1.stl的实现:<br>以mem_fun_ref为例(省略某些对说明不重要的细节，两条虚线包括的代码为stl类似源码):<br>//======================================================<br>//----------------------------------------------------------<br>namespace stl_test<br>{<br>// 主要实现:<br>template<br>class mem_fun_ref_t<br>{<br>public:<br>mem_fun_ref_t( R (T::*_Pm)() ) : _Ptr(_Pm) {} // 构造: 保存成员函数地址<br>R operator()(T&amp; _X) const // 调用: 这里可看出mem_fun_ref以引用方式处理<br>{<br>return ((_X.*_Ptr)()); // 这里执行调用函数，并返回该函数所返回值<br>}<br>private:<br>R (T::*_Ptr)(); // 指向成员函数地址的指针<br>};<br>// 这里只是利用函数的参数推导来自动获取型别(方便调用)<br>template inline<br>mem_fun_ref_t mem_fun_ref(R (T::*_Pm)())<br>{<br>return (mem_fun_ref_t(_Pm));<br>}<br><br>} // end of namespace test_stl<br>//----------------------------------------------------------<br>class CFoo<br>{<br>public:<br>int test1(){<br>__asm int 3<br>return 0;<br>}<br>void test2(){<br>__asm int 3<br>}<br>};<br>int APIENTRY WinMain(HINSTANCE hInstance,<br>HINSTANCE hPrevInstance,<br>LPSTR lpCmdLine,<br>int nCmdShow)<br>{<br>CFoo t;<br>stl_test::mem_fun_ref( &amp;CFoo::test1 ) (t);<br>return 0;<br>}<br>//======================================================<br>/////////////////////////////////////////////////////////////////<br>从源码"return ((_X.*_Ptr)()); " 可以看到stl直接返回该函数所返回值。<br>所以函数没有返回值（即为void时）的话编译器就会报错。好，那么如果我们在<br>这里只是直接执行函数而不用return返回的话编译器应该可以通过了。<br><br>嗯，boost中正是这么处理的。（btw.为了更为通用，boost对stl原有仿函数及绑定作了大量的改进工作）。<br>但是具体应该怎么区分有没有返回值呢？这个也容易，我们只需用到模板的偏特性就可<br>以做到。下面就看看boost的实现(btw.boost有两种版本，我用的是兼容版本，代码难看)<br><br>2. boost的实现(这里我把boost的一大堆宏（真@$@#@#难看，loki在这方面来得比较清爽）去掉了):<br>/////////////////////////////////////////////////////////////////<br>// notreturn.cpp : Defines the entry point for the application.<br>//<br><br>#include "stdafx.h"<br>//------------------------------------<br>namespace boost_test<br>{<br>template // 有返回值时会调用这个<br>struct mf<br>{<br>template<br>class inner_mf0<br>{<br>R (T::*_Ptr)();<br>public:<br>inner_mf0(R (T::*f)()) : _Ptr(f) {}<br>R operator()(T&amp; X) const<br>{<br>return ((X.*_Ptr)());<br>}<br>};<br>};<br>template&lt;&gt; // 没有反回值时会调用这个<br>struct mf // 偏特化<br>{<br>template<br>class inner_mf0<br>{<br>R (T::*_Ptr)();<br>public:<br>inner_mf0(R (T::*f)()) : _Ptr(f) {}<br>R operator()(T&amp; X) const<br>{<br>((X.*_Ptr)());<br>}<br>};<br>};<br>// 创建一派生类,派生于上述基类<br>template<br>struct mf0 : public mf::inner_mf0<br>{<br>typedef R(T::*F)();<br>explicit mf0(F f) : mf::inner_mf0(f) {}<br>};<br>// 通过函数的参数推导自动获取类型<br>template<br>mf0 mem_fn( R(T::*f)() )<br>{<br>return mf0(f);<br>}<br>} // namespace boost_test<br>//------------------------------------<br><br>class CFoo<br>{<br>public:<br>int test1(){ return 0; }<br>void test2(){}<br>};<br>int APIENTRY WinMain(HINSTANCE hInstance,<br>HINSTANCE hPrevInstance,<br>LPSTR lpCmdLine,<br>int nCmdShow)<br>{<br>CFoo t;<br>boost_test::mem_fn( &amp;CFoo::test1 ) (t);<br>return 0;<br>}<br>/////////////////////////////////////////////////////////////////<br><br>从上述代码可以看到偏特性帮助我们解决了返回值为void的情况。但是手写了两份<br>基本相同的代码。。。<br>另外处理参数个数的情况也很容易，只要分别实现不同参数的各个模板类就可以了，<br>boost最多只能支持成员函数有8个参数，因为它内部实现了8份这样不同参数模板类。<br>其实的处理方法都是一模一样的，可是由于语言的限制我们还是没有办法不一一实现<br>不同参数的类:。在loki中参数可以用TList实现任意的参数，但是在实现还是得老<br>老实实的每份手写一份（loki实现了15份可以支持15个参数）。<br>这真让人郁闷。。。不过没办法。<br><br>说完来仿函数，下面开始说说有关绑定，stl、boost、loki的绑定的意思是<br>对某物实体的&#8220;绑定&#8221;，通俗来说是指对函数、构造函数、仿函数等与其对应的某个参数的绑定，<br>以便在调用时不用再次输入此参数（因为某些时候参数是固定的，比如说绑定一个内部存有<br>成员函数地址的仿函数和它对应的对象地址在一起）。<br>以下是stl的bind用法:<br>//================================<br>#include "stdafx.h"<br>#include  // stl<br>#include  // boost<br><br>struct CFoo {<br>int test(int){<br>return 0;<br>}<br>};<br>void main()<br>{<br>boost::function1 f; // 这里用了boost<br>CFoo obj;<br>f = std::bind1st(std::mem_fun(&amp;CFoo::test), &amp;obj);<br>f(5);<br>}<br>//================================<br>loki中的BindFirst比较类似于stl的binder<br>(binder1st，binder2nd)，但是它是通用的，可以通过嵌套实现任意多个参数绑定：<br>//================================<br>void f()<br>{<br>Functor cmd1(something);<br>Functor cmd2(BindFirst(cmd1, 10));<br>cmd2(20);<br>Functor cmd3(BindFirst(cmd2, 30));<br>cmd3();<br>}<br><br>而boost中的实现是以占位符来表现,具体如何实现，下回继续讨论(嗯，<br>boost代码的宏太多了，这部份还是等有空再补全了，现在我们来看看如何实现一个委托类)<br><br>三。委托类的实现:<br>1. 桥接模式:<br>设计模式告诉我们可以使用桥接模式(Bridge Pattern)减少对象之间的 耦合度，桥接模式如下:<br>Invoker &lt;&gt;-------------------&gt;* Interface<br>^<br>|<br>Receiver<br>上图的Invoker表示事件触发者，Receiver表示事件处理者，符号类似于＜c++大规模编程。。＞一书所描述（注：这里符号对位可能出错：变成左对齐了），<br>其中&lt;&gt;-------------------&gt;表示Invoker 内含(拥用)Interface(即Invoker 有Interface的变量或指针并负责Interface的释放)，<br>而*号表示可有多个。<br>^<br>| 　号则表示继承于（Receiver继承于Interface）。<br><br>好，我们先来分析前面在" 第2种方法: 回调类(虚函数多态法):"的实现思想(请回到前面看看代码)，<br>它其实就是一个桥接模式，如下(括号内对应前面所实现的类)：<br>Invoker(CInput) &lt;&gt;---------------&gt;* Interface（IWillBack）<br>^<br>|<br>Receiver（CUI）<br>对照我们前面实现的代码可以发现此种实现的桥接的缺点是：每一个想要<br>注册一方法到Invoker中以便Invoker在事件触发时调用的类(如Receiver)都要派生自Interface。<br>有没有更好的办法再次减少这种耦合度呢？这正是下面我们要讨<br>论的下一种设计模式：<br>2. 委托与事件:<br>委托的处理设计如下:<br>Invoker &lt;&gt;---------------------&gt;* Interface<br>^<br>|<br>Implementation -----------------&gt; Receiver<br>即在原桥接模式下再加一层间接性:Implementation 。其中<br>Implementation与Receiver之间的-----------------&gt;表示Implementation引用了Receiver一些服务，<br>即Implementation用到了Receiver某些东西（如函数或数据）。嗯，这些解释不知是否适当，希望不会误导。。。<br>好，一开始可能我们会这么设计:<br>//======================================================================================<br>class handle {};<br>template <br>class Implementation : public handle<br>{<br>T* m_pThis;<br>public:<br>Implementation ( T* pThis ) : m_pThis(pThis) {}<br>template<br>void execute( void (T::*mfn)(T1), T1 arg ) { (m_pThis-&gt;*mfn)( arg ); }<br><br>};<br>struct Receive {<br>void Proc(int) {<br>__asm int 3<br>}<br>};<br>Receive a;<br>void Invoker(){<br>Implementation test = Implementation (&amp;a);<br>test.execute( Receive::Proc, 10 ); // 当事件发生时调用<br>};<br>int _tmain(int argc, _TCHAR* argv[])<br>{<br>Invoker();<br>}<br>//======================================================================================<br>但是Invoker知道了太多Receive的信息，况且我们想让触发者Invoker作成一个类。<br>一个改进的版本如下:<br>//-------------------------------------------------------------<br>// signal slot system<br>// 注: 该法我是看了"落木随风"的"委托、信号和消息反馈的模板实现技术"，<br>// 代码作了部份添加。在这里非常的感谢他!<br>// 他的博客：http://blogs.gcomputing.com/rocwood/archives/000154.html<br>//-------------------------------------------------------------<br>// Delegation.cpp : Defines the entry point for the console application.<br>//<br>#include <br><br>#ifndef K_DELEGATE_H<br>#define K_DELEGATE_H<br><br>namespace kUTIL<br>{<br>// 1. 桥接类(纯虚类):<br>// 为什么叫作桥接?<br>// 因为通过它的虚函数方法可以调用到对应的正确的不同派生实例(指后面<br>// 提到的委托人)所改写的虚函数方法(这是委托人用来完成委托任务的方法)<br>struct kDelegationInterface<br>{<br>virtual ~kDelegationInterface() {};<br>virtual void Execute() = 0;<br>};<br>// 2. 委托类(派生于桥接类，这里我叫为&#8221;委托人&#8220;)<br>// 为什么叫委托？<br>// 因为调用者把&#8220;通知&#8221;的工作委托给它来负责处理。<br>// 一个&#8220;委托人&#8221;保存了: a.&#8221;接收者&#8220;(对象指针m_pThis) 及 b.&#8220;要作的事&#8221;(方法指针m_mfn)，<br>// 以便调用者发出信号弹(后面提到,信号弹有一个作桥接用的纯虚类的指针指向相应的委托人)<br>// 告知此信号对应的委托人来完成它被委托的工作：即让&#8220;接收者&#8221;(m_pThis)作&#8221;要作的事&#8220;(m_mfn)。<br>template<br>struct kDelegationImpl : public kDelegationInterface<br>{<br>typedef void ( T::* MFN )();<br>kDelegationImpl( T* pthis, MFN mfn ) : m_pThis( pthis ), m_mfn( mfn ) {<br>}<br>virtual void Execute() {<br>if( m_pThis ) { <br>( m_pThis-&gt;*m_mfn )(); <br>}<br>}<br>T* m_pThis;<br>MFN m_mfn;<br>};<br><br>// 3. 信号弹(实现为仿函数来调用统一的虚函数接口):<br>// 为什么叫信号？<br>// 因为当"信号弹"发射时(调用信号的操作符"()")<br>// 它会通知所指向的"委托人"事件发生了(调用纯虚类指针的m_DI-&gt;Execute()方法)。<br>// 一个信号保存了一个指向对应&#8221;委托人&#8220;的桥接类(纯虚类)指针。<br>struct kSignal0<br>{<br>kDelegationInterface* m_DI; // 纯虚类的指针<br>kSignal0() : m_DI(0) {}<br>// 1. 纯虚类的m_DI指针可以指向不同的派生实例:<br>template<br>void ConnectSlot(T* recv, void (T::* mfn)()) {<br>DisConnect();<br>m_DI = new kDelegationImpl( recv, mfn );<br>int test = 0;<br>}<br>void DisConnect() {<br>if( m_DI ) { delete m_DI; m_DI = NULL; }<br>}<br>// 2. 用统一的纯虚类指针调用不同派生类改写的虚函数<br>void operator() () { <br>if( m_DI ) {<br>m_DI-&gt;Execute(); <br>}<br>}<br>};<br><br>// 下面是两个为方便使用的函数:<br>template<br>void kConnect( kSignal0&amp; sgn, T* pObj, void(T::*fn)())<br>{<br>sgn.ConnectSlot( pObj, fn );<br>int i = 0;<br>}<br>inline void kDisConnect( kSignal0&amp; sgn )<br>{<br>sgn.DisConnect();<br>}<br>} // end of namespace kUTIL<br>#endif //#ifndef K_DELEGATE_H<br><br>//----------------------------------------------------------------------------<br>// 一个使用实例:<br>class kButton {<br>public:<br>kUTIL::kSignal0 sgnMouseBtnUp;<br>void OnMouseButtonUp() { sgnMouseBtnUp(); }<br>};<br><br>class kDialog {<br>kButton btn;<br>public:<br>kDialog() {<br>kUTIL::kConnect( btn.sgnMouseBtnUp, this, &amp;kDialog::DoWork ); // vc6下这里kDialog::DoWork的前面一定要可加"&amp;"号<br>}<br>void DoWork() { <br>__asm int 3<br>}<br>void TestMouseHit() { btn.OnMouseButtonUp(); }<br>};<br><br>int main(int argc, char* argv[])<br>{<br>kDialog dlg;<br>kButton btn;<br>kUTIL::kConnect( btn.sgnMouseBtnUp, &amp;dlg, kDialog::DoWork ); // vc6下这里kDialog::DoWork的前面可加/不加"&amp;"号<br><br>// 测试一:<br>btn.OnMouseButtonUp();<br><br>// 测试二:<br>dlg.TestMouseHit();<br>return 0;<br>}<br><br>// 委托实例总结：<br>// 下面我们来具体说明&#8221;当某事发生时，调用者发射信号弹通知对应的接收者作相应处理&#8220;<br>// 1. "调用者" 拥有各种信号弹。<br>// 2. 初始时，我们把信号弹与对应的委托人联系起来，并让委托人记录在信号触发时应该通知的"接收人"和"接收人该作的事"。<br>// a. 信号弹保存了桥(纯虚类)指针，指针指向通过其模板函数ConnectSlot方法来找出(产生的)委托人(委托实例)。<br>// b. 委托人(委托实例)在信号弹用ConnectSlot方法产生它的时候保存了函数ConnectSlot所传入的两个参数：<br>// 即"接收者指针"及"其方法指针"。<br>// 3. 当事件发生时"调用者"发射对应信号弹后，信号弹会调用其所保存的纯虚类指针的虚函数方法，<br>// 于是由于虚函数特性就会调用到其所指向的委托实例(委托人)所改写的方法。<br>// 5. 委托人改写的方法中通过其所保存的&#8221;接收者指针&#8220;及其"方法指针"来呼叫"接收者"用对应的&#8221;方法指针&#8220;<br>// 来处理事情。<br>// 即如下流程:<br>// "调用者"发射"信号弹" ---&gt; "信号弹"通过"桥"找到对应"委托人" ---&gt; "委托人"呼叫"接收者"作"该作的事"<br><br>//=============================================================================<br>嗯，这样到此，一个非常方便的委托类就得以实现了！如果你还不懂的话请仔细的琢磨，如此精华(因为简单而强大)<br>不要错过。不过上述只是部份实现，当你要支持带参数及返回值的各种情况的话，还得自己作扩充。<br>返回值的处理方法可参见前面剖述boost的mem_fn的处理方法，而带不同参数的处理则只能一一手动<br>实现，就象前面所说的那样，这是很无奈的事情，但是目前来说没有办法。。。<br><br>(待续。。。。。。,如果有时间有必要的话。。。)<br><br>附:<br>loki下载:<br>http://sourceforge.net/projects/loki-lib/<br><br>boost下载:<br>http://sourceforge.net/projects/boost/<br><br>2004.10.26更新：<br>修正:<br>原void connect( Signal0&amp; sgn,T1 obj, void(T2::*fn)())改成<br>void connect( Signal0&amp; sgn,T1&amp; obj, void(T2::*fn)())<br>另外加了kDisConnect释放内存，原来只作测试没写它，现在还是加上了。<br><br>--------------------------------------------------------------------------------<br><br>千里马肝<br>实在是太好了<br><br>PS，阳春的东西，就会曲高合寡。 :D<br><br>--------------------------------------------------------------------------------<br><br>flipcode<br>好久没上网了，<br>今天上来看到版主的夸奖，<br>虽然是过奖了，不过还是感觉轻飘飘的。。。嘿嘿~~~， :D<br><br>--------------------------------------------------------------------------------<br><br>kane<br>嗯，这种技巧是比较实用的。<img src ="http://www.cppblog.com/richardhe/aggbug/52015.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-06-03 11:39 <a href="http://www.cppblog.com/richardhe/articles/52015.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何将16进制字符串转换成整型？</title><link>http://www.cppblog.com/richardhe/articles/50068.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Fri, 16 May 2008 09:44:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/50068.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/50068.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/50068.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/50068.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/50068.html</trackback:ping><description><![CDATA[<span style="color: #000000;">转自http://www.cppblog.com/alantop/archive/2008/05/15/49989.html<br>&nbsp;</span><span style="color: #0000ff;">char</span><span style="color: #000000;">&nbsp;test[]&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">0xf</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br>&nbsp;</span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;lResult&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;strtol(test,&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">\0</span><span style="color: #000000;">'</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">16</span><span style="color: #000000;">);</span><img src ="http://www.cppblog.com/richardhe/aggbug/50068.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-05-16 17:44 <a href="http://www.cppblog.com/richardhe/articles/50068.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>callback function</title><link>http://www.cppblog.com/richardhe/articles/49933.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Thu, 15 May 2008 06:54:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/49933.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/49933.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/49933.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/49933.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/49933.html</trackback:ping><description><![CDATA[<pre>声明函数指针<br><br>    回调函数是一个程序员不能显式调用的函数；通过将回调函数的地址传给调用者从而实现调用。要实现回调，必须首先定义函数指针。尽管定义的语法有点不可思议，但如果你熟悉函数声明的一般方法，便会发现函数指针的声明与函数声明非常类似。请看下面的例子：<br><br>void f()；// 函数原型<br><br>上面的语句声明了一个函数，没有输入参数并返回void。那么函数指针的声明方法如下：<br><br>void (*) ();<br><br>    让我们来分析一下，左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型（void）和由边圆括弧中的入口参数（本例中参数是空）。注意本例中还没有创建指针变量-只是声明了变量类型。目前可以用这个变量类型来创建类型定义名及用sizeof表达式获得函数指针的大小：<br><br>// 获得函数指针的大小<br>unsigned psize = sizeof (void (*) ()); <br><br>// 为函数指针声明类型定义<br>typedef void (*pfv) ();<br><br>pfv是一个函数指针，它指向的函数没有输入参数，返回类行为void。使用这个类型定义名可以隐藏复杂的函数指针语法。<br><br>指针变量应该有一个变量名：<br><br>void (*p) (); //p是指向某函数的指针<br><br>    p是指向某函数的指针，该函数无输入参数，返回值的类型为void。左边圆括弧里星号后的就是指针变量名。有了指针变量便可以赋值，值的内容是署名匹配的函数名和返回类型。例如：<br><br>void func() <br>{<br>/* do something */<br>} <br>p = func; <br><br>p的赋值可以不同，但一定要是函数的地址，并且署名和返回类型相同。<br><br>传递回调函数的地址给调用者<br><br>    现在可以将p传递给另一个函数（调用者）- caller()，它将调用p指向的函数，而此函数名是未知的：<br><br>void caller(void(*ptr)())<br>{<br>ptr(); /* 调用ptr指向的函数 */ <br>}<br>void func();<br>int main()<br>{<br>p = func; <br>caller(p); /* 传递函数地址到调用者 */<br>}<br><br>    如果赋了不同的值给p（不同函数地址），那么调用者将调用不同地址的函数。赋值可以发生在运行时，这样使你能实现动态绑定。<br><br>调用规范<br><br>    到目前为止，我们只讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。许多编译器有几种调用规范。如在Visual C++中，可以在函数类型前加_cdecl，_stdcall或者_pascal来表示其调用规范（默认为_cdecl）。C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名，参数传递的顺序（从右到左或从左到右），堆栈清理责任（调用者或者被调用者）以及参数传递机制（堆栈，CPU寄存器等）。<br><br>    将调用规范看成是函数类型的一部分是很重要的；不能用不兼容的调用规范将地址赋值给函数指针。例如：<br><br>// 被调用函数是以int为参数，以int为返回值<br>__stdcall int callee(int); <br><br>// 调用函数以函数指针为参数<br>void caller( __cdecl int(*ptr)(int)); <br><br>// 在p中企图存储被调用函数地址的非法操作<br>__cdecl int(*p)(int) = callee; // 出错<br><br><br>    指针p和callee()的类型不兼容，因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针p，尽管两者有相同的返回值和参数列。<br></pre><img src ="http://www.cppblog.com/richardhe/aggbug/49933.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-05-15 14:54 <a href="http://www.cppblog.com/richardhe/articles/49933.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《设计模式精解》读书笔记</title><link>http://www.cppblog.com/richardhe/articles/49843.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 14 May 2008 09:53:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/49843.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/49843.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/49843.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/49843.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/49843.html</trackback:ping><description><![CDATA[<div class="postTitle">
<a  href="http://www.cnblogs.com/lzcarl/archive/2005/09/01/228281.html" id="AjaxHolder_ctl01_TitleUrl" class="postTitle2">《设计模式精解》读书笔记 &lt;转&gt;http://www.cnblogs.com/lzcarl/archive/2005/09/01/228281.html</a>
</div>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US">1</span></strong><strong><span style="font-family: 宋体;">、模式是应该结合在一起共同解决问题的。</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论：以前看《</span><span lang="EN-US">GOF</span><span style="font-family: 宋体;">设计模式》的时候主要就是看目的，然后就是代码，对协作啊，优缺点啊，效果啊都不怎么注意，不过就算看了也不知所云。学下来就像背数学公式一样，单个是记住了，拿到具体环境里又懵了。看来还是得多花点时间看看模式的关联啊。</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US">2</span></strong><strong><span style="font-family: 宋体;">、我的错误是：我尝试先创建问题领域中的累，然后将这些类缝合起来形成最终的系统。——</span><span lang="EN-US">Alexander</span></strong><strong><span style="font-family: 宋体;">把这样的过程称为一个&#8220;坏主意&#8221;。我从来没有问过我自己：我是否拥有正确的类？仅仅因为这些类看起来如此正确、如此明显。我拥有的是在开始分析时立刻进入脑海中的类，是我们的老师告诉我们应该在系统中寻找的&#8220;名词&#8221;。但是我的错误就是仅仅尝试把它们简单地放在一起。</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论：连大牛都会犯这样的错误，我这样的菜鸟这样分析系统应该也比较正常吧。在看问题的层次上，我们太过于重视细节，就像我写</span><span lang="EN-US">WinForm</span><span style="font-family: 宋体;">程序时先考虑有哪些组件可以使用一样，把思路给限制住了，忽视了类所处的环境、上下文。</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US">3</span></strong><strong><span style="font-family: 宋体;">、面向对象的范式：使用对象将责任转义到更局部的范围。对象应该对自己负责，并且这种责任应该被清楚的定义出来。</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论：这个对类的定义就要合理的多。作者认为软件开发中有三个视角：概念（用例）－</span><span lang="EN-US">&gt;</span><span style="font-family: 宋体;">规格（接口）－</span><span lang="EN-US">&gt;</span><span style="font-family: 宋体;">实现（代码）。从最低层次看，类就是变量</span><span lang="EN-US">+</span><span style="font-family: 宋体;">函数，但这样就太狭隘了。真正的对象应该与现实生活中的对象相似，有自己的行为、状态，<strong>自己对自己负责</strong>（多么精确的概述啊）。</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US">4</span></strong><strong><span style="font-family: 宋体;">、抽象类就是其他类的占位符。</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论：比较形象的描述。我先描述类的大致行为供调用者了解，具体的行为留到子类实现，抽象类与子类达成一个契约。</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US">5</span></strong><strong><span style="font-family: 宋体;">、过早的对细节投入过多的关心是一个分析缺陷。</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论：同</span><span lang="EN-US">2</span><span style="font-family: 宋体;">。</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US">6</span></strong><strong><span style="font-family: 宋体;">、留意你的直观：当你看到某些不喜欢的东西时，你胃部的感觉。</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论：牛人就是幽默。这里主要是说明如何感觉一个设计是坏的设计的。我是说最近写程序胃老不舒服，饭也吃不好&#8230;&#8230;</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US">7</span></strong><strong><span style="font-family: 宋体;">、关注模式的场景而非解决方案。</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论：作者自始至终一直强调是场景决定解决方案，而不是为了实现某个模式而去进行设计，否则就像小学生套公式一样了。场景分析好了，模式自然也就出来了（这是境界比较高的时候）。</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US">8</span></strong><strong><span style="font-family: 宋体;">、在创建对象时用共同点</span><span lang="EN-US">/</span></strong><strong><span style="font-family: 宋体;">变化点分析而非观察名词与动词。因此，我们要：</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US"><span>&nbsp;&nbsp; </span>a</span></strong><strong><span style="font-family: 宋体;">、发现并封装变化点。</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US"><span>&nbsp;&nbsp; </span>b</span></strong><strong><span style="font-family: 宋体;">、优先使用对象组合，而非类继承。</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论：关于共同点</span><span lang="EN-US">/</span><span style="font-family: 宋体;">变化点的分析，在</span><span lang="EN-US">Bridge</span><span style="font-family: 宋体;">模式中体现的犹为明显（我在前面的</span><span lang="EN-US">Blog</span><span style="font-family: 宋体;">中介绍过），借助分析矩阵也可以帮助理清思路。而</span><span lang="EN-US">b</span><span style="font-family: 宋体;">点是面向对象设计中的基本原则，就不用多说了。</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US">9</span></strong><strong><span style="font-family: 宋体;">、</span><span lang="EN-US">switch</span></strong><strong><span style="font-family: 宋体;">语句常常标志着：（</span><span lang="EN-US">1</span></strong><strong><span style="font-family: 宋体;">）对多态行为的需要。（</span><span lang="EN-US">2</span></strong><strong><span style="font-family: 宋体;">）存在着放错地方的责任。请考虑用一种更普适的解决方案代替</span><span lang="EN-US">switch</span></strong><strong><span style="font-family: 宋体;">语句，比如抽象、将责任分配给另外的对象等等。</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论：老听别人说丑陋的</span><span lang="EN-US">switch</span><span style="font-family: 宋体;">语句、</span><span lang="EN-US">ifelse</span><span style="font-family: 宋体;">语句，都不是太懂，现在好像有点懂了，因为条件语句不灵活，很难维护吧。碰到它马上就要想到用多态的方式去处理，像</span><span lang="EN-US">State</span><span style="font-family: 宋体;">模式、</span><span lang="EN-US">Visitor</span><span style="font-family: 宋体;">模式就和这有关。</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US">10</span></strong><strong><span style="font-family: 宋体;">、用模式的方法去思考：</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span style="font-family: 宋体;">步骤：</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span style="font-family: 宋体;">（</span><span lang="EN-US">1</span></strong><strong><span style="font-family: 宋体;">）、发现我在问题领域中拥有的模式</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span style="font-family: 宋体;">（</span><span lang="EN-US">2</span></strong><strong><span style="font-family: 宋体;">）、对于这些需要分析的模式，做下列工作：</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US"><span>&nbsp;&nbsp; </span>a</span></strong><strong><span style="font-family: 宋体;">、挑出为其他模式提供最多场景的模式。</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US"><span>&nbsp;&nbsp; </span>b</span></strong><strong><span style="font-family: 宋体;">、在我概念性最高的设计中使用这个模式。</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US"><span>&nbsp;&nbsp; </span>c</span></strong><strong><span style="font-family: 宋体;">、识别任何可能已经出现的附加模式。将它们添加到&#8220;需要分析的模式&#8221;中。</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US"><span>&nbsp;&nbsp; </span>d</span></strong><strong><span style="font-family: 宋体;">、对需要分析而未分析的模式，重复上述工作。</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span style="font-family: 宋体;">（</span><span lang="EN-US">3</span></strong><strong><span style="font-family: 宋体;">）、按照需要将细节添加的设计中。扩展方法和类定义。</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论：具体的说就是找到可以统领全局的模式，再用其他的模式配合它，&#8220;面向模式的设计&#8221;</span><span lang="EN-US">(POP)<img  src="http://www.cnblogs.com/Emoticons/emteeth.gif" align="absmiddle" border="0"></span><span style="font-family: 宋体;">。算了吧，我连</span><span lang="EN-US">OOP</span><span style="font-family: 宋体;">都没学好，这个以后再说啦。</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US">11</span><span style="font-family: 宋体;">、在学习面向对象技术的早期学习设计模式，可以大大帮助你提高对面向对象分析、设计的理解。</span></strong></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论：这个倒是第一次听说，不过怎么也得先把继承、封装、多态弄清楚吧。再加上一条，在学习设计模式时，一定要先读这本《设计模式精解》，再看《</span><span lang="EN-US">GOF</span><span style="font-family: 宋体;">设计模式》，</span><span lang="EN-US"><img  src="http://www.cnblogs.com/Emoticons/emwink.gif" align="absmiddle" border="0"></span><span style="font-family: 宋体;">。</span></p><img src ="http://www.cppblog.com/richardhe/aggbug/49843.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-05-14 17:53 <a href="http://www.cppblog.com/richardhe/articles/49843.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OOD原则：SRP、OCP以及LSP</title><link>http://www.cppblog.com/richardhe/articles/49813.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 14 May 2008 05:21:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/49813.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/49813.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/49813.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/49813.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/49813.html</trackback:ping><description><![CDATA[<span id="ArticleContent1_ArticleContent1_lblContent">
<div id="blogContent"><font style="line-height: 1.5em;" size="4"><strong>单一职责原则（SRP :  Single Response Principle)</strong></font><br><br>就一个类而言，应该仅有一个引起它变化的原因。<br><font color="#ee1d24">在这里，职责的定义是：  &#8220;变化的原因&#8221;。</font><br><br>对于何时遵循SRP有以下的考虑：<br>1.如果应用程序的变化会影响到类中某一种职责，那么就应该将它与另一种职责分开，这样做可以避免客户应用程序和类中的这两职责耦合在一起。<br>2.如果应用程序的变化总是会导致两个职责同时变化，那么就不必要分离它们。实际上，分离它们会引起不必要的复杂性。<br><br>从上可以得知：变化的轴线仅当变化实际发生时才具有真正的意义。如果没有征兆，那么去应用SRP，或者任何其它原则都是不明智。<br><br><strong>实际应用：持久化（Persistence）</strong><br>实
际开发中，考虑到业务规则是会频繁改变的，而持久化的方式却不会如此频繁的变化，并且变化的原因也是完全不同的。如果把业务规则和持久化方式绑定到一起，
就会为以后的开发、维护造成麻烦。运用分层（layer）架构模式或者TDD开发方式可以很早分离这两个职责，特殊情况下，还可以使用FACADE或者
PROXY模式对设计进行重构，分离这两个职责。<br><br>
<div id="blogContent"><strong><font style="line-height: 1.5em;" size="4">开闭原则（OCP : The  Open-Close  Principle)</font></strong><br><br>描述：软件实体（类、模型、函数等等）应该是可以扩展的，但是不可修改。<br><br>遵循开闭原则设计出的模块具有两个主要的特征。它们是：<br>1.  &#8220;对于扩展是开放的&#8221;（Open for extension）。<br>&nbsp;&nbsp;  这意味着模块的行为是可以扩展的。当应用的需要改变时，我们可以对模块进行扩展，使其具有满足那些改变的新行为。换句话说，我们可以改变模块的功能。<br>2.  &#8220;对于更改是封闭的&#8221;（Closed for modification）。<br>&nbsp;&nbsp;  对模块行为进行扩展时，不必改动模块的源代码或者二进制代码。模块的二进制可执行版本，无论是可链接的库、DLL或者Java的.jar文件，都无需改动。<br><br><font color="#ee1d24">对于OCP，关键的是  抽象</font><br>模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体，所以它对于更改可以是关闭的。同时，通过从这个抽象体派生，也可以扩展此模块的行为。<br><br>在许多方面，OCP都是面向对象设计的核心所在。但实际应用中，滥用OCP原则也是错误的。正确的做法是应该仅仅对程序中呈现出频繁变化的那些部分做出抽象。拒绝不成熟的抽象和抽象本身一样重要。</div>
<br><br>
<div id="blogContent"><strong><font style="line-height: 1.5em;" size="4">Liskov替换原则（LSP）</font></strong><br><br>描述：子类型（subtype）必须能够替换掉它们的基类型（base  type）。<br><br>此原则是Barbara  Liskov首次在1988年写下的。所以就叫做Liskov替换原则。她如此写到：<br>&#8220;这里需要如下替换性质：若对每个类型S的对象o1，都存在一个类型T的对象o2，使得在所有针对T编写的程序P中，用o1替换o2后，程序P行为功能不变，则S是T的子类型。<br><br>LSP然我们得出一个非常重要的结论：一个模型，如果孤立的看，并不具有真正意义上的有效性。模型的有效性只能通过它的客户程序来表现。<br><br>在考虑一个特定设计是否恰当时，不能完全孤立的来看这个解决方案。必须要根据该设计的使用者所做出的合理假设来审视它。<br><br>有
谁知道设计的使用者会做出什么样的合理假设呢？大多数这样的假设都很难预测。事实上，如果试图去预测所有这些假设，我们所得到的系统很可能会充满不必要的
复杂性的臭味。因此，像所有其它原则一样了，通常最好的办法就是只预测那些最明显的对于LSP的违反情况，而推迟所有其它的预测，直到出现相关的脆弱性的
臭味时，才去处理它们。<br><br><font color="#ee1d24">IS-A是关于行为的。</font><br>LSP清晰的指出，OOD中IS－A关系是就行为方式而言的，行为方式是可以进行合理假设的，是客户程序所依赖的。<br><br><font color="#ee1d24">基于契约设计</font><br>基
于契约设计（DBC：Design By
Contract）。使用DBC，类的编写者能够显式的规定针对该类的契约。客户代码的编写者可以通过该契约获悉可以依赖的行为方式。契约是通过为每个方
法声明的前置条件（preconditions)和后置条件（postconditions)来指定的。要使一个方法得以执行，前置条件必须要为真。执行
完毕后，该方法要保证后置条件为真。<br><br><font color="#ee1d24">在单元测试中指定契约</font><br>也可以通过编写单元测试的方式来指定契约。客户代码编写者会去查看这些单元测试，这样他们就可以知道对于要使用的类，应该做什么合理的假设。<br><br><font color="#ee1d24">启发式规则和习惯用法</font><br><br>1.派生类中的退化函数<br>&nbsp;  在基类中实现了f()方法，在派生类中的函数f()就是退化的，派生类中的退化函数并不总表示为违反LSP，但是当存在这种情况时，还是值得注意一下的。<br>2.从派生类中抛出异常<br>&nbsp;  在派生类的方法中添加了其基类不会抛出的异常。如果基类的使用者不期望这些异常，那么把它们添加到派生类的方法中就会导致不可替换性。此时要遵循LSP，要么就必须改变使用者的期望，要么派生类就不应该抛出这些异常。<br><br>总
结：OCP是OOD中很多原则的核心。如果这个原则应用的有效，应用程序就会具有更多的可维护性、可重用性以及健壮性。LSP是使OCP成为可能的主要原
则之一。正是子类型的可替换性才使得使用基类类型的模块在无需修改的情况下就可以扩展。这种可替换性必须是开发人员可以隐式依赖的。因此，如果没有显式的
强制基类类型的契约，那么代码就必须良好的并且明显的表达出这一点。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  术语IS-A的含义过于广泛以至于不能作为子类型的定义。子类型的正确定义是&#8220;可替换性&#8221;，这里的可替换性可以通过显式或者隐式的契约来定义。</div>
</div>
</span>
<br>
<div style="font-size: 14px; line-height: 25px;"><strong>作者Blog：</strong><a  href="http://blog.csdn.net/nirvana_li/" id="ArticleContent1_ArticleContent1_AuthorBlogLink" target="_blank">http://blog.csdn.net/nirvana_li/</a></div><img src ="http://www.cppblog.com/richardhe/aggbug/49813.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-05-14 13:21 <a href="http://www.cppblog.com/richardhe/articles/49813.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>理解: 面向对象的设计原则与设计模式</title><link>http://www.cppblog.com/richardhe/articles/49794.html</link><dc:creator>RichardHe</dc:creator><author>RichardHe</author><pubDate>Wed, 14 May 2008 02:37:00 GMT</pubDate><guid>http://www.cppblog.com/richardhe/articles/49794.html</guid><wfw:comment>http://www.cppblog.com/richardhe/comments/49794.html</wfw:comment><comments>http://www.cppblog.com/richardhe/articles/49794.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardhe/comments/commentRss/49794.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardhe/services/trackbacks/49794.html</trackback:ping><description><![CDATA[来自http://www.cppblog.com/cool-liangbing/archive/2008/05/13/49729.html<br>记得2004年刚接触设计模式，买了经典的&lt;&lt;设计模式&gt;&gt;一书，细细地阅读，然后在开发中模仿。一两年时间过去，对23种设计
模式弄得还算比较熟悉，也在软件设计中能用则用，比如Singleton, template method, proxy,
facade等等。但总感觉用的不爽，当时也说不出原因；就是感觉在使用的过程中，是一种为了使用设计模式而使用上他们，有时候是生搬硬套。总之，自己当
时是搞不清楚为什么要使用设计模式，停留在别人说它牛，我就学着用而不落人之后。 <br>&nbsp;&nbsp;&nbsp; 我不是一个天质聪颖的人，对软件设计的理解，基本上无法评自己能力单独领悟出来。只有常常督促自己多买国内外软件专家写的好书，来学习他们在这些方面的发现和总结。靠后天学习来弥补先天不足，也是没有办法中的办法。<br>&nbsp;&nbsp;&nbsp;
终于在2007年看到了&lt;&lt;java与模式&gt;&gt;，
书中对设计模式的讨论，并没有特别吸引我的地方，不过是用java语言来详细讲解23种模式而已，最多增加一些变体。深深吸引我的是"第二部分
面向对象的设计原则"，
这一部分虽然篇幅不多，但清晰地说明了我们为什么要用设计模式，使用设计模式是来解决什么问题的，使用之后我们要达到什么效果。软件的生命周期让我们认识
到，面向对象的设计要解决的核心问题是可维护性和可复用性，特别是可维护性，一个好软件的维护成本远远大于初期开发成本。<br>&nbsp;&nbsp;&nbsp;
一个软件设计的可维护性很差，原因在于：过于僵硬、过于脆弱、复用率低、黏度过高。相反，一个好的系统设计应该是可扩展的、灵活的、可插入的。在软件发达
国家如美国，一些软件界的高手，在20世纪80~90年代，就陆续提出一些设计原则，这些设计原则是在提高一个系统的可维护性的同时，提高系统的可复用性
的指导原则：<br>&nbsp;&nbsp;&nbsp; 1、开闭原则：软件架构应该是对扩展开发，对修改关闭<br>&nbsp;&nbsp;&nbsp; 2、Liskov替换原则：任何基类可以出现的地方，子类一定可以出现<br>&nbsp;&nbsp;&nbsp; 3、依赖倒转原则：要依赖于抽象，不要依赖于实现<br>&nbsp;&nbsp;&nbsp; 4、接口隔离原则：应当为客户提供尽可能小的接口，而不是提供大的接口。<br>&nbsp;&nbsp;&nbsp; 5、组合、聚合复用原则：要尽量使用组合、聚合，而不是继承关系以达到复用的目的。<br>&nbsp;&nbsp;&nbsp; 6、Demeter法则：一个软件实体应该与尽可能少的其他实体发生互相作用。<br>&nbsp;&nbsp;&nbsp;
可以说，&lt;&lt;java与模式&gt;&gt;里很好的解决了我心中两三年来的不快，让我真正理解了为什么要使用设计模式。软件开发、设计原则
与设计模式的关系，我不恰当的比喻为战争、战略与战术的关系；要取得战争的全面胜利，你首先要在战略上把握好，然后才是战术上指挥好；同样，要开发出好的
软件，我们首先要遵循一定的设计原则，为了达到我们的目的，在开发中我们就恰当的使用相应的设计模式。<br>&nbsp;&nbsp;&nbsp; &lt;&lt;java与模式&gt;&gt;写的好，而且是中国人写的，但它缺少了一个方面的描述：我们怎样在实际的设计开发中做到这些呢？是不是一开始就要遵循这些设计原则，就要使用设计模式？还是有一个什么样的过程？<br>&nbsp;&nbsp;&nbsp;
终于今年初看到了英文版&lt;&lt;敏捷软件开发：原则、模式与实践&gt;&gt;，
国外这些大牛就是大牛，在这方面就是理解深刻，实践经验丰富。作者的观点很有点唯物辨证法，就是软件设计开始时，我们如果没有看出抽象的必要，可以先实现
一个简单的，当第一次被需求触发而显现出抽象的必要时，我们这时机会就来了，需要很快提取抽象接口，遵循以上设计原则。当然，作者还有很多其它好的思想，
这里不一一列举。<br>&nbsp;&nbsp;&nbsp; 认识和理解都需要一个过程，没有理论，我们这些智商一般的人是很难仅仅在项目开发中尽快提高的；同样，光看书不实践，我们也很难真正理解这些别人总结的经验，那将是雾里看花。<br>&nbsp;&nbsp;&nbsp; 丰富经验和软件设计思想以及软件工程思想，对软件开发的重要性真的是如此重要，怪不得我们国家无法开发出大型的高质量的软件来。<br><br><img src ="http://www.cppblog.com/richardhe/aggbug/49794.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardhe/" target="_blank">RichardHe</a> 2008-05-14 10:37 <a href="http://www.cppblog.com/richardhe/articles/49794.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>