??xml version="1.0" encoding="utf-8" standalone="yes"?>久久久久久无码Av成人影院,久久无码一区二区三区少妇,欧美一区二区久久精品http://www.shnenglu.com/richardhe/category/6665.html学无止境!永远学下?zh-cnSun, 20 Jul 2008 02:55:46 GMTSun, 20 Jul 2008 02:55:46 GMT60介Boost.Regexhttp://www.shnenglu.com/richardhe/articles/56662.htmlRichardHeRichardHeSun, 20 Jul 2008 02:31:00 GMThttp://www.shnenglu.com/richardhe/articles/56662.htmlhttp://www.shnenglu.com/richardhe/comments/56662.htmlhttp://www.shnenglu.com/richardhe/articles/56662.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/56662.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/56662.html

?sup>1
    ? 则表辑ּQRegular expressionQ是一U在文本处理中经常用到的模式匚w的Ş式,可能许多用户熟悉Unix下的grep,sed,awk{工h者perl语言Q它 们都q泛地用C正则表达式。传l的C++用户q受限于用POSIX C API'sQPortable operateing system interface standard)来操作正则表辑ּQ而regex++已经提供了这些API'sQ虽然它不一定是使用POSIX C 正则表达式库的最好方法。比如regex++能处理宽字符字符Ԍwide character stringsQ,或者搜索、替换操作(在某U意义上cM于sed或perlQ,q些在传l的C 库中是不能实现的?br>cboost::reg_expression是regex++库中的关键类Q它表示“机器可读”的正则表辑ּQreg_expression 是在string的基之上构徏的,可以认ؓ它是一个具有string的功能,外加q个正则表达式算法所需要的状态机?br>像std::basic_string一P它提供了两个针对char和wchar_t的特化版本:

namespace boost{
template <class charT,
          class traits = regex_traits<charT>,
          class Allocator = std::allocator<charT> >
class basic_regex;
typedef basic_regex<char> regex;
typedef basic_regex<wchar_t> wregex;
}

    要知道regex++库到底有什么用Q可以试x们要写一个信用卡处理E序。信用卡通常?6位数字组成的 LQ其中每四位一l,用空格或q字号隔开。在这些信用卡L存入数据库之前,我们N不要验这些数字是否符合正格式么Qؓ了匹配Q何一个数字我? 可以用正则表辑ּ[0-9]Q数字串的宽度可以用[[:digit:]],当然q些是POSIX标准。在regex++和perl中可化ؓ\dQ注意许 多老的库們֐于硬~码到C-localeQ因此这不是什么问题)。下面的正则表达式可以检验信用卡L的格式?br>(\d{4}[- ]){3}\d{4}
    ()标记子表辑ּQ{4}表示重复4ơ。这仅是一个perl,awk,egrep的正则表辑ּ的例子。regex++也支持那些sed,grep用到的比较老的“基本”语法Q虽然它们很用刎ͼ除非你需要重用一些已有的基本正则表达式?/font>

    现在让我们把q个表达式置于C++代码中来验信用卡L的格式:

bool validate_card_format(const std::string s)
{
   static const boost::regex e("(
\\d{4}[- ]){3}\\d{4}");
   return regex_match(s, e);
}

    注意我们以前是如何将某些额外的{义序列(或者翻译成Q{义字W)d到表辑ּ的:要知道,正则表达式引擎处理{义字W前Q该转义字符只能?C++~译器识别一ơ,因此Q在C++代码中嵌入正则表辑ּ的{义字W必d写(写两ơ)?br>    q要注意C的编译器必须支持Koening lookup 2Q比如VC6׃支持Q,否则你需要附加一些boost::prefixes到某些函数引用中?br>    ? 些熟悉信用卡操作的hQ可能还会想C面的那种格式适合于h的阅读,q不表示|上信用卡系l要求的格式Q可能是16?5个没有间隔符的数字串Q。我们需 要一U简单的转换ҎQ我们需?个字W串Q?个是正则表达式,一个是格式字符Ԍ提供一U被匚w的内容的描述Q。在regex++中,search? replace都能完成regex_merge法Q对于信用卡的例子我们给Z下面2个算法用于格式{换:

// match any format with the regular expression:
const boost::regex e("
\\A(\\d{3,4})[- ]?(\\d{4})[- ]?(\\d{4})[- ]?(\\d{4})\\z");
const std::string machine_format("
\\1\\2\\3\\4");
const std::string human_format("
\\1-\\2-\\3-\\4");

std::string machine_readable_card_number(const std::string s)
{
   return regex_merge(s, e, machine_format, boost::match_default | boost::format_sed);
}

std::string human_readable_card_number(const std::string s)
{
   return regex_merge(s, e, human_format, boost::match_default | boost::format_sed);
}


    q儿Q我们用正则表达式中的子式把L分ؓ4块,format string 用类似sed的语法把被匹配的内容替换定格式?br>    上面的例子中Q我们还没有直接操作匚wl果Q匹配结果包括全体匹配和一些子式的匚w。当需要正则表辑ּ的匹配结果时Q就需要用到class match_results的实例,下面是常用类型的特化版本Q?br>
namespace boost{
typedef match_results<const char*> cmatch;
typedef match_results<const wchar_t*> wcmatch;
typedef match_results<std::string::const_iterator> smatch;
typedef match_results<std::wstring::const_iterator> wsmatch;
}

    regex_search和regex_grep法都用到match_result?br>    注意q些法q不局限于一般的C-strings,M双向q代器(bidirectional iteratorQ类型都能被搜烦Q这为无~搜索Q何类型数据提供了可能性?/font>

    对于那些不喜Ƣ模板(templatesQ的可以使用class RegEx,它是Ҏ板代码的高层ơ包装,它ؓ那些使用不到库的整个功能的h提供了简单的接口Q当然它仅支持窄字符Qnarrow characterQ和“扩展”的正则表辑ּ语法?br>    对于惛_容POSIX的h可以使用POSIXAPI函数Qregcomp,regexec,regfree,regerror,q些对narrow character和Unicode都适用?/font>

    最后注意,q个库现在支持运行时本地化(run-time localizationQ,它能完全识别POSIX正则表达式语法,包括一些多字符的元素比较和同等cd的高U特性,它还能兼容其它一些正则表辑ּ库包括GNU、BSD4的regex包?/font>

安装和配|?/strong>

    首先当你从zip文g解压本库时必M留它的内部结构,如果你没q样做,那只好删除你解压的文Ӟ重现来一ơ?br>    ׃支持大多数常见的~译?标准?q_Q这个库不需要作使用前的配置。如果你到配置问题Q或x试你的编译器的配|信息,可以参?配置文档Q这和boost的其它所有的库的处理q程都一P?/font>

    ׃本库混合了模板代码(头文件中Q和静态代码数据(cpp文g中)Q所以在你用之前,必须库支持的代?生成到库内和档案文g中。以下是几个具体q_的操作步骤:
Borland C++ Builder
?/font>
Microsoft Visual C++ 6 and 7
如果你用VC5Q你可能要找一下本库的以前版本?br>打开命o提示W(其MSVC环境变量必须已定义,如果没有可运行Vcvars32.batQ位?lt;VC6>\binQ,q入<boost>\libs\regex\build 目录
选择正确的makefile,VC6++的是vc6.mak,支持STLPort的是vc6-stlport.mak
如下调用
nmake -fvc6.mak
如果惛_VC6子目录包含所有的lib、dll文gQlib文g拷在<VC6>\libQdll文g拷在<VC6>\binQ可使用
nmake -fvc6.mak install
删除生成q程中所有的临时文gQ可使用
nmake -fvc6.mak clean

最后只需d<boost>根目录到你的工程所包含的目录列表中。没有必要手动将*.lib文g加到工程中,因ؓ在头文g会选择正确?lib文g?br>注意Q如果想静态地铑օregex库,可定义BOOST_REGEX_STATIC_LINKQ在release版中生效Q?br>      如果想直接用源文gQ可定义BOOST_REGEX_NO_LIBQ这栯动选择库将失效?/font>

1.介出处:http://www.boost.org/libs/regex/doc/introduction.html
2.Koening lookupQWhen 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.

   


RichardHe 2008-07-20 10:31 发表评论
]]>
boost::regexhttp://www.shnenglu.com/richardhe/articles/56653.htmlRichardHeRichardHeSun, 20 Jul 2008 01:45:00 GMThttp://www.shnenglu.com/richardhe/articles/56653.htmlhttp://www.shnenglu.com/richardhe/comments/56653.htmlhttp://www.shnenglu.com/richardhe/articles/56653.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/56653.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/56653.html

boost::regex

  • boost::regex的用?/li>

        bool validate_card_format(const std::string s)
        {
           static const boost::regex e("(\\d{4}[- ]){3}\\d{4}");
           return regex_match(s, e);
        }

  • boost::regex的默认正则表辑ּ语法是perl语法

        boost::regex支持perl regular表达式、POSIX-Extended regular表达式和POSIX-Basic Regular表达式,但默认的表达式语法是perl语法Q如果要使用其余两种语法需要在构造表辑ּ的时候明指定?/p>

        例如Q下面两U方法效果相?br>        // e1 is a case sensitive Perl regular expression: 
        // since Perl is the default option there's no need to explicitly specify the syntax used here:
        boost::regex e1(my_expression);
        // e2 a case insensitive Perl regular expression:
        boost::regex e2(my_expression, boost::regex::perl|boost::regex::icase);

  • perl正则表达式语?/li>

        perl正则表达式语法可参见《perl语言入门》第7??章或boost的文档。这里列出的语法是不全面的,而且部分说明可能q不清楚?/p>

        . L字符;使用match_no_dot_null标志时不匚wNULL字符; 使用match_not_dot_newline时不匚w换行字符

        ^ 匚w行的开?br>        $ 匚w行的l束
        * 重复零次或则更多,例如a*b可匹配b,ab,aab,aaaaaaab
        + 重复一ơ以上,例如a+b可匹配ab,aab,aaaaaaaab。但不能匚wb?br>        ? 零次或则一ơ,例如ca?b匚wcb,cab但不匹被caab    
        a{n} 匚w字符'a'重复n?br>        a{n,}Q字Wa重复nơ以上(含nơ)
        a{n,m} a重复n到mơ(含)

        *?   匚w前一个原子零ơ以?br>        +?   匚w前一个原子一ơ以?br>        ??   匚w前一个原子零ơ以?br>        {n,}?  匚w前一个原子nơ以??
        {n,m?  匚w前一个原子n到m??

        | 或操作,例如ab(d|ef)匚wabd或则abef
        [] 字符集操作,例如[abc]匹配Q何单个字W?a'Q?b'Q?c'
        [a-d]Q表Ca、b、c、d
        ^否操作,例如[^a-c]表示a至c之外的所有字W?br>

  • boost::regex对unicode~码的支?/li>

        boost::regex使用ICU来实现对unicode及unicode变种的支持,q需要在~译boost的时候指出是否 用ICU以及ICU所在的目录。否则编译出来的boost::regex不支持unicode~码。其中boost::wregex支持unicode~? 码的搜烦Q如果要搜烦UTF-8、UTF-16、UFT-32~码的字W串Q则要用boost::u32regex。注意boost::wregex只能 支持unicode~码Q不能支持uft~码?/p>

  • 搜烦时如何忽略大写

        如果要在搜烦时忽略大写Q即大小写不敏感Q,则要用到表达式选项boost::regex::icaseQ例 如: boost::regex e2(my_expression, boost::regex::perl|boost::regex::icase);



RichardHe 2008-07-20 09:45 发表评论
]]>
C/C++中宏的?/title><link>http://www.shnenglu.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.shnenglu.com/richardhe/articles/56328.html</guid><wfw:comment>http://www.shnenglu.com/richardhe/comments/56328.html</wfw:comment><comments>http://www.shnenglu.com/richardhe/articles/56328.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/richardhe/comments/commentRss/56328.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/richardhe/services/trackbacks/56328.html</trackback:ping><description><![CDATA[关于## ?#?@的用?br><br><br>## 是连接符?q接两个?##被称接符QconcatenatorQ,用来两个Tokenq接Z个Token。注意这里连接的对象是TokenpQ而不一定是宏的?<br>量。比如你要做一个菜单项命o名和函数指针l成的结构体的数l,q且希望在函数名和菜单项命o名之间有直观的、名字上的关pR那可以用:宏参?# <br>固定部分。当然还可以n?#W号q接 n+1个TokenQ这个特性也?W号所不具备的?<br>#define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d<br>typedef struct _record_type LINK_MULTIPLE(name,company,position,salary);<br>// q里q个语句展开为:<br>//      typedef struct _record_type name_company_position_salary;<br><br><br>#@功能是将其后面的宏参数进行字W化?br>#define makechar(x)  <a href="mailto:#@xa">#@x<br>a</a> = makechar(b);<br>//a = 'b';<br><br><br>#是把名字代替成字W串,宏体中,#的功能是其后面的宏参数q行字符串化操作QStringficationQ,单说是在对它所引用的宏变量通过替换后在其左叛_加上一?<br>双引受?br>#define WARN_IF(EXP)        \<br>        do{ if (EXP)        \<br>                fprintf(stderr, "Warning: " #EXP "\n"); }       \<br>        while(0)<br>那么实际使用中会出现下面所C的替换q程Q?<br>WARN_IF (divider == 0);<br>被替换ؓ<br>do {<br>        if (divider == 0)<br>fprintf(stderr, "Warning" "divider == 0" "\n");<br>} while(0);<br>q样每次dividerQ除敎ͼ?的时候便会在标准错误上输出一个提CZ息?<br><br><br> <font style="font-size: 12pt;" face="#ce_temp_font#"><dt><strong>!IF </strong><span id="euaoqow" class="parameter">constantexpression</span> </dt><dd> <p>如果 <span id="gcgssku" class="parameter">constantexpression</span> 计算l果为非零|则处?<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> 块的l尾。同一行上 <strong>!ENDIF</strong> 后面的所有文本被忽略?/p> </dd></font><img src ="http://www.shnenglu.com/richardhe/aggbug/56328.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/richardhe/" target="_blank">RichardHe</a> 2008-07-16 18:10 <a href="http://www.shnenglu.com/richardhe/articles/56328.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>extern "C"http://www.shnenglu.com/richardhe/articles/55817.htmlRichardHeRichardHeThu, 10 Jul 2008 09:27:00 GMThttp://www.shnenglu.com/richardhe/articles/55817.htmlhttp://www.shnenglu.com/richardhe/comments/55817.htmlhttp://www.shnenglu.com/richardhe/articles/55817.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/55817.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/55817.html前些天,~程序是用到了很久以前写的CE序Q想把里面的函数利用hQ连接发现出C找不到具体函数的错误Q?/p>

以下是假设旧的CE序?/p>

C的头文g

/*-----------c.h--------------*/
#ifndef _C_H_
#define _C_H_
extern int add(int x, int y);
#endif

C的源文g

/*-----------c.c--------------*/
int
add(int x, int y){
return
x+y;
}

C++的调?/p>

/*-----------cpp.cpp--------------*/
#include "c.h"
void main()
{

add(1, 0);
}

q样~译会生错误cpp.obj : error LNK2001: unresolved external symbol "int __cdecl add(int,int)" (?add@@YAHHH@Z)Q原因是找不到add的目标模?/p>

q才令我惌vC++重蝲的函数命名方式和C函数的命名方式,让我们回一下:C中函数编译后命名会在函数名前加以"_",比如add函数~译成obj文g时的实际命名为_addQ而c++命名则不同,Z实现函数重蝲同样的函数名add因参数的不同会被~译成不同的名字

例如

int add(int , int)==>add@@YAHHH@Z,

float add(float , float )==>add@@YAMMM@Z,

以上是VC6的命名方式,不同的编译器会不同,M不同的参数同L函数名将~译成不同目标名Q以便于函数重蝲是调用具体的函数?/p>

~译cpp.cpp中编译器在cpp文g中发现add(1, 0);的调用而函数声明ؓextern int add(int x, int y);~译器就军_Ladd@@YAHHH@ZQ可惜他找不刎ͼ因ؓC的源文g?font color="#990000">extern int add(int x, int y);~译成_add了;

Z解决q个问题C++采用了extern "C",q就是我们的主题Q想要利用以前的CE序库,那么你就要学会它Q我们可以看以下标准头文件你会发玎ͼ很多头文仉有以下的l构

#ifndef __H
#define __H
#ifdef __cplusplus
extern "C" {
#endif

extern
int f1(int, int);
extern
int f2(int, int);
extern
int f3(int, int);


#ifdef __cplusplus
}
#endif

#endif /*__H*/

如果我们仿制该头文g可以得到

#ifndef _C_H_
#define _C_H_
#ifdef __cplusplus
extern "C" {
#endif

extern
int add(int, int);

#ifdef __cplusplus
}
#endif

#endif /* _C_H_ */

q样~译

/*-----------c.c--------------*/
int
add(int x, int y){
return
x+y;
}

q时源文件ؓ*.cQ?/font>__cplusplus没有被定义,extern "C" {}q时没有生效对于C他看到只?font color="#990000">extern int add(int, int);
add函数~译成_add(int, int);

而编译c++源文?/font>

/*-----------cpp.cpp--------------*/
#include "c.h"
void main()
{

add(1, 0);
}

q时源文件ؓ*.cpp,__cplusplus被定?对于C++他看到的?font color="#990000">extern "C" {extern int add(int, int);}~译器就会知?add(1, 0);调用的C风格的函敎ͼ׃知道去c.obj中找_add(int, int)而不?/strong>add@@YAHHH@ZQ?/font>

q也׃ؓ什么DLL中常看见extern "C" {}Q?/strong>windows是采用C语言~制他首先要考虑到C可以正确调用q些DLLQ而用户可能会使用C++?font color="#990000">extern "C" {}׃发生作用


RichardHe 2008-07-10 17:27 发表评论
]]>推荐--丰富的游戏开发的站点http://www.shnenglu.com/richardhe/articles/55714.htmlRichardHeRichardHeWed, 09 Jul 2008 07:11:00 GMThttp://www.shnenglu.com/richardhe/articles/55714.htmlhttp://www.shnenglu.com/richardhe/comments/55714.htmlhttp://www.shnenglu.com/richardhe/articles/55714.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/55714.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/55714.html
http://www.gameres.com     属于游戏人的中文|络q_
http://www.chinagamedev.net    中国游戏开发?br> http://www.gamedevelop.net    中国游戏开发技术资讯网 http://www.ogdev.net     中国|游研发中心
http://www.chaosstars.com    中国游戏开发基?br> http://gamedev.91.com    游戏制作联盟
http://www.chinadv.com    中国数码视频在线
http://www.u9u6.com     u9u6游戏|?br> http://www.amanpage.com    游戏开发站?br> http://www.gamecollege.org    游戏学院

手机随便列的Q?br> http://soft.yesky.com/SoftChannel/72348977504190464/20031215/1753502.shtml Java   手机游戏开发专?br>
国外站点Q?br> http://www.gdse.com    Game Development Search Engine
http://www.gamedev.net
http://www.igda.org    International Game Developers Association
http://www.gameinstitute.com   Game Institute
http://www.gametutorials.com   Game Tutorials
http://www.sagamedev.co.za    South African Game Development
http://lgdc.sunsite.dk    Linux Game Development Center

个h的及专题的:
http://gd.91.com/zt/ogre/index.asp      OGRE引擎研究?br> http://www.gameres.com/Topics/Technic/OGRE      OGRE引擎教程

RichardHe 2008-07-09 15:11 发表评论
]]>
用Boost.Spirit写了一个term下c++词法高亮http://www.shnenglu.com/richardhe/articles/55612.htmlRichardHeRichardHeTue, 08 Jul 2008 03:49:00 GMThttp://www.shnenglu.com/richardhe/articles/55612.htmlhttp://www.shnenglu.com/richardhe/comments/55612.htmlhttp://www.shnenglu.com/richardhe/articles/55612.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/55612.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/55612.html

1.1版,命o行下使用?.0版,

    增加更易用的ҎQ点击coco.exeQ将文g拖入H口Q高亮文件输出到同名txt文g?/font>

    转成html的功能以后不懒的时候再写吧


1.0版:命o行下使用Q输出到stdoutQ?/font>

可以重定向到文gQ彩色复制就行了
coco.exe xxx.cpp > whateverfile
C本打开whateverfileQ复Ӟ在bbs上彩色粘?br> ~译q个大概需?癑֤M内存

想直接现在用的,可以在这下蝲Q这是水木的附gQ不知道能有效到什么时?/font>

http://www.newsmth.net/att.php?p.335.193566.17417.exe

论坛可以用它来实C码高亮显C?br> 以后再改q?/font>

正确的代码coco应该不会分析错,能分析正的不一定是正确的代?/font>

M码可以先看spirit最基础的ch_p,str_p{,q有spirit文档中的utility

再看如何分析文g

http://hi.baidu.com/yesbaba/blog/item/091ca995d0fe6e49d1135e8b.html

gnu source highlight可以实现这个功能了Q我做这个一来是学spiritl练手,

二来是要做个评设计Q三来这个很实用Q^常在水木帖代码也能用的上

///////////////////////////////////////////////////////////////////////////////
//
//       highlight c++ source code in ansi code format
//       COCO (COlorful COde) version 1.1
//       ibe@newsmth.net
//       [ 2007-08-13 ]
//
///////////////////////////////////////////////////////////////////////////////

#include <boost/spirit/core.hpp>
#include <boost/spirit/iterator/file_iterator.hpp>
#include <boost/spirit/utility/confix.hpp>
#include <boost/spirit/utility/escape_char.hpp>
#include <iostream>
#include <fstream>
///////////////////////////////////////////////////////////////////////////////
using namespace boost::spirit;
using namespace std;

////////////////////////////////////////////////////////////////////////////
//
//       Types
//types needed for file parsing
////////////////////////////////////////////////////////////////////////////
typedef char char_t;
typedef file_iterator < char_t > iterator_t;
typedef scanner < iterator_t > scanner_t;
typedef rule < scanner_t > rule_t;

////////////////////////////////////////////////////////////////////////////
//To ansiQaction函数Q匹配后输出成ansi代码的彩色控制符Q?/font>
//可参考各大高校bbs的asciiart版精华区Q可以输出成需要的格式Q如html
//
////////////////////////////////////////////////////////////////////////////
ofstream outfile;
namespace to_ansi
{
  void
   black (iterator_t first, iterator_t const &last)
   {
     outfile << "\033[1;30m";
    while (first != last)
       outfile << *first++;
     outfile << "\033[m";
   }

  void
   red (iterator_t first, iterator_t const &last)
   {
     outfile << "\033[1;31m";
    while (first != last)
       outfile << *first++;
     outfile << "\033[m";
   }

  void
   green (iterator_t first, iterator_t const &last)
   {
     outfile << "\033[1;32m";
    while (first != last)
       outfile << *first++;
     outfile << "\033[m";
   }

  void
   yellow (iterator_t first, iterator_t const &last)
   {
     outfile << "\033[1;33m";
    while (first != last)
       outfile << *first++;
     outfile << "\033[m";
   }

  void
   blue (iterator_t first, iterator_t const &last)
   {
     outfile << "\033[1;34m";
    while (first != last)
       outfile << *first++;
     outfile << "\033[m";
   }

  void
   magenta (iterator_t first, iterator_t const &last)
   {
     outfile << "\033[1;35m";
    while (first != last)
       outfile << *first++;
     outfile << "\033[m";
   }

  void
   cyan (iterator_t first, iterator_t const &last)
   {
     outfile << "\033[1;36m";
    while (first != last)
       outfile << *first++;
     outfile << "\033[m";
   }

  void
   white (iterator_t first, iterator_t const &last)
   {
     outfile << "\033[1;37m";
    while (first != last)
       outfile << *first++;
     outfile << "\033[m";
   }

  void
   echo (iterator_t first, iterator_t const &last)
   {
    while (first != last)
       outfile << *first++;
   }
}
////////////////////////////////////////////////////////////////////////////
//
//       cpp lex
//c++的词法描qͼ有了comment_p方便多了,识别函数名还没实?/font>
////////////////////////////////////////////////////////////////////////////
namespace cpp_lex
{
     rule_t comment = comment_p ("/*", "*/")
                     | comment_p ("http://")
                     ;
     rule_t whitespace = space_p
                     ;
     rule_t include = str_p ("#include") >> *space_p >>
                     (comment_p ("<", ">") | comment_p ("\"", "\""))
                     ;
     rule_t preprocessor = (include | "##" | "#define" | "#error" | ("#if" >> space_p)
                         | "#ifdef" | "#ifndef" | "#else" | "#elif"
                         | "#endif" | "#line" | "#pragma" | "#undef" | "#"
                         | "__LINE__" | "__FILE__" | "__DATE__" | "__TIME__"
                         | "_cplusplus" | "__STDC__")
                         >> space_p
                         ;
     rule_t keyword_ = str_p ("asm") | "auto" | "bool" | "break" | "case"
                     | "catch" | "char" | "class" | "const" | "const_cast"
                     | "continue" | "default" | "delete" | "do" | "double"
                     | "dynamic_cast" | "else" | "enum" | "explicit"
                     | "extern" | "false" | "float" | "for" | "friend"
                     | "goto" | "if" | "inline" | "int" | "long" | "mutable"
                     | "namespace" | "new" | "operator" | "private"
                     | "protected" | "public" | "register" | "reinterpret_cast"
                     | "return" | "short" | "signed" | "sizeof" | "static"
                     | "static_cast" | "struct" | "switch" | "template"
                     | "this" | "throw" | "true" | "try" | "typedef" | "typeid"
                     | "typename" | "union" | "unsighed" | "using" | "virtual"
                     | "void" | "volatile" | "wchar_t" | "while"
                     ;
     rule_t keyword = keyword_ >> space_p
                     ;
     rule_t identifer = (alpha_p | '_') >> *(alnum_p | '_')
                     ;
         rule_t operators = punct_p - '`' - '@' - '$' - '\\'
                         ;
     rule_t number = real_p
                     ;
     rule_t str = confix_p ("\"", *c_escape_ch_p, "\"")
                     ;
     rule_t charcter = confix_p("\'", *c_escape_ch_p, "\'")
                     ;
     rule_t constant = number
                     | str
                     | charcter
                     ;

};



////////////////////////////////////////////////////////////////////////////
//
//       Main program
//
////////////////////////////////////////////////////////////////////////////
int
main (int argc, char *argv[])
{
         string filepath;
        if (2 > argc)
         {//把要处理的文件拖到窗口内
                 cout << "drag file to this windows\n";
                 string filepath_input;
                 getline(cin, filepath_input);
                 filepath_input.erase(filepath_input.end()-1); //Ll尾?\"'
                 filepath_input.erase(filepath_input.begin());   //L开?
                for (int i = 0; filepath_input[i] != 0; i++) {
                         filepath.push_back(filepath_input[i]);
                        if (filepath_input[i] == '\\')
                                 filepath.push_back('\\');
                 }
         }else{
                // for console usage
                 filepath = argv[1];
         }
         iterator_t first (filepath);
        if (!first)   {
                 std::cerr << "Unable to open file!\n";
                return -1;
         }

        // Create an EOF iterator
         iterator_t last = first.make_end ();

         string filepath_output = filepath+".txt";
         outfile.open(filepath_output.c_str());
        // A simple rule词法对应的颜色在q里改就可以
         rule_t r = *(
                 cpp_lex::comment[&to_ansi::cyan]
                 | cpp_lex::constant[&to_ansi::yellow]
                 | cpp_lex::preprocessor[&to_ansi::red]
                 | cpp_lex::keyword[&to_ansi::green]
                 | cpp_lex::whitespace[&to_ansi::echo]
                 | cpp_lex::operators[&to_ansi::magenta]
                 | cpp_lex::identifer[&to_ansi::white]
                 )
                 ;
        //  
        // Parse
        /*The parse_info structure
         The functions above return a parse_info structure parameterized by the iterator type passed in.
         The parse_info struct has these members:parse_info
         stop     Points to the final parse position (i.e The parser recognized and processed the input up to this point)
         hit     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.
         full     True when we have a full match (i.e The parser consumed all the input).
         length     The number of characters consumed by the parser. This is valid only if we have a successful match (either partial or full).
        */
         parse_info < iterator_t > info = parse (first, last, r);

        // This really shouldn't fail...
        if (info.full)
                 std::cout << "\nParse succeeded!\n";
        else
                 std::cout << "\nParse failed!\n";
        
        std::cout << "highlight file saved in " << filepath_output << endl;
         string end;
         getline (cin, end);//按回车键推出H口

        return 0;
}


q只是个词法高亮E序Q还不能识别语法Q如函数{,用正则也能做Q但boost.spirit更简?/font>




RichardHe 2008-07-08 11:49 发表评论
]]>
内存分区http://www.shnenglu.com/richardhe/articles/55395.htmlRichardHeRichardHeSat, 05 Jul 2008 05:39:00 GMThttp://www.shnenglu.com/richardhe/articles/55395.htmlhttp://www.shnenglu.com/richardhe/comments/55395.htmlhttp://www.shnenglu.com/richardhe/articles/55395.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/55395.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/55395.html

五大内存分区
    在C++中,内存分成5个区Q他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区?br>    栈,是那些q译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等?br>    堆,是那些由new分配的内存块Q他们的释放~译器不ȝQ由我们的应用程序去控制Q一般一个newp对应一个delete。如果程序员没有释放掉,那么在程序结束后Q操作系l会自动回收?br>    自由存储区,是那些由malloc{分配的内存块,他和堆是十分怼的,不过它是用free来结束自q生命的?br>    全局/静态存储区Q全局变量和静态变量被分配到同一块内存中Q在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有q个区分了,他们共同占用同一块内存区?br>    帔R存储区,q是一块比较特D的存储区,他们里面存放的是帔RQ不允许修改Q当Ӟ你要通过非正当手D也可以修改Q而且Ҏ很多Q?br>明确区分堆与?br>    在bbs上,堆与栈的区分问题Q似乎是一个永恒的话题Q由此可见,初学者对此往往是؜淆不清的Q所以我军_拿他W一个开刀?br>    首先Q我们D一个例子:
    void f() { int* p=new int[5]; }
    q条短短的一句话包含了堆与栈,看到newQ我们首先就应该惛_Q我们分配了一块堆内存Q那么指针p呢?他分配的是一块栈内存Q所以这句话的意思就是: 在栈内存中存放了一个指向一块堆内存的指针p。在E序会先定在堆中分配内存的大小Q然后调用operator new分配内存Q然后返回这块内存的首地址Q放入栈中,他在VC6下的汇编代码如下Q?br>    00401028   push        14h
    0040102A   call        operator new (00401060)
    0040102F   add         esp,4
    00401032   mov         dword ptr [ebp-8],eax
    00401035   mov         eax,dword ptr [ebp-8]
    00401038   mov         dword ptr [ebp-4],eax
    q里Q我们ؓ了简单ƈ没有释放内存Q那么该怎么去释攑֑Q是delete p么?澻I错了Q应该是delete []pQ这是ؓ了告诉编译器Q我删除的是一个数l,VC6׃Ҏ相应的Cookie信息去进行释攑ֆ存的工作?br>    好了Q我们回到我们的主题Q堆和栈I竟有什么区别?
    主要的区别由以下几点Q?br>    1、管理方式不同;
    2、空间大不同;
    3、能否生碎片不同;
    4、生长方向不同;
    5、分配方式不同;
    6、分配效率不同;
    理方式Q对于栈来讲Q是q译器自动理Q无需我们手工控制Q对于堆来说Q释攑ַ作由E序员控ӞҎ产生memory leak?br>    I间大小Q一般来讲在32位系l下Q堆内存可以辑ֈ4G的空_从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲Q一般都是有一定的I间大小的,例如Q在VC6下面Q默认的栈空间大是1MQ好像是Q记不清楚了Q。当Ӟ我们可以修改Q?nbsp;  
    打开工程Q依ơ操作菜单如下:Project->Setting->LinkQ在Category 中选中OutputQ然后在Reserve中设定堆栈的最大值和commit?br>注意Qreserve最gؓ4ByteQcommit是保留在虚拟内存的页文g里面Q它讄的较大会使栈开辟较大的|可能增加内存的开销和启动时间?br>    片问题Q对于堆来讲Q频J的new/delete势必会造成内存I间的不q箋Q从而造成大量的碎片,使程序效率降低。对于栈来讲Q则不会存在q个问题Q? 因ؓ栈是先进后出的队列,他们是如此的一一对应Q以至于永远都不可能有一个内存块从栈中间弹出Q在他弹Z前,在他上面的后q的栈内容已l被弹出Q详l的 可以参考数据结构,q里我们׃再一一讨论了?br>    生长方向Q对于堆来讲Q生长方向是向上的,也就是向着内存地址增加的方向;对于栈来Ԍ它的生长方向是向下的Q是向着内存地址减小的方向增ѝ?br>    分配方式Q堆都是动态分配的Q没有静态分配的堆。栈?U分配方式:静态分配和动态分配。静态分配是~译器完成的Q比如局部变量的分配。动态分配由alloca函数q行分配Q但是栈的动态分配和堆是不同的,他的动态分配是q译器q行释放Q无需我们手工实现?br>    分配效率Q栈是机器系l提供的数据l构Q计机会在底层Ҏ提供支持Q分配专门的寄存器存放栈的地址Q压栈出栈都有专门的指o执行Q这决定了栈的效率? 较高。堆则是C/C++函数库提供的Q它的机制是很复杂的Q例如ؓ了分配一块内存,库函C按照一定的法Q具体的法可以参考数据结?操作pȝQ在? 内存中搜索可用的_大小的空_如果没有_大小的空_可能是由于内存碎片太多)Q就有可能调用系l功能去增加E序数据D늚内存I间Q这样就有机会分 到够大的内存Q然后进行返回。显Ӟ堆的效率比栈要低得多?br>    从这里我们可以看刎ͼ堆和栈相比,׃大量new/delete的用,Ҏ造成大量的内存碎片;׃没有专门的系l支持,效率很低Q由于可能引发用h? 和核心态的切换Q内存的甌Q代价变得更加昂c所以栈在程序中是应用最q泛的,q是函数的调用也利用栈d成,函数调用q程中的参数Q返回地 址QEBP和局部变量都采用栈的方式存放。所以,我们推荐大家量用栈Q而不是用堆?br>    虽然栈有如此众多的好处,但是׃和堆相比不是那么灉|Q有时候分配大量的内存I间Q还是用堆好一些?br>    无论是堆q是栈,都要防止界现象的发生(除非你是故意使其界Q,因ؓ界的结果要么是E序崩溃Q要么是摧毁E序的堆、栈l构Q生以想不到的l果,? 是在你的程序运行过E中Q没有发生上面的问题Q你q是要小心,说不定什么时候就崩掉Q那时候debug可是相当困难的:Q?br>    对了Q还有一件事Q如果有人把堆栈合v来说Q那它的意思是栈,可不是堆Q呵呵,清楚了?
static用来控制变量的存储方式和可见?br>       函数内部定义的变量,在程序执行到它的定义处时Q编译器为它在栈上分配空_函数在栈上分配的I间在此函数执行l束时会释放掉,q样׃生了一个问? 如果惛_函数中此变量的g存至下一ơ调用时Q如何实玎ͼ 最Ҏ惛_的方法是定义一个全局的变量,但定义ؓ一个全局变量有许多缺点,最明显的缺Ҏ破坏了此变量的访问范_使得在此函数中定义的变量Q不仅仅受此 函数控制Q?/p>

       需要一个数据对象ؓ整个c而非某个对象服务,同时又力求不破坏cȝ装?卌求此成员隐藏在类的内部,对外不可见?/p>

       static的内部机Ӟ
       静态数据成员要在程序一开始运行时必d在。因为函数在E序q行中被调用Q所以静态数据成员不能在M函数内分配空间和初始化?br>       q样Q它的空间分配有三个可能的地方,一是作为类的外部接口的头文Ӟ那里有类声明Q二是类定义的内部实玎ͼ那里有类的成员函数定义;三是应用E序的mainQ)函数前的全局数据声明和定义处?br>      静态数据成员要实际地分配空_故不能在cȝ声明中定义(只能声明数据成员Q。类声明只声明一个类?#8220;寸和规?#8221;Qƈ不进行实际的内存分配Q所以在cd 明中写成定义是错误的。它也不能在头文件中cd明的外部定义Q因为那会造成在多个用该cȝ源文件中Q对光复定义?br>      static被引入以告知~译器,变量存储在E序的静态存储区而非栈上I间Q静?br>数据成员按定义出现的先后序依次初始化,注意静态成员嵌套时Q要保证所嵌套的成员已l初始化了。消除时的顺序是初始化的反顺序?/p>

       static的优势:
       可以节省内存Q因为它是所有对象所公有的,因此Q对多个对象来说Q静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一P但它? 值是可以更新的。只要对静态数据成员的值更Cơ,保证所有对象存取更新后的相同的|q样可以提高旉效率?/p>

        引用静态数据成员时Q采用如下格式:
         <cd>::<静态成员名>
    如果静态数据成员的讉K权限允许的话(即public的成?Q可在程序中Q按上述格式
来引用静态数据成员?/p>

       PS:
      (1)cȝ静态成员函数是属于整个c而非cȝ对象Q所以它没有this指针Q这导?br>了它仅能讉Kcȝ静态数据和静态成员函数?br>      (2)不能静态成员函数定义ؓ虚函数?br>      (3)׃静态成员声明于cMQ操作于其外Q所以对其取地址操作Q就多少有些Ҏ
Q变量地址是指向其数据cd的指?Q函数地址cd是一?#8220;nonmember函数指针”?/p>       (4)׃静态成员函数没有this指针Q所以就差不多等同于nonmember函数Q结果就
产生了一个意想不到的好处Q成Z个callback函数Q得我们得以将C++和C-based X W
indowpȝl合Q同时也成功的应用于U程函数w上?br>      (5)staticq没有增加程序的时空开销Q相反她q羃短了子类对父c静态成员的讉K
旉Q节省了子类的内存空间?br>      (6)静态数据成员在<定义或说?gt;时前面加关键字static?br>      (7)静态数据成员是静态存储的Q所以必d它进行初始化?br>      (8)静态成员初始化与一般数据成员初始化不同:
      初始化在cM外进行,而前面不加staticQ以免与一般静态变量或对象相؜淆;
      初始化时不加该成员的讉K权限控制WprivateQpublic{;
           初始化时使用作用域运符来标明它所属类Q?br>           所以我们得出静态数据成员初始化的格式:
         <数据cd><cd>::<静态数据成员名>=<?gt;
      (9)Z防止父类的媄响,可以在子cd义一个与父类相同的静态变量,以屏蔽父cȝ影响。这里有一炚w要注意:我们说静态成员ؓ父类和子cd享,但我们有 重复定义了静态成员,q会不会引v错误呢?不会Q我们的~译器采用了一U绝妙的手法Qname-mangling 用以生成唯一的标志?br>


RichardHe 2008-07-05 13:39 发表评论
]]>
回调函数http://www.shnenglu.com/richardhe/articles/55396.htmlRichardHeRichardHeSat, 05 Jul 2008 05:39:00 GMThttp://www.shnenglu.com/richardhe/articles/55396.htmlhttp://www.shnenglu.com/richardhe/comments/55396.htmlhttp://www.shnenglu.com/richardhe/articles/55396.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/55396.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/55396.html

?br>
  对于很多初学者来_往往觉得回调函数很神U,很想知道回调函数的工作原理。本文将要解释什么是回调函数、它们有什么好处、ؓ什么要使用它们{等问题Q在开始之前,假设你已l熟知了函数指针?

  什么是回调函数Q?/strong>

  而言之,回调函数是一个通过函数指针调用的函数。如果你把函数的指针Q地址Q作为参C递给另一个函敎ͼ当这个指针被用ؓ调用它所指向的函数时Q我们就说这是回调函数?br>
  Z么要使用回调函数Q?/strong>

  因ؓ可以把调用者与被调用者分开。调用者不兛_谁是被调用者,所有它需知道的,只是存在一个具有某U特定原型、某些限制条Ӟ如返回gؓintQ的被调用函数?br>
如果想知道回调函数在实际中有什么作用,先假设有q样一U情况,我们要编写一个库Q它提供了某些排序算法的实现Q如冒排序、快速排序、shell? 序、shake排序{等Q但Z库更加通用Q不惛_函数中嵌入排序逻辑Q而让使用者来实现相应的逻辑Q或者,惌库可用于多种数据cdQint? float、stringQ,此时Q该怎么办呢Q可以用函数指针,q进行回调?br>
  回调可用于通知机制Q例如,有时要在E序中设|一? 计时器,每到一定时_E序会得到相应的通知Q但通知机制的实现者对我们的程序一无所知。而此Ӟ需有一个特定原型的函数指针Q用q个指针来进行回调, 来通知我们的程序事件已l发生。实际上QSetTimer() API使用了一个回调函数来通知计时器,而且Q万一没有提供回调函数Q它q会把一个消息发往E序的消息队列?br>
  另一个用回调机制的 API函数是EnumWindow()Q它枚D屏幕上所有的层H口Qؓ每个H口调用一个程序提供的函数Qƈ传递窗口的处理E序。如果被调用者返回一? |ql进行P代,否则Q退出。EnumWindow()q不兛_被调用者在何处Q也不关心被调用者用它传递的处理E序做了什么,它只兛_q回|因ؓ Zq回|它将l箋执行或退出?br>
  不管怎么_回调函数是l自C语言的,因而,在C++中,应只在与C代码建立接口Q或与已有的回调接口打交道时Q才使用回调函数。除了上q情况,在C++中应使用虚拟Ҏ或函数符QfunctorQ,而不是回调函数?br>
  一个简单的回调函数实现

下面创徏了一个sort.dll的动态链接库Q它导出了一个名为CompareFunction的类?-typedef int (__stdcall *CompareFunction)(const byte*, const byte*)Q它是回调函数的类型。另外,它也导出了两个方法:Bubblesort()和Quicksort()Q这两个Ҏ原型相同Q但实现了不? 的排序算法?br>
void DLLDIR __stdcall Bubblesort(byte* array,int size,int elem_size,CompareFunction cmpFunc);

void DLLDIR __stdcall Quicksort(byte* array,int size,int elem_size,CompareFunction cmpFunc);

  q两个函数接受以下参敎ͼ

  ·byte * arrayQ指向元素数l的指针QQ意类型)?br>
  ·int sizeQ数l中元素的个数?br>
  ·int elem_sizeQ数l中一个元素的大小Q以字节为单位?br>
  ·CompareFunction cmpFuncQ带有上q原型的指向回调函数的指针?br>
q两个函数的会对数组q行某种排序Q但每次都需军_两个元素哪个排在前面Q而函C有一个回调函敎ͼ其地址是作Z个参C递进来的。对~写者来_? 必介意函数在何处实现Q或它怎样被实现的Q所需在意的只是两个用于比较的元素的地址Qƈq回以下的某个|库的~写者和使用者都必须遵守q个U定Q:

  ·-1Q如果第一个元素较,那它在已排序好的数组中,应该排在W二个元素前面?br>
  ·0Q如果两个元素相{,那么它们的相对位|ƈ不重要,在已排序好的数组中,谁在前面都无所谓?

  ·1Q如果第一个元素较大,那在已排序好的数l中Q它应该排第二个元素后面?br>
  Z以上U定Q函数Bubblesort()的实现如下,Quicksort()q微复杂一点:

void DLLDIR __stdcall Bubblesort(byte* array,int size,int elem_size,CompareFunction cmpFunc)
{
 for(int i=0; i < size; i++)
 {
  for(int j=0; j < size-1; j++)
  {
   //回调比较函数
   if(1 == (*cmpFunc)(array+j*elem_size,array+(j+1)*elem_size))
   {
    //两个相比较的元素怺?br>    byte* temp = new byte[elem_size];
    memcpy(temp, array+j*elem_size, elem_size);
    memcpy(array+j*elem_size,array+(j+1)*elem_size,elem_size);
    memcpy(array+(j+1)*elem_size, temp, elem_size);
    delete [] temp;
   }
  }
 }
}

  注意Q因为实C使用了memcpy()Q所以函数在使用的数据类型方面,会有所局限?br>
  对用者来_必须有一个回调函敎ͼ其地址要传递给Bubblesort()函数。下面有二个单的CZQ一个比较两个整敎ͼ而另一个比较两个字W串Q?br>
int __stdcall CompareInts(const byte* velem1, const byte* velem2)
{
 int elem1 = *(int*)velem1;
 int elem2 = *(int*)velem2;

 if(elem1 < elem2)
  return -1;
 if(elem1 > elem2)
  return 1;

 return 0;
}

int __stdcall CompareStrings(const byte* velem1, const byte* velem2)
{
 const char* elem1 = (char*)velem1;
 const char* elem2 = (char*)velem2;
 return strcmp(elem1, elem2);
}

  下面另有一个程序,用于试以上所有的代码Q它传递了一个有5个元素的数组lBubblesort()和Quicksort()Q同时还传递了一个指向回调函数的指针?br>
int main(int argc, char* argv[])
{
 int i;
 int array[] = {5432, 4321, 3210, 2109, 1098};

 cout << "Before sorting ints with Bubblesort\n";
 for(i=0; i < 5; i++)
  cout << array[i] << '\n';

 Bubblesort((byte*)array, 5, sizeof(array[0]), &CompareInts);

 cout << "After the sorting\n";
 for(i=0; i < 5; i++)
  cout << array[i] << '\n';

 const char str[5][10] = {"estella","danielle","crissy","bo","angie"};

 cout << "Before sorting strings with Quicksort\n";
 for(i=0; i < 5; i++)
  cout << str[i] << '\n';

 Quicksort((byte*)str, 5, 10, &CompareStrings);

 cout << "After the sorting\n";
 for(i=0; i < 5; i++)
  cout << str[i] << '\n';

 return 0;
}

  如果惌行降序排序(大元素在先)Q就只需修改回调函数的代码,或用另一个回调函敎ͼq样~程h灉|性就比较大了?/p>

调用U定

  上面的代码中Q可在函数原型中扑ֈ__stdcallQ因为它以双下划U打_所 以它是一个特定于~译器的扩展Q说到底也就是微软的实现。Q何支持开发基于Win32的程序都必须支持q个扩展或其{h物。以__stdcall标识的函 C用了标准调用U定Qؓ什么叫标准U定呢,因ؓ所有的Win32 APIQ除了个别接受可变参数的除外Q都使用它。标准调用约定的函数在它们返回到调用者之前,都会从堆栈中U除掉参敎ͼq也是Pascal的标准约定。但 在C/C++中,调用U定是调用者负责清理堆栈,而不是被调用函数Qؓ强制函数使用C/C++调用U定Q可使用__cdecl。另外,可变参数函数也? C/C++调用U定?br>
  Windows操作pȝ采用了标准调用约定(PascalU定Q,因ؓ其可减小代码的体U。这点对早期的Windows来说非常重要Q因为那时它q行在只?40KB内存的电脑上?br>
  如果你不喜欢__stdcallQ还可以使用CALLBACK宏,它定义在windef.h中:

#define CALLBACK __stdcallor

#define CALLBACK PASCAL //而PASCAL在此?defined成__stdcall

  作ؓ回调函数的C++Ҏ

  因ؓqx很可能会使用到C++~写代码Q也怼惛_把回调函数写成类中的一个方法,但先来看看以下的代码Q?br>
class CCallbackTester
{
 public:
 int CALLBACK CompareInts(const byte* velem1, const byte* velem2);
};

Bubblesort((byte*)array, 5, sizeof(array[0]),
&CCallbackTester::CompareInts);

  如果使用微Y的编译器Q将会得C面这个编译错误:

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

  q是因ؓ非静态成员函数有一个额外的参数Qthis指针Q这迫使你在成员函数前面加上static。当Ӟq有几种Ҏ可以解决q个问题Q但限于幅Q就不再了?/p>



RichardHe 2008-07-05 13:39 发表评论
]]>
Boost.Singals 教程http://www.shnenglu.com/richardhe/articles/55334.htmlRichardHeRichardHeFri, 04 Jul 2008 05:23:00 GMThttp://www.shnenglu.com/richardhe/articles/55334.htmlhttp://www.shnenglu.com/richardhe/comments/55334.htmlhttp://www.shnenglu.com/richardhe/articles/55334.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/55334.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/55334.html阅读全文

RichardHe 2008-07-04 13:23 发表评论
]]>
[摘录]回调函数与{U表http://www.shnenglu.com/richardhe/articles/55333.htmlRichardHeRichardHeFri, 04 Jul 2008 05:18:00 GMThttp://www.shnenglu.com/richardhe/articles/55333.htmlhttp://www.shnenglu.com/richardhe/comments/55333.htmlhttp://www.shnenglu.com/richardhe/articles/55333.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/55333.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/55333.html

摘录?《C和指针?strong>
1.回调函数

q里有一个简单的函数,它用于在一个单链表中查找一个?它的参数是一个指向链表第一个节点的指针以及那个需要查扄?
Node* search_list(Node* node,int const value)

   while(node!=NULL)
  {  
    if(node->value==value)  break;
    node=node->link;
  }
  return node;
}
 q? 个函数看上去相当?但它只适用于gؓ整数的链?如果你需要在一个字W串链表中查?你不得不另外~写一个函?q个函数和上面那个函数的l大部分? 码相?只是W二个参数的cd以及节点值的比较Ҏ不同.一U更为通用的方法是查找函数与类型无?q样它就能用于Q何类型的值的链表,我们必须对函数的 两个斚wq行修改,使它与类型无?
首先我们必须改变比较的执行方?q样函数可以对Mcd的D行比?q个目标听上d象不可能,如果? ~写语句用于比较整型?它怎么q可能用于其他类型如字符串的比较?解决Ҏ是使用函数指针,调用者编写一个函?用于比较两个?然后把一个指向这 个函数的指针作ؓ参数传递给查找函数.然后查找函数调用q个函数来执行值的比较,使用q种Ҏ,Mcd的值都可以q行比较.我们必须修改的第二个斚w? 向函C递一个指向值的指针而不是本w?函数׃个void *形参,用于接收q个参数,然后指向q个值的指针便传递给比较函数,q个修改使字W串和数l对象也可以被?字符串和数组无法作ؓ参数传递给函数,但指 向它们的指针可以.
使用q种技巧的函数?回调函数"(callback function);因ؓ用户把一个函数指针作为参C递给其他函数,后者将"回调"用户的函?M时?如果你所~写的函数必能够在不同的时L? 不同cd的工作或执行只能由函数调用者定义的工作,你都可以使用q个技?许多H口pȝ使用回调函数q接多个动作,如拖拽鼠标和点击按钮来指定用L序中 的某个特定函?我们无法在这个上下文环境中ؓ回调函数~写一个准的原型,因ؓ我们q不知道q行比较的值的cd.事实?我们需要查扑և数能作用于Q? cd的?解决q个N的方法是把参数类型声明ؓ"void *",表示"一个指向未知类型的指针".


/***在一个单链表中查找一个指定值的函数,它的参数是一个指向链表第一个节?br>   **的指?一个指向我们需要查扄值的指针和一个函数指?它所指向的函?br>   **用于比较存储于此链表中的cd的?
*/
#include "node.h"
Node* search_list(Node *node,void  const *value, int(*compare)(void const*,void const*)) //函数声明;
{    
  while   (node!=NULL)
  {     
    if(compare(&node->value,value)==0)   break;
    node=node->link;
  }
  return node;
}
同时注意虽然函数不会修改参数node所指向的Q何节?但nodeq未声明为const。如果node被声明ؓconst,函数不得不返回一个constl果Q这限制调用程序,它便无法修改查找函数所扑ֈ的节炏V?br> 在一个特定的链表中进行查找时Q用户需要编写一个适当的比较函敎ͼq把指向该函数的指针和指向需要查扄值的指针传递给查找函数?br>例如Q下面是一个比较函敎ͼ它用于在一个整数链表中q行查找?br>int compare_ints(void const* a,void const* b)
{
    if(*(int*)a==*(int*)b)     return 0;
    else     return 1;
}
q个函数像下面q样使用Q?br>desired_node=search_list(root,&desired_value,compare_ints);

2.转换表(jump table)
 转移表最好用个例子来解释。下面的代码D取自一个程序,它用于实C个袖珍式计算器。程序的其他部分已经d两个敎ͼop1和op2Q和一个操作符Qoper)。下面的代码Ҏ作符q行试Q最后决定调用哪个函数?br>switch(oper)
{
  case ADD:   result=add(op1,op2);break;
  case SUB:    result=sub(op1,op2);break;
  case MUL:    result=mul(op1,op2);break;
  case DIV:     result=div(op1,op2);break;
  ......
}

      对于一个新奇的h上百个操作符的计器Q这条switch语句会非常之长。ؓ什么要调用函数来执行这些操作呢Q把具体操作和选择操作的代码分开是一U? 良好的设计方案。更为复杂的操作肯定以独立的函数来实现Q因为它们的长度可能很长。但即是简单的操作也可能具有副作用Q例如保存一个常量值用于以后的 操作?br>Z使用switch语句Q表C操作符的代码必L整数。如果它们是从零开始连l的整数Q我们可以用{换表来实现相同的d。{换表是一个函数指针数l?br>创徏一个{换表需要两个步骤。首先,声明q初始化一个函数指针数l。唯一需要留心之处就是确保这些函数的原型出现在这个数l的声明之前?/p>

double add(double,double);
double sub(double,double);
double mul(double,double);
double div(double,double);

double (*oper_func[])(double,double)={add,sub,mul,div,...}Q?/p>

      初始化列表中各个函数名的正确序取决于程序中用于表示每个操作W的整型代码。这个例子假定ADD?QSUB?QMUL?Q接下去以此cL?br>      W二个步骤是用下面这条语句替换前面整条switch语句Q?br>result=oper_func[oper](op1,op2);
oper从数l中选择正确的函数指针,而函数调用操作符执行这个函数?/p>



RichardHe 2008-07-04 13:18 发表评论
]]>MD5法的C++实现http://www.shnenglu.com/richardhe/articles/54567.htmlRichardHeRichardHeWed, 25 Jun 2008 09:12:00 GMThttp://www.shnenglu.com/richardhe/articles/54567.htmlhttp://www.shnenglu.com/richardhe/comments/54567.htmlhttp://www.shnenglu.com/richardhe/articles/54567.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/54567.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/54567.html阅读全文

RichardHe 2008-06-25 17:12 发表评论
]]>
ZC++有限状态机的实现技?调查报告)http://www.shnenglu.com/richardhe/articles/53134.htmlRichardHeRichardHeFri, 13 Jun 2008 07:01:00 GMThttp://www.shnenglu.com/richardhe/articles/53134.htmlhttp://www.shnenglu.com/richardhe/comments/53134.htmlhttp://www.shnenglu.com/richardhe/articles/53134.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/53134.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/53134.html阅读全文

RichardHe 2008-06-13 15:01 发表评论
]]>
有限状态机的实?/title><link>http://www.shnenglu.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.shnenglu.com/richardhe/articles/53131.html</guid><wfw:comment>http://www.shnenglu.com/richardhe/comments/53131.html</wfw:comment><comments>http://www.shnenglu.com/richardhe/articles/53131.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/richardhe/comments/commentRss/53131.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/richardhe/services/trackbacks/53131.html</trackback:ping><description><![CDATA[有限状态机QFinite State Machine或者Finite State Automata)是Y仉域中一U重要的工具Q很多东西的模型实际上就是有限状态机?br><br>最q看了一些游戏编EAI的材料,感觉游戏中的AIQ第一要说的就是有限状态机来实现精늚AIQ然后才是A*寻\Q其他学术界讨论比较多的经|络、模p控制等问题q不是很热?br><br>FSM的实现方式:<br>1Q?switch/case或者if/else<br>q无意是最直观的方式,使用一堆条件判断,会编E的人都可以做到Q对单小巧的状态机来说最合适,但是毫无疑问Q这L方式比较原始Q对庞大的状态机难以l护?br><br>2Q?状态表<br>l护一个二l状态表Q横坐标表示当前状态,U坐标表C入,表中一个元素存储下一个状态和对应的操作。这一招易于维护,但是q行旉和存储空间的代h较大?br><br>3Q?使用State Pattern<br>? 用State Pattern使得代码的维护比switch/case方式E好Q性能上也不会有很多的影响Q但是也不是100Q完。不qRobert C. Martin做了两个自动产生FSM代码的工Pfor java和for C++各一个,在http://www.objectmentor.com/resources/index上有免费下蝲Q这个工L输入是纯文本的状? 机描qͼ自动产生W合State Pattern的代码,q样developer的工作只需要维护状态机的文本描qͼ每必要冒引入bug的风险去l护code?br><br>4Q?使用宏定义描q状态机<br>一般来_C++~程中应该避免?defineQ但是这主要是因为如果用宏来定义函数的话Q很Ҏ产生q样那样的问题,但是巧妙的?q是能够产生奇妙的效果。MFC是使用宏定义来实现大的架构的?br>在实现FSM的时候,可以把一些繁琐无比的if/elseq有花括Ll合攑֜宏中Q这P在代码中可以3Q中状态机描述文本一样写Q通过~译器的预编译处理?Q一L效果Q我见过产生C代码的宏Q如果要产生C++代码Q己软MFC可以Q那么理Z也是可行的?img src ="http://www.shnenglu.com/richardhe/aggbug/53131.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/richardhe/" target="_blank">RichardHe</a> 2008-06-13 14:48 <a href="http://www.shnenglu.com/richardhe/articles/53131.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用C++标准库中文g对象,如何获得文g的大?/title><link>http://www.shnenglu.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.shnenglu.com/richardhe/articles/52241.html</guid><wfw:comment>http://www.shnenglu.com/richardhe/comments/52241.html</wfw:comment><comments>http://www.shnenglu.com/richardhe/articles/52241.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/richardhe/comments/commentRss/52241.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/richardhe/services/trackbacks/52241.html</trackback:ping><description><![CDATA[C++标准库中的文件流cL供的各种操作中没有直接获得正在操作的文g的大的函数。要获得文g大小得{个弯Q用如下的方?br>    假设我们有了一个已l打开的文件对象ifile?br>    先将文g内的位置指针Ud文g?br>    ifile.seekg( 0, ios::end );<br>    再读取当前位|,q就是文件的大小了?br>    long filelength = ifile.tellg();<br>:http://www.shnenglu.com/walkspeed/archive/2007/05/02/23336.html<br><img src ="http://www.shnenglu.com/richardhe/aggbug/52241.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/richardhe/" target="_blank">RichardHe</a> 2008-06-05 14:17 <a href="http://www.shnenglu.com/richardhe/articles/52241.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>接口设计的要点(接口不应被用者直接销毁)http://www.shnenglu.com/richardhe/articles/52163.htmlRichardHeRichardHeWed, 04 Jun 2008 07:39:00 GMThttp://www.shnenglu.com/richardhe/articles/52163.htmlhttp://www.shnenglu.com/richardhe/comments/52163.htmlhttp://www.shnenglu.com/richardhe/articles/52163.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/52163.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/52163.html 接口设计的要点(接口不应被用者直接销毁) 接口在用后Q不需要也不允许用户销?br>
接口用来通讯的。虽然在C++中,接口的一般用指针实现Q但它代表的是一U通讯方式Q而不是资源的
位置。请求者在使用完后不应ȝ接销毁这个指针,而应由接口的提供者去理?br>
接口的提供者要理接口兌的实体的生命周期Q而且q有知道接口的用情冉|能管理接口?br>
接口于实体之间有兌Q这表明实体要在接口前创建,要在接口后销毁。设计中接口提供者通过接口?br>计数Q来理接口的状态。有一个请求者就接口计数增加一Q接口每被赋gơ接口计C增加一?br>?br>
接口的用者在使用完后Q要接口返回给接口提供者,而不是自q接销毁。因为接口不是被使用?br>甌的资源,而是用户要求另一个对象通讯的通道。所以用完后要返q给接口提供者。接口提供者每?br>C个接口的q还Q就减少q个接口的计Cơ?br>
在要销毁接口关联的实体Ӟ先要查这个接口是否还有用者,如果有,p通知使用者,要求它们
q还接口。在接口没有使用者的情况下常能销毁实体(q样比较安全Q当然也可以销毁)?br>
同一个接口可能对应多个实体,不过用户是感觉不到的。但是接口提供者一定要理q种接口于不同的
实体之间的关联关p,保证对不同的实体销毁时Q要收回与这个实体关联的所有接口?img src ="http://www.shnenglu.com/richardhe/aggbug/52163.html" width = "1" height = "1" />

RichardHe 2008-06-04 15:39 发表评论
]]>
在C++中侦内嵌类型的存在http://www.shnenglu.com/richardhe/articles/52160.htmlRichardHeRichardHeWed, 04 Jun 2008 07:23:00 GMThttp://www.shnenglu.com/richardhe/articles/52160.htmlhttp://www.shnenglu.com/richardhe/comments/52160.htmlhttp://www.shnenglu.com/richardhe/articles/52160.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/52160.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/52160.html阅读全文

RichardHe 2008-06-04 15:23 发表评论
]]>
boost源码剖析之:泛型~程_type_traitshttp://www.shnenglu.com/richardhe/articles/52159.htmlRichardHeRichardHeWed, 04 Jun 2008 07:22:00 GMThttp://www.shnenglu.com/richardhe/articles/52159.htmlhttp://www.shnenglu.com/richardhe/comments/52159.htmlhttp://www.shnenglu.com/richardhe/articles/52159.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/52159.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/52159.html阅读全文

RichardHe 2008-06-04 15:22 发表评论
]]>
boost源码剖析之:boost::multi_arrayhttp://www.shnenglu.com/richardhe/articles/52157.htmlRichardHeRichardHeWed, 04 Jun 2008 07:21:00 GMThttp://www.shnenglu.com/richardhe/articles/52157.htmlhttp://www.shnenglu.com/richardhe/comments/52157.htmlhttp://www.shnenglu.com/richardhe/articles/52157.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/52157.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/52157.html阅读全文

RichardHe 2008-06-04 15:21 发表评论
]]>
boost源码剖析之:泛型指针cany之vU百?/title><link>http://www.shnenglu.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.shnenglu.com/richardhe/articles/52158.html</guid><wfw:comment>http://www.shnenglu.com/richardhe/comments/52158.html</wfw:comment><comments>http://www.shnenglu.com/richardhe/articles/52158.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/richardhe/comments/commentRss/52158.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/richardhe/services/trackbacks/52158.html</trackback:ping><description><![CDATA[     摘要: 首先Qanyc里面一定要提供一个模板构造函数和模板operator=操作W。其ơ,数据的存放之所是个问题Q显然你不能它保存在anycMQ那会导致anycL为模板类Q后者是明确不被允许的。结论是Qؓ容器准备一个非泛型的基c,而让指针指向该基cR?动机 C++是强cd语言Q所有强cd语言对类型的要求都是苛刻的,cd一有不合编译器׃抱怨说不能某某类型{换ؓ某某cdQ当然如果在cd之间提供了{?..  <a href='http://www.shnenglu.com/richardhe/articles/52158.html'>阅读全文</a><img src ="http://www.shnenglu.com/richardhe/aggbug/52158.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/richardhe/" target="_blank">RichardHe</a> 2008-06-04 15:21 <a href="http://www.shnenglu.com/richardhe/articles/52158.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>boost源码剖析之:Tuple Typeshttp://www.shnenglu.com/richardhe/articles/52156.htmlRichardHeRichardHeWed, 04 Jun 2008 07:20:00 GMThttp://www.shnenglu.com/richardhe/articles/52156.htmlhttp://www.shnenglu.com/richardhe/comments/52156.htmlhttp://www.shnenglu.com/richardhe/articles/52156.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/52156.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/52156.html阅读全文

RichardHe 2008-06-04 15:20 发表评论
]]>
刘未鹏之《boost源码剖析》系列多重回调机制signalhttp://www.shnenglu.com/richardhe/articles/52155.htmlRichardHeRichardHeWed, 04 Jun 2008 07:19:00 GMThttp://www.shnenglu.com/richardhe/articles/52155.htmlhttp://www.shnenglu.com/richardhe/comments/52155.htmlhttp://www.shnenglu.com/richardhe/articles/52155.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/52155.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/52155.html阅读全文

RichardHe 2008-06-04 15:19 发表评论
]]>
Boost.Bind的基使用http://www.shnenglu.com/richardhe/articles/52150.htmlRichardHeRichardHeWed, 04 Jun 2008 07:01:00 GMThttp://www.shnenglu.com/richardhe/articles/52150.htmlhttp://www.shnenglu.com/richardhe/comments/52150.htmlhttp://www.shnenglu.com/richardhe/articles/52150.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/52150.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/52150.html Boost.Bind的基使用 当我们用函数时习惯于C函数的格?卛_下Ş?br>resulttype funname( arglist );
q回值类?函数? 参数列表 );

在Boost.Function中,我们可以方便的定义定义函数对象。不q在定义用来表示cL员函数的函数对象?br>W一个参数是cL针。而且在调用时Q要传入一个类实例的指针。这Lhq不是很方便Q因用?br>要知道类实例。这实际上没有实现解耦。而解耦是我们使用回调或委托设计的一个目标?br>
Z解决q个问题Q我们要使用Boost.Bind?br>
Boost.Bind是一个函数对象工厂。他用来产生我们需要的函数对象。好了,有了它,你可以在你设计中?br>量用Boost.Function。不用再d义类成员函数形式的函数对象啦Q只用定义普通函数对象?br>
一个简单的例子

class CExample
{
public:
    bool printstr( const std::string &str )
    {
        std::cout << "CExample::printstr" << str << std::endl;
        return true;
    }
};

//定义一个函数对?br>boost::function< bool ( const std::string& ) > printstr;

//用Boost.Bind创徏一个函数对象,赋给printstr
CExample example;
printstr = boost::bind( &CExample::printstr, &example, _1 );

好了Q我们创Z一个函数对象,而且调用时不再需要类实例拉。用Boost.Function和Boost.Bind大大
的简化了Command模式的实现?br>
在上面的例子中要个古怪的对象"_1"。这个叫做站位符Q他代表q个位置有个参数Q但现在q不知道?br>数是什么。_1代表参数列表中的W一个位|上的参数。Boost.Bind一共定义了9个站位符对象。如?br>_1,_2,_3,_4,_5,_6,_7,_8,_9。分别代表参数列表中位子?br>
Boost.Bind产生的函数对象可以直接用,利用上面的例子?br>
bool b = boost::bind( &CExample::printstr, &example, _1 )( "Hello World" );

RichardHe 2008-06-04 15:01 发表评论
]]>
Boost源码剖析QC++泛型函数指针c?/title><link>http://www.shnenglu.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.shnenglu.com/richardhe/articles/52148.html</guid><wfw:comment>http://www.shnenglu.com/richardhe/comments/52148.html</wfw:comment><comments>http://www.shnenglu.com/richardhe/articles/52148.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/richardhe/comments/commentRss/52148.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/richardhe/services/trackbacks/52148.html</trackback:ping><description><![CDATA[<strong>前奏 </strong><br><br>  如你所知,Boost库是个特性完备,且具备工业强度的库,众多C++权威的参与其达Cd造极的程度。尤?a class="bluekey" target="_blank">泛型</a>的强大威力在其中被发挥得淋漓致Qo人瞠目结舌?br><br>  然而弱水三千,我们只取一瓢饮。下面,我试图从最单纯的世界开始,一步一步带领你q入<a class="bluekey" target="_blank">源码</a>的世界,LIboost::function(下文Ufunction)内部的精微结构?br><br>  通常 Q在单纯的情况下Q对函数?a class="bluekey" target="_blank">调用</a>单而且直观Q像q样Q?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 class="bluekey" target="_blank">函数指针</a>保存下来Qƈ在以后的另一个时刻调用它Q像q样Q?br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>int fun(int);<br><a 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>  但是Q如果fun形式?a class="bluekey" target="_blank">void</a> fun(int)呢?如你所见,fun可能有无数种形式Q如果对fun的每一个Ş式都typedef一个对应的func_handleQ则E序员会焦头烂额Q不胜其扎ͼ代码也可能变得臃肿和丑陋不堪Q甚臛_果fun是仿函数呢?<br><br>  q运的是C++泛型可以使代码变?a class="bluekey" target="_blank">优雅</a>_致Q面Ҏ数种的可能,泛型是最好的选择?因此Q你只是需要一个能够保存函?a class="bluekey" target="_blank">指针</a>的泛?a class="bluekey" target="_blank">模板c?/a>(对应于Command模式)Q因为泛?a class="bluekey" target="_blank">~程</a>有一个先天性的优势——可以借助<a class="bluekey" target="_blank">~译?/a>的力量在~译期根据用h供的型别信息化n千万(L?Q所以一个泛型的cd以有无限个具CQ也是说可以保存无限多U可能型别的函数或类似函数的东西(如,仿函?。这个类(在Boost库中的类名ؓfunction)与函数指针相比应该有以下一些优势:<br><br>  ¨ 同一个function对象应能够接受与它Ş式兼容的所有函数和仿函?例如Q?br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>int f1(int); //q是个函敎ͼ形式?int(int)<br>short f2(<a class="bluekey" target="_blank">double</a>); //q个函数形式?short(double)<br><br><a class="bluekey" target="_blank">struct</a> functor //q是个仿函数c,形式为int(int)<br>{<br> int <a class="bluekey" target="_blank">operator</a>()(int){}<br>};<br><br>functor f3; //创徏仿函数对?br><br>boost::functionQint(int)Q?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>  ¨ function应能够和参数<a class="bluekey" target="_blank">l定</a>以及其它function-construction?a class="bluekey" target="_blank">协同工作</a>。例如,function应该也能够接受std::bind1stq回的仿函数。这一点其实由W一点已l有所保证?br><br>  ¨ 当接受的一个空的仿函数对象被调用的时候function应该有可预期的行为?br><br>  昄Q第一Ҏ我们的重点,所谓Ş式兼容,是_对于Q?br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>R1 (T0,<a class="bluekey" target="_blank">T1</a>,T2,...,TN) =Q?FunctionType1<br><a class="bluekey" target="_blank">R2</a> (P0,P1,<a class="bluekey" target="_blank">P2</a>,...,PN) =Q?FunctionType2</td> </tr> </tbody> </table> <br>  两种cd的函敎ͼq义Q,只要满Q?br><br>  1. R2能够隐式转换为R1<br><br>  2. 所有Ti都能够隐式{换ؓ<a class="bluekey" target="_blank">Pi</a> (i?,1,2,...)<br><br> 那么pQboost::functionQFunctionType1Q可以接受FunctionType2cd的函?注意Q反之不?。支持这一 论断的理由是Q只要Ti能够隐式转型为PiQ那么参数被转发l真实的函数调用是安全的,q且如果R2能够隐式转型为R1Q那么返回真实函数调用所q回? 值就是安全的。这里安全的含义是,C++cdpȝ认ؓ隐式转换不会丢失信息Q或者会l出~译警告Q但能够通过~译?br><br>  后面你会看到Qboost::function通过所谓的invoker非常巧妙地实Cq点Qƈ且阻止了被Ş式不兼容的函数赋值的操作?br><strong>探险</strong><br><br>  好吧Q准备好Q我们要出发了,q行深入源码世界的探险?br><br>  先看一个function的最单的使用Q?br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>int g(int); //Z让代码简单,假设g有定义,以后的代码都会如?br>functionQint(int)Q?f(g);<br>f(0);</td> </tr> </tbody> </table> <br>  <strong>间奏——R(T1,T2,...)函数cd</strong><br><br>  虽然q个间奏未免早了点儿Q但是ؓ了让你以后不会带着qhQ这昄是必要的。请保持耐心?br><br> 或许你会Ҏ板参数int(int)感到陌生Q其实它是个函数型别——函数g的确切型别就是int(int)Q而我们通常所看到的函数指针型别int (*)(int)则是&g的型别。它们的区别与联pd于:当把g作ؓ一个D行拷贝的时候(例如Q按g参)Q其cd׃由int(int)退? 为int(*)(int)Q即从函数类型退化ؓ函数指针cd——因Z语义上说Q函C能被“按值拷?#8221;Q但wؓ函数指针的地址值则是可以被拯的。另一 斚wQ如果g被绑定到引用Q则其类型不会退化,仍保持函数类型。例如:<br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>templateQclass TQ?br><br>void test_func_type(T ft) //按g递,cd退?br>{<br> static_castQintQ?ft); //引发~译错误Q从而看出ft的类型ؓ退化后的函数指?br>}<br><br>int g(int); //函数gQ名字g的类型ؓint(int)<br>test_func_type(g); //注意Qƈ?amp;gQ参数g的类型将会退化ؓ函数指针cd<br>int (&ref_f)(int) = g; //注意Qƈ?#8220;= &g”Q因为绑定到引用Q类型ƈ不退?/td> </tr> </tbody> </table> <br> 当然Q这L代码不能通过~译Q因为static_castQ>昄不会让一个函数指针{换ؓintQ然而我们就是要它通不q编译,q样我们才能H视? 按g递的参数ft的类型到底是什么,从编译错误中我们看出Qft的类型是int(*)(int)Q也是_在按g递的q程中,g的类型退化ؓ函数? 针类型,变得?amp;g的类型一样了。而ref_t的类型则是引用,引用l定则没有引L型退化?br><br>  h意,函数cd乃是个极其特D的cdQ在大多数时候它都会退化ؓ函数指针cdQ以便满x贝语义,只有面对引用l定的时候,能够l持原来的类型。当Ӟ对于boost::functionQL按值拷贝?br><br>  <strong>l箋旅程</strong><br><br>  好吧Q回q神来,我们q有更多地带要去探究?br><br>  functionQint(int)Q实际上q行了模板偏特化QBoost库给function的类声明为:<br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>templateQtypename Signature, //函数cd<br>typename Allocator = ...<br>Q?//Allocatorq重点Q故不作介绍<br><br>class function;</td> </tr> </tbody> </table> <br>  事实上functioncd是个薄薄的外覆(wrapperQ,真正起作用的是偏特化版本?br><br> 对于functionQR(T0)QŞ式,偏特化版本的function源码像这?实际上在boost源代码中你看不到模板参数T0的声明,也看不到 function1Q它们被宏替换掉了,那些_y的宏是ؓ了减可见的代码量,至于它们的细节则又是一个世界,以下代码可看作对那些o人眼q݋q宏展 开后所得到的代码,h更好的可L?Q?br><br>  摘自Q?#8221;boost/function/function_template.hpp”<br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>templateQtypename R,typename T0,typename AllocatorQ?br>class functionQR(T0),AllocatorQ?//对R(T0)函数cd的偏特化版本<br><br>:public function1QR,T0,AllocatorQ?//为R(T0)形式的函数准备的基类Q在下面讨论<br>{<br> typedef function1QR,T0,AllocatorQ?base_type;<br> typedef function selftype;<br> struct clear_type{}; //马上你会看到q个y؜的类型定义的作用<br> public:<br>  function() : base_type() {} //默认构?br>  templateQtypename FunctorQ?//模板化的构造函敎ͼZ能够接受形式兼容的仿函数对象<br>  function(Functor f, typename enable_ifQ?br>    (ice_notQ?is_sameQFunctor, intQ?:value)Q?:value),<br>    int<br>  Q?:type = 0) Qbase_type(f){} <br><br> function(clear_type*) : base_type() {} //q个构造函数的作用在下面解?br> self_type& operator=(const self_type& f) //同类型function对象之间应该能够赋?br> {<br>  self_type(f).swap(*this); //swap技巧,l节见《Effective STL?br>  return *this;<br> }<br> ...<br>};<br><br>enable_if<br></td> </tr> </tbody> </table> <br>  你一定对模板构造函C出现的那个冗长的enable_ifQ?..Q的作用心存疑惑Q其实它的作用说I了很简单,是Q当用户构造:<br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>functionQint(int)Q?f(0);</td> </tr> </tbody> </table> <br>  的时候,该Q带有enable_if的)构造函C重蝲册的候选集中踢掉。重蝲册的结果ؓ选中W三个构造函敎ͼ<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;有效QenableQ?#8221;的,会被重蝲册选中。但如果用户提供了一 ?Q用意是构造一个空(null)的函数指针,那么该函数就会由?#8220;SFINAE”原则而被从重载决议的候选函Ct掉。ؓ什么要q样呢?因ؓ该构造函 数负责把切的f保存hQ它假定fq0。那应该选择谁呢Q第三个构造函敎ͼ其参数类型是clear_type*Q当Ӟ0可以被赋lQ何指针,所以它 被选出Q执行缺省的构造行为?br><strong>基类 functionN</strong><br><br>  function的骨架就q些。也怽会问Qfunction作ؓ一个仿函数c,怎么没有? 载operator()——这可是wؓ仿函数的标志啊!别急,function把这些烦人的d都丢l了它的基类functionNQ根据情况不同,N? 能ؓ0Q?Q?...Q说具体一点就是:Ҏ用户使用function时给出的函数cdQfunction会l承自不同的基类——如果用L出的函数c? 型ؓ“R()”形式的,即仅有一个参敎ͼ则functionl承自function0Q而对于R(T0)形式的函数类型,则承自function1Q依 此类推。前面说q,function只是一层外覆,而所有的U密都在其基cfunctionN中!<br><br>  不知道你有没有发玎ͼfunction的骨架中也几乎没有用到函数类型的信息Q事实上Q它也将q些信息一股脑儿抛l了基类。在q过E中Q؜沌一团的int(int)型别被拆解ؓ两个单独的模板参Cl基c?<br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>templateQtypename R,typename T0,typename AllocatorQ?br>class functionQR(T0),AllocatorQ?//R(T0)整个Z型别<br>:public function1QR,T0,AllocatorQ?//拆解Z个模板参数R,T0传给基类</td> </tr> </tbody> </table> <br>  好了Q下面我们深入基cfunction1。真正丰富的宝藏在里面?br><br>  function1<br><br>  function1的源代码像这?与上面一P事实上有些代码你是看不到的,Z不让你迷惑,我给出的是将宏展开后得到的代码):<br><br>  摘自Q?#8221;boost/function/function_template.hpp”<br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>templateQtypename R,typename T0,class Allocator = ...Q?br><br>class function1:public function_base //function_base负责理内存<br>{<br> ...<br> public:<br>  typedef R result_type; //q回cd<br>  typedef function1 self_type;<br>  function1() : function_base(),invoker(0){}//默认构?br>  templateQtypename FunctorQ?br>  function1(Functor const & f, //模板构造函?br>   typename enable_ifQ?..Q?:type = 0) : <br>   function_base(),<br>   invoker(0)<br>   {<br>    this-Qassign_to(f); //q儿真正q行赋|assign_to的代码在下面列出<br>   }<br><br>  function1(clear_type*) : function_base(), invoker(0){} //该构造函C面解释过<br>  function1(const function& f) : //拯构造函?br>  function_base(),<br>  invoker(0){<br>   this-Qassign_to_own(f); //专用于在function之间赋值的assignment<br>  }<br><br>  result_type operator()(T0 a0) const //wؓ仿函数的标志Q?br>  {<br>   //下面负责调用指向的函?br><br>   if (this-Qempty())<br>    boost::throw_exception(bad_function_call());<br>    //q里q行真正的函数调用,使用invoker<br><br>    internal_result_type result = invoker(function_base::functor,a0); <br>    return static_castQresult_typeQ?result);<br>  }<br><br>  templateQtypename FunctorQ?br>  void assign_to(Functor f) //所有的构造函数都调用它!h多个重蝲版本?br>  {<br>   //以一个get_function_tag萃取出Functor的类?category)!<br>   typedef typename detail::function::get_function_tagQFunctorQ?:type tag;<br>   this-Qassign_to(f, tag());//Ҏ不同cd的Functor采取不同的assign{略Q?br>  }</td> </tr> </tbody> </table> <br>  get_function_tagQ>能萃取出Functor的类?category)Q有下面几种cd<br><br>  struct function_ptr_tag {}; //函数指针cd<br><br>  struct function_obj_tag {}; //仿函数对象类?br><br>  struct member_ptr_tag {}; //成员函数cd<br><br>  struct function_obj_ref_tag {};//以ref(obj)加以装的类别,h引用语义<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>  的仿函数对象UCؓstateless?br><br>  而对于不同的函数cdQassign_to有各个不同的重蝲版本Q如下:<br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>templateQtypename FunctionPtrQ?//如果是函数指针就调用q个版本<br><br>void assign_to(FunctionPtr f, function_ptr_tag) //q个版本针对函数指针<br>{ <br> clear();<br> if (f){<br>  typedef typename detail::function::get_function_invoker1QFunctionPtr,R,T0Q?:type invoker_type;<br>  invoker = &invoker_type::invoke; //invoke是static成员函数<br><br>  function_base::manager = //理{略<br>     &detail::function::functor_managerQFunctionPtr, AllocatorQ?:manage;<br><br>  function_base::functor = //交给function的函数指针或仿函数对象指针最l在q儿保存<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; //重要成员Q负责调用函敎ͼ<br><br>};</td> </tr> </tbody> </table> <br>  你可能已l被q段“夹叙夹议”的代码弄得头昏脑涨了Q但q才刚刚开始!<br><br>  <strong>function的底层存储机?/strong><br><br>  请将目光转向上面的代码段末尾的assign_to函数中,其中有两行深色的代码Q分别对function_base里的manager和functor成员赋倹{这两行代码肩负了保存各U函数指针的d?br><br> manager是一个函数指针,它所指向的函C表管理策略,例如Q对于函数指针,仅仅作一ơ赋|׃存完毕了Q但是对于仿函数Q得额外分配一ơ内 存,然后仿函数拯到分配的内存中,q才完成了保存的d。这些策略根据函数的cd而定Q上面代码中的assign_to函数是针对函数指针类别的重蝲 版本Q所以manager的策略是不作M内存分配Q直接返回被转型?#8220;void(*)()”Q利于在底层以统一的Ş式保存)的函数指针就行了Q这从代? 中可以看出?br><br>  需要说明的是,对于函数指针Qfunction_baseq不知道也不兛_它要保存的函数指针是什么确切的cdQ只要是 函数指针pQ因为它M把该函数指针f转型?#8220;void (*)()”cdQ然后保存在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; //L仿函数对象指针都可以用static_castQ>转型为void*?br>const void* const_obj_ptr; //为const仿函数准备的<br>void (*func_ptr)(); //L函数指针都可以用reinterpret_castQ>转型为void(*)()?br>char data[1];<br>};</td> </tr> </tbody> </table> <br>  q个any_pointer可以通过安全转型保存所有Ş式的仿函数和函数指针Q承载在底层保存数据的Q?br><br>  <strong>function的调用机制——invoker</strong><br><br>  我们把目光{到function1的定义的最底部Q那儿定义了它最重要的成员invokerQ它是一个函数指针,所指向的函数就是function的调用机制所在,invoker的类型ؓQ?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> 前面已经说过Qany_pointer是个unionQ可以保存Q何类型的函数指针或函数对象,里面保存的是用户注册的函数或仿函敎ͼT0? any_pointer中的函数的参数的型别Q对于不同情况,可能会有T1,T2{)。这也就是说Qinvoker负责调用保存在any_pointer 中的用户提供的函数或仿函数?br><br>  那么Qinvokerq个函数指针到底指向什么函数呢——也是_在什么时候invoker被赋g呢?我们再次把目光{向assign_to函数Q其中有一行对invoker成员赋值的语句Q从q行语句出发我们可以揭露invoker的全部奥U:<br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>invoker = &invoker_type::invoke; //invoke是static成员函数</td> </tr> </tbody> </table> <br> 请不要把q个invoker_type和上面那个函数指针型别invoker_typehhQ这个invoker_type是位? assign_to函数中的一个局部的typedefQ所以隐藏了后者(即类作用域中的那个invoker_type——invoker成员的类型)。往 上一行,你就看到q个局部型别invoker_type的定义了Q?br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>typedef typename get_function_invoker1Q?br><br>FunctionPtr,R,T0Q?:type invoker_type;</td> </tr> </tbody> </table> <br>  get_function_invoker1又是何物Q很昄Q这是个traitsQ其内嵌?:type会根据不同的模板参数表现Z同的cdQ在本例中,::type的类型将会被推导?br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>function_invoker1Qint(*)(int),int,intQ?/td> </tr> </tbody> </table> <br>  而function_invoker1是个cL板,其定义ؓQ?br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>templateQtypename FunctionPtr,<br>typename RQtypename T0Q?//注意q里的模板参敎ͼ后面会解?br><br>struct function_invoker1<br>{<br> static R invoke(any_pointer function_ptr,T0 a0)<br> {<br>  FunctionPtr f = reinterpret_castQFunctionPtrQ?function_ptr.func_ptr);<br>  return f(a0);<br> }<br>};</td> </tr> </tbody> </table> <br>  所以对invoker的赋值最l相当于Q?br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>invoker=&function_invoker1Qint(*)(int),int,intQ?:invoke;</td> </tr> </tbody> </table> <br>  而function_invoker1Qint(*)(int),int,intQ?: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> //先{型,再调用,注意Q这一行语句还有一个额外的作用Q在后面解释<br> int (*f)(int) = reinterpret_castQint(*)(int)Q?function_ptr.func_ptr);<br><br> //因ؓf指向的是用户保存在该function中的函数或仿函数Q所以这一行语句进行了真实的调用!<br> return f(a0);<br>}</td> </tr> </tbody> </table> <br>  我们可以看出Q在invoke函数中,真正的调用现w了?br><br>  如果接受的是仿函敎ͼ则有function_obj_invoker1与它对应Q后者也是一个类似的模板Q它的invoke静态成员函数的形式也是Q?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静态成员函C对它的调用语句是q样的:<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> 最后一U可能:如果接受的是成员函数怎么办呢Q简单的{案是:boost::functionq没有ؓ成员函数作Q何特D准备!理由也很单, boost::function只要先将成员函数装Z函数Q然后将其作Z般的仿函数对待就行了Q具体代码就不列了,STL中有一个函数模? std::mem_fun是用于装成员函数指针的,它返回的是一个仿函数。boost中也对该函数模板做了扩充Q它可以接受Q意多个参数的成员? 数?br><strong>做一个,送一个——invoker的额外好?/strong><br><br>  我们注意到function的构造和赋值函数及其基cȝ构造和赋值函数都 是模板函敎ͼq是因ؓ用户可能提供函数也可能提供函数模板,但最关键的还是,functiont提供一U能力:对于functionQdouble (int)Q类型的泛型函数指针Q用户可以给它一个int(int)cd的函数——是的,q是可行且安全的Q因为其q回值类型int可以安全的{型ؓ doubleQ而对于这U类型兼Ҏ的查就在上面分析的invoke静态成员函CQ这是我们要说的额外好处——如果类型兼容,那么invoke函数 p正常~译通过Q但如果用户l出cd不兼容的函数Q就会得C个错误,q个错误是在~译器实例化invoke函数代码的时候给出的Q例如,用户如果q样 写:<br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>RT1 f(P1,P2); // RT1(P1,P2)函数cdQ这里的RT1,P1,P2假定已经定义Q这是一般化的符?br><br>functionQRT(P)Q?f_ptr; //RT(P)函数cdQ同样假定RT,P已定?br><br>f_ptr = &f; //cd不兼容,错误Q?/td> </tr> </tbody> </table> <br>  q就会导致编译错误,错误发生在invoke静态成员函C。下面我׃ؓ你解释ؓ什么?br><br>  我想你对function_invoker1考的三个模板参数仍然心存疑惑Q我们再一ơ来回顾一下其声明Q?br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>templateQtypename FunctionPtr,typename RQtypename T0Q?<br><br>struct function_invoker1</td> </tr> </tbody> </table> <br>  我们q得把目光投向assign_to模板函数Q其中用function_invoker1的时候是q样的:<br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>typedef typename detail::function::get_function_invoker1Q?br><br>FunctionPtr,R,T0Q?:type invoker_type;</td> </tr> </tbody> </table> <br>  q里Q给出的FunctionPtr,R,T0三个模板参数会原封不动的传lfunction_invoker1Q那么对于我们上面的错误CZQ这三个模板参数各是什么呢Q?br><br>  首先Q我们很Ҏ看出QFunctionPtr是assign_to模板函数的模板参敎ͼ也就是用户传递的函数或仿函数的类型,在我们的错误CZ中,函数f的类型ؓRT1(P1,P2)Q所?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模板时给出的模板参数Q我们写的是functionQRT(P)Q,于是:<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的类型ؓQ?br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>function_invoker1Q?RT1(*)(P1,P2),RT,PQ?/td> </tr> </tbody> </table> <br>  对于q样一个function_invoker1Q其内部的invoke静态成员函数被实例化ؓQ?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_castQRT1(*)(P1,P2)Q?function_ptr.func_ptr);<br> return f(a0); //错啦Q瞧瞧f的型别,f接受一个Pcd的参数吗Q编译器在此打住?br> //q行语句的另一个隐含的查是q回值类型匹配,f(...)q回RT1Q而invokedq回RT<br>}</td> </tr> </tbody> </table> <br>  看看最后一行语句,所有的查都在那里了——我们最l把?#8220;委托”l了C++底层的类型系l?br><br>  很精妙不是吗Q虽然在模板形式的assign_to函数中,看v来我们ƈ不关心到底用L的参数是何类型,看v来用户可以把M函数或仿函数塞过来,但是一旦下面触及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>  一下就把问题暴露出来了Q这U把cd查gq到最 后,不得不进行的时候,由C++底层的类型系l来负责查的手法的确很奇妙——看h我们没有在assign_to函数中及时利用类型信息进行类型检查, 但是我们却ƈ没有丧失Mcd安全性,一切最l都逃不qC++底层的类型系l的考验Q?br><br> <strong> function如何对待成员函数</strong><br><br>  对于成员函数Qassign_to的重载版本只有一行:<br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>this-Qassign_to(mem_fn(f));</td> </tr> </tbody> </table> <br>  mem_fun(f)q回一个仿函数Q它装了成员函数fQ之后一切皆与仿函数无异?br><br>  关于mem_fun的细节,q里׃多说了,大家可以参考STL中的实现Q相信很Ҏ看懂Q这里只单的提醒一下,成员函数装的效果是q样的:<br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>R (C::*)(T0,T1,...) --Q?R (*)(C*,T0,T1,...) ?R (*)(C&,T0,T1,...)</td> </tr> </tbody> </table> <br>  <strong>safe_bool惯用手法</strong><br><br>  如你所知,对于函数指针fptrQ我们可以这h试它Qif(fptr) ...Q所以function也应该提供这一Ҏ,然而如果直接重载operator  bool()则会D下面的代码成为合法的:<br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>functionQint(int)Q?f;<br><br>bool b=f;</td> </tr> </tbody> </table> <br>  q显然不妥,所以function用另一个y妙的手法替代它,既所谓的safe_bool惯用手法Q这在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不能转型ZQ何其它类型!<br><br>operator safe_bool () const <br><br>{ return (this-Qempty())? 0 : &dummy::nonnull; }</td> </tr> </tbody> </table> <br> q样Q当你写if(f)的时候,~译器会扑ֈoperator safe_bool()Q从而将f转型为safe_boolQ这是个指针cdQif语句会正判定它是否为空。而当你试图把f赋给其它cd的变量的时候则 会遭到编译期的拒l——因为safe_bool无法向其它类型{换?br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>get_function_tagQ></td> </tr> </tbody> </table> <br>  get_function_tagQ>用于萃取出函数所属类?category)Q各个类别在源代码中已经列出Q至于它到底是如何萃取的Q这与本文关pM是很 大,有一炚w要提醒一下:函数指针cd也是指针cdQ这听v来完全是句废话,但是考虑q样的代码: <br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>templateQtypename TQ?struct is_pointer{enum{value=0};};<br><br>templateQtypename TQ?struct is_pointerQT*Q{enum{value=1};};<br><br>std::coutQ<is_pointerQint(*)(int)Q?:value; //q将输出 1</td> </tr> </tbody> </table> <br>  也就是说int(*)(int)可以与T*形式匚wQ匹配时T为int(int)?br><br>  <strong>最后一些细?/strong><br><br>  1. 我没有给出function_base的源代码Q实际上那很单,它最主要的成员就是union any_pointer型的数据成员<br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>detail::function::any_pointer functor; //用于l一保存函数指针及仿函数对象指针</td> </tr> </tbody> </table> <br>  2. 我没有给出functor_manager的信息,实际上它与function的实现没有太大关p,它负责copy和delete函数对象Q如果必要的话。所以我它略去Q它的源码在Q?#8221;boost/function/function_base.hpp”里?br><br>  3. 我给出的源代码是宏展开后的版本Q实际的代码中充斥着让hD~ؕ的宏Q关于那些宏则又是一个奇妙的世界。Boost库通过那些宏省M许多可见代码量。随着函数参数的不同,那些宏会扩展出function2,function3...各个版本?br><br>  本文只研I了int(int)型的情况Q其它只是参数数目的改变而已。经q宏的扩展,function的偏特化版本有:<br><br> <table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"> <tbody> <tr> <td>templateQtypename R,typename AllocatorQ?br><br>class functionQR(),AllocatorQ?public function0QR,AllocatorQ?br><br>{...};<br><br>templateQtypename R,typename T0,typename AllocatorQ?br><br>class functionQR(T0),AllocatorQ?public function1QR,T0,AllocatorQ?br><br>{...};<br><br>templateQtypename R,typename T0,typename T1,typename AllocatorQ?br><br>class functionQR(T0,T1),AllocatorQ?public function2QR,T0,T1,AllocatorQ?br><br>{...};</td> </tr> </tbody> </table> <br><br> {更多版本,一共有BOOST_FUNCTION_MAX_ARGS+1个版本,BOOST_FUNCTION_MAX_ARGSZ个宏Q表C最多能 够接受有多少个参数的函数及仿函数对象Q你可以重新定义q个宏ؓ一个新|以控制function所能支持的函数参数个数的最大倹{其中的 function0,function1,function2{名字也由宏扩展出?br>  阅读关于 <a target="_blank"><font color="red">C++</font></a> 的全部文?br><img src ="http://www.shnenglu.com/richardhe/aggbug/52148.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/richardhe/" target="_blank">RichardHe</a> 2008-06-04 14:50 <a href="http://www.shnenglu.com/richardhe/articles/52148.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Boost.Function的基本?/title><link>http://www.shnenglu.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.shnenglu.com/richardhe/articles/52147.html</guid><wfw:comment>http://www.shnenglu.com/richardhe/comments/52147.html</wfw:comment><comments>http://www.shnenglu.com/richardhe/articles/52147.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/richardhe/comments/commentRss/52147.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/richardhe/services/trackbacks/52147.html</trackback:ping><description><![CDATA[<div id="mauyasi" class="postTitle"> <a href="http://www.shnenglu.com/walkspeed/archive/2007/07/18/28272.html" id="viewpost1_TitleUrl" class="postTitle2">Boost.Function的基本?/a> </div> Boost.Function库用来提供一个对象化的函数指针?br><br>函数指针对设计很有用。它使调用者可以g期调用,调用时机p用者确定。而且可以改变<br>响应者,以应对不同的要求?br><br>C中的函数指针只能用于自由函数。在C++中除了自由函数还有函数对象和cL员函敎ͼq些<br>C的函数指针是无法用的。这要求能适应C++语言的函数指针。既然C++语言本n没有提供Q?br>那就提供一个库。stl提供了,但是定义了很多类型,使用hq不是很方便Q而且函数参数<br>的个数被限定在两个以下,更能是备受限制。Boost.Function库提供了一个好的解x案?br><br>Boost.Function库可以支持自由函敎ͼ函数对象Q类成员函数。而且参数个数多达10个?br>Boost.Function库利用模板技术来实现。生成的代码有很高的q行效率。本库可以不用编?br>直接使用?br><br>Boost.Function的头文g?br>function.hpp<br><br>定义一个Boost.Function的对象(是一个返回值类型ؓintQ第一个参数是std::stringcd<br>W二个参数是floatcLQ?br><br>boost::function< int ( std::string, float ) > funptr;<br><br>上面q个定义方式是一U容易理解的定义方式。但有些~译器不支持Q如果想更多的编译器<br>支持Q则用下面这U定义方?br><br>boost::function2< int, std::string, float > funptr;<br><br>注意模板中有3个类型,而functioncd是boost::function2。应回值类型不计算在参?br>cd中(原因很简单,C++的编译器不会Ҏq回cd不同来区分函数定义的不同Q?br><br>int freefun( std::string str, float f )<br>{<br>    std::cout << str << " : " << f << std::endl;<br>    return 0;<br>}<br><br>class CFun<br>{<br>public:<br>    int operator() ( std::string str, float f )<br>    {<br>        std::cout << str << " : " << f << std::endl;<br>        return 0;  <br>    }<br>}<br><br>上面定义了一个自由函数和一个函数对象。下面将把他们付lfunction对象?br><br>赋gؓ自由函数<br>funptr = &freefun;<br><br>赋gؓ函数对象<br>CFun fun;<br>funptr = fun;<br><br>以上两种情况的调用方法一_如下<br>funptr( "float =", 10.0 );<br><br>Boost.Function对象要能指向cd原函敎ͼ其定义要如下<br><br>class FreeClass<br>{<br>public:<br>    int out( std::string str, float f )<br>    {<br>        std::cout << str << " : " << f << std::endl;<br>        return 0;  <br>    }<br>};<br><br>boost::function< int ( FreeClass*, std::string, float ) > funptr;<br><br>跨^台的定义Ҏ<br>boost::function3< int, FreeClass*, std::string, float > funptr;<br><br>赋值方?br>funptr = &FreeClass::out;<br><br>调用Ҏ<br>FreeClass fc;<br><br>funptr( &fc, "float =", 10.0 );<img src ="http://www.shnenglu.com/richardhe/aggbug/52147.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/richardhe/" target="_blank">RichardHe</a> 2008-06-04 14:45 <a href="http://www.shnenglu.com/richardhe/articles/52147.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>仿函数、绑定、桥接、委托相兌?/title><link>http://www.shnenglu.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.shnenglu.com/richardhe/articles/52015.html</guid><wfw:comment>http://www.shnenglu.com/richardhe/comments/52015.html</wfw:comment><comments>http://www.shnenglu.com/richardhe/articles/52015.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/richardhe/comments/commentRss/52015.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/richardhe/services/trackbacks/52015.html</trackback:ping><description><![CDATA[flipcode<br>仿函数、绑定、桥接、委托相兌?<br>以下随便讨论下,没突出的中心论点Q个中理论只代表我个点,隑օ有错:)Q欢q指正?br>一。需?<br>在事件处理常怼到q样的情况:<br>1。接口分R即invokers(调用??receivers)接收者分R?br>2。时间分R?br><br>比如_UI相关元素Q按钮、菜单等Q就是一个invokers?br>receivers则是响应命o的对象(如对话框或应用程序本w)?br>q需要我们要先将UI相关元素的事件响应的接收者在初始化时先保存v来?br>待后用户按下按钮{再触发Q即invokers通过调用对应先前保存的receivers来执行命令)<br>嗯,在delphi、java、vcl?net中有相关的实现。而vc则需要自己来弄?br><br>二。仿函数的实?<br>在说仿函数前先说说我们应该怎么保存q些操作相关函数的呢Q?br>// 一般的函数我们可以q么?<br>void (*fun)() = Test;<br>(*fun)();<br>// 而类成员函数可以q么?<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->*mfn)(); // 调用Ҏ2<br>如上所q可见ؓ了处理前面所q的事g响应情况Q我们通常会用回调函数Q?br>是把类成员函数定义为静态函敎ͼ在初始时保存函数地址Q与一般函数处理类同)及对应的对象指针Q?br>在事件触发时调用对应的静态函敎ͼ而该函数中在把指针强制{化ؓ对应cd对象地址Q?br>得以操纵该对象的成员变量(嗯,理论上跟成员函数的实现差不多Q成员函Cq译器安插一?br>this指针作ؓW?个参Cl函敎ͼ以便可以操作该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)->pfn && (*iter)->pThis ){<br>(*(*iter)->pfn)( (*iter)->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 // 下个断点试下(某些~译器不能这么写Qvc可以Q?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->pfn = &CUI::InputProc;<br>pListener->pThis = &ui;<br>// 触发:<br>input.AddListener( pListener ); // input即ؓinvokersQ调用者,但叫触发者好点)<br>input.HitOneKey(); // 某处事g触发,内部呼叫receivers(q里是原先保存的CUI对象)来真正处理该事g(InputProc(...)Ҏ)?br>// 清除<br>input.clearListener();<br>if( pListener )<br>{<br>delete pListener;<br>pListener = NULL;<br>}<br>return 0;<br>}<br>//======================================================<br><br>// W?U方? 回调c?虚函数多态法):<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; // q里用list存v来才好,q里只作试<br>}<br>void OnInputEvent(){<br>m_pListener->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(&bb);<br>aa.OnInputEvent();<br>return 0;<br>}<br>//======================================================<br><br>但是W?U静态函数用法是不直观的Q第2U需要派生增加了之间的联p,而ؓ了方便我们通常会将成员函数指针转化为函数对象来处理,即仿函数(一般是指重载了()操作W的c?来实现?br><br>cM于这L操作Qstl提供了mem_fun、mem_fun_ref、binder1st、binder2nd单操作?br>但stl的方法相Ҏ较原始而受限制Q比如说std::mem_fun需要成员函数有q回?<br>std::mem_fun最多只能支持成员函数有一个参数等Q?br>下面来看std:mem_fun_ref不支持成员函数返回gؓvoid的一个例?<br>//======================================================<br>#include <br>class CFoo<br>{<br>public:<br>void test() // 只有voidҎ别的cd才可以,?int<br>{<br>return 0;<br>}<br>};<br>void main()<br>{<br>CFoo t;<br>std::mem_fun_ref(&CFoo::test)(t);<br>}<br>//======================================================<br>上述代码只有voidҎ别的cdQ如intQ才可以Q?br>那么Z么不可以处理q回void的函数呢? stl的实现究竟是怎么L?<br>嗯,stl单实Cmem_fun_ref及mem_fun,其中mem_fun_ref以引用方式处理函数所属对象,<br>而mem_fun以指针方式处理函数所属对象?br>现在让我们从vc的stl挖出部䆾代码来看看,<br><br>1.stl的实?<br>以mem_fun_refZ(省略某些对说明不重要的细节,两条虚线包括的代码ؓstlcM源码):<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& _X) const // 调用: q里可看出mem_fun_ref以引用方式处?br>{<br>return ((_X.*_Ptr)()); // q里执行调用函数Qƈq回该函数所q回?br>}<br>private:<br>R (T::*_Ptr)(); // 指向成员函数地址的指?br>};<br>// q里只是利用函数的参数推导来自动获取型别(方便调用)<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( &CFoo::test1 ) (t);<br>return 0;<br>}<br>//======================================================<br>/////////////////////////////////////////////////////////////////<br>从源?return ((_X.*_Ptr)()); " 可以看到stl直接q回该函数所q回倹{?br>所以函数没有返回|即ؓvoidӞ的话~译器就会报错。好Q那么如果我们在<br>q里只是直接执行函数而不用returnq回的话~译器应该可以通过了?br><br>嗯,boost中正是这么处理的。(btw.Z更ؓ通用Qboost对stl原有仿函数及l定作了大量的改q工作)?br>但是具体应该怎么区分有没有返回值呢Q这个也ҎQ我们只需用到模板的偏Ҏ就?br>以做到。下面就看看boost的实?btw.boost有两U版本,我用的是兼容版本Q代码难?<br><br>2. boost的实?q里我把boost的一大堆宏(真@$@#@#隄Qloki在这斚w来得比较清爽Q去掉了):<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& X) const<br>{<br>return ((X.*_Ptr)());<br>}<br>};<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& X) const<br>{<br>((X.*_Ptr)());<br>}<br>};<br>};<br>// 创徏一zc?z于上q基c?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>// 通过函数的参数推D动获取类?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( &CFoo::test1 ) (t);<br>return 0;<br>}<br>/////////////////////////////////////////////////////////////////<br><br>从上qC码可以看到偏Ҏ帮助我们解决了q回gؓvoid的情c但是手写了两䆾<br>基本相同的代码。。?br>另外处理参数个数的情况也很容易,只要分别实现不同参数的各个模板类可以了Q?br>boost最多只能支持成员函数有8个参敎ͼ因ؓ它内部实C8份这样不同参数模板类?br>其实的处理方法都是一模一LQ可是由于语a的限制我们还是没有办法不一一实现<br>不同参数的类:。在loki中参数可以用TList实现L的参敎ͼ但是在实现还是得?br>老实实的每䆾手写一份(loki实现?5份可以支?5个参敎ͼ?br>q真让h郁闷。。。不q没办法?br><br>说完来仿函数Q下面开始说说有关绑定,stl、boost、loki的绑定的意思是<br>Ҏ物实体的“l定”Q通俗来说是指对函数、构造函数、仿函数{与其对应的某个参数的绑定,<br>以便在调用时不用再次输入此参敎ͼ因ؓ某些时候参数是固定的,比如说绑定一个内部存?br>成员函数地址的仿函数和它对应的对象地址在一P?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; // q里用了boost<br>CFoo obj;<br>f = std::bind1st(std::mem_fun(&CFoo::test), &obj);<br>f(5);<br>}<br>//================================<br>loki中的BindFirst比较cM于stl的binder<br>(binder1stQbinder2nd)Q但是它是通用的,可以通过嵌套实现L多个参数l定Q?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中的实现是以占位W来表现,具体如何实现Q下回l讨?嗯,<br>boost代码的宏太多了,q部份还是等有空再补全了Q现在我们来看看如何实现一个委托类)<br><br>三。委托类的实?<br>1. 桥接模式:<br>设计模式告诉我们可以使用桥接模式(Bridge Pattern)减少对象之间?耦合度,桥接模式如下:<br>Invoker <>------------------->* Interface<br>^<br>|<br>Receiver<br>上图的Invoker表示事g触发者,Receiver表示事g处理者,W号cM于<c++大规模编E。。>一书所描述Q注Q这里符号对位可能出错:变成左对齐了Q,<br>其中<>------------------->表示Invoker 内含(拥用)Interface(即Invoker 有Interface的变量或指针q负责Interface的释?Q?br>?可C可有多个?br>^<br>|  号则表示l承于(Receiverl承于InterfaceQ?br><br>好,我们先来分析前面? W?U方? 回调c?虚函数多态法):"的实现思想(请回到前面看看代?Q?br>它其实就是一个桥接模式,如下(括号内对应前面所实现的类)Q?br>Invoker(CInput) <>--------------->* InterfaceQIWillBackQ?br>^<br>|<br>ReceiverQCUIQ?br>对照我们前面实现的代码可以发现此U实现的桥接的缺ҎQ每一个想?br>注册一Ҏ到Invoker中以便Invoker在事件触发时调用的类(如Receiver)都要z自Interface?br>有没有更好的办法再次减少q种耦合度呢Q这正是下面我们要讨<br>论的下一U设计模式:<br>2. 委托与事?<br>委托的处理设计如?<br>Invoker <>--------------------->* Interface<br>^<br>|<br>Implementation -----------------> Receiver<br>卛_原桥接模式下再加一层间接?Implementation 。其?br>Implementation与Receiver之间?---------------->表示Implementation引用了Receiver一些服务,<br>即Implementation用到了Receiver某些东西Q如函数或数据)。嗯Q这些解释不知是否适当Q希望不会误对{。?br>好,一开始可能我们会q么设计:<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->*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 (&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>一个改q的版本如下:<br>//-------------------------------------------------------------<br>// signal slot system<br>// ? 该法我是看了"落木随风"?委托、信号和消息反馈的模板实现技?Q?br>// 代码作了部䆾d。在q里非常的感谢他!<br>// 他的博客Qhttp://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. 桥接c?U虚c?:<br>// Z么叫作桥?<br>// 因ؓ通过它的虚函数方法可以调用到对应的正的不同z实例(指后?br>// 提到的委托h)所改写的虚函数Ҏ(q是委托人用来完成委托Q务的Ҏ)<br>struct kDelegationInterface<br>{<br>virtual ~kDelegationInterface() {};<br>virtual void Execute() = 0;<br>};<br>// 2. 委托c?z于桥接类Q这里我叫ؓ”委托?#8220;)<br>// Z么叫委托Q?br>// 因ؓ调用者把“通知”的工作委托给它来负责处理?br>// 一?#8220;委托?#8221;保存? a.”接收?#8220;(对象指针m_pThis) ?b.“要作的事”(Ҏ指针m_mfn)Q?br>// 以便调用者发Z号弹(后面提到,信号Ҏ一个作桥接用的U虚cȝ指针指向相应的委托h)<br>// 告知此信号对应的委托人来完成它被委托的工作:卌“接收?#8221;(m_pThis)?#8221;要作的事“(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->*m_mfn )(); <br>}<br>}<br>T* m_pThis;<br>MFN m_mfn;<br>};<br><br>// 3. 信号?实现Z函数来调用统一的虚函数接口):<br>// Z么叫信号Q?br>// 因ؓ?信号?发射?调用信号的操作符"()")<br>// 它会通知所指向?委托?事g发生?调用U虚cL针的m_DI->Execute()Ҏ)?br>// 一个信号保存了一个指向对?#8221;委托?#8220;的桥接类(U虚c?指针?br>struct kSignal0<br>{<br>kDelegationInterface* m_DI; // U虚cȝ指针<br>kSignal0() : m_DI(0) {}<br>// 1. U虚cȝ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. 用统一的纯虚类指针调用不同zcL写的虚函?br>void operator() () { <br>if( m_DI ) {<br>m_DI->Execute(); <br>}<br>}<br>};<br><br>// 下面是两个ؓ方便使用的函?<br>template<br>void kConnect( kSignal0& sgn, T* pObj, void(T::*fn)())<br>{<br>sgn.ConnectSlot( pObj, fn );<br>int i = 0;<br>}<br>inline void kDisConnect( kSignal0& 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, &kDialog::DoWork ); // vc6下这里kDialog::DoWork的前面一定要可加"&"?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, &dlg, kDialog::DoWork ); // vc6下这里kDialog::DoWork的前面可?不加"&"?br><br>// 试一:<br>btn.OnMouseButtonUp();<br><br>// 试?<br>dlg.TestMouseHit();<br>return 0;<br>}<br><br>// 委托实例ȝQ?br>// 下面我们来具体说?#8221;当某事发生时Q调用者发信号弹通知对应的接收者作相应处理“<br>// 1. "调用? 拥有各种信号式V?br>// 2. 初始Ӟ我们把信号弹与对应的委托pv来,q让委托录在信号触发时应该通知?接收??接收作的??br>// a. 信号弹保存了?U虚c?指针Q指针指向通过其模板函数ConnectSlotҎ来找?产生?委托?委托实例)?br>// b. 委托?委托实例)在信号弹用ConnectSlotҎ产生它的时候保存了函数ConnectSlot所传入的两个参敎ͼ<br>// ?接收者指??其方法指??br>// 3. 当事件发生时"调用?发射对应信号弹后Q信号弹会调用其所保存的纯虚类指针的虚函数ҎQ?br>// 于是׃虚函数特性就会调用到其所指向的委托实?委托?所改写的方法?br>// 5. 委托人改写的Ҏ中通过其所保存?#8221;接收者指?#8220;及其"Ҏ指针"来呼?接收?用对应的”Ҏ指针“<br>// 来处理事情?br>// 卛_下流E?<br>// "调用?发射"信号? ---> "信号?通过"?扑ֈ对应"委托? ---> "委托?呼叫"接收??该作的事"<br><br>//=============================================================================<br>嗯,q样到此Q一个非常方便的委托cd得以实现了!如果你还不懂的话请仔l的琢磨Q如此精?因ؓ单而强?<br>不要错过。不q上q只是部份实玎ͼ当你要支持带参数及返回值的各种情况的话Q还得自׃扩充?br>q回值的处理Ҏ可参见前面剖qboost的mem_fn的处理方法,而带不同参数的处理则只能一一手动<br>实现Q就象前面所说的那样Q这是很无奈的事情,但是目前来说没有办法。。?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更新Q?br>修正:<br>原void connect( Signal0& sgn,T1 obj, void(T2::*fn)())Ҏ<br>void connect( Signal0& sgn,T1& obj, void(T2::*fn)())<br>另外加了kDisConnect释放内存Q原来只作测试没写它Q现在还是加上了?br><br>--------------------------------------------------------------------------------<br><br>千里马肝<br>实在是太好了<br><br>PSQ阳春的东西Q就会曲高合寡?:D<br><br>--------------------------------------------------------------------------------<br><br>flipcode<br>好久没上|了Q?br>今天上来看到版主的夸奖,<br>虽然是过奖了Q不q还是感觉轻飘飘的。。。嘿嘿~~~Q?:D<br><br>--------------------------------------------------------------------------------<br><br>kane<br>嗯,q种技巧是比较实用的?img src ="http://www.shnenglu.com/richardhe/aggbug/52015.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/richardhe/" target="_blank">RichardHe</a> 2008-06-03 11:39 <a href="http://www.shnenglu.com/richardhe/articles/52015.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何?6q制字符串{换成整型Q?/title><link>http://www.shnenglu.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.shnenglu.com/richardhe/articles/50068.html</guid><wfw:comment>http://www.shnenglu.com/richardhe/comments/50068.html</wfw:comment><comments>http://www.shnenglu.com/richardhe/articles/50068.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/richardhe/comments/commentRss/50068.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/richardhe/services/trackbacks/50068.html</trackback:ping><description><![CDATA[<span style="color: #000000;">转自http://www.shnenglu.com/alantop/archive/2008/05/15/49989.html<br> </span><span style="color: #0000ff;">char</span><span style="color: #000000;"> test[] </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #000000;">"</span><span style="color: #000000;">0xf</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br> </span><span style="color: #0000ff;">long</span><span style="color: #000000;"> lResult </span><span style="color: #000000;">=</span><span style="color: #000000;"> strtol(test, </span><span style="color: #000000;">'</span><span style="color: #000000;">\0</span><span style="color: #000000;">'</span><span style="color: #000000;">, </span><span style="color: #000000;">16</span><span style="color: #000000;">);</span><img src ="http://www.shnenglu.com/richardhe/aggbug/50068.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/richardhe/" target="_blank">RichardHe</a> 2008-05-16 17:44 <a href="http://www.shnenglu.com/richardhe/articles/50068.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>callback functionhttp://www.shnenglu.com/richardhe/articles/49933.htmlRichardHeRichardHeThu, 15 May 2008 06:54:00 GMThttp://www.shnenglu.com/richardhe/articles/49933.htmlhttp://www.shnenglu.com/richardhe/comments/49933.htmlhttp://www.shnenglu.com/richardhe/articles/49933.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/49933.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/49933.html声明函数指针

回调函数是一个程序员不能昑ּ调用的函敎ͼ通过回调函数的地址传给调用者从而实现调用。要实现回调Q必首先定义函数指针。尽定义的语法有点不可思议Q但如果你熟悉函数声明的一般方法,便会发现函数指针的声明与函数声明非常cM。请看下面的例子Q?br>
void f()Q?/ 函数原型

上面的语句声明了一个函敎ͼ没有输入参数q返回void。那么函数指针的声明Ҏ如下Q?br>
void (*) ();

让我们来分析一下,左边圆括弧中的星h函数指针声明的关键。另外两个元素是函数的返回类型(voidQ和p圆括弧中的入口参敎ͼ本例中参数是I)。注意本例中q没有创建指针变?只是声明了变量类型。目前可以用q个变量cd来创建类型定义名及用sizeof表达式获得函数指针的大小Q?br>
// 获得函数指针的大?br>unsigned psize = sizeof (void (*) ());

// 为函数指针声明类型定?br>typedef void (*pfv) ();

pfv是一个函数指针,它指向的函数没有输入参数Q返回类行ؓvoid。用这个类型定义名可以隐藏复杂的函数指针语法?br>
指针变量应该有一个变量名Q?br>
void (*p) (); //p是指向某函数的指?br>
p是指向某函数的指针,该函数无输入参数Q返回值的cd为void。左边圆括弧里星号后的就是指针变量名。有了指针变量便可以赋|值的内容是v名匹配的函数名和q回cd。例如:

void func()
{
/* do something */
}
p = func;

p的赋值可以不同,但一定要是函数的地址Qƈ且v名和q回cd相同?br>
传递回调函数的地址l调用?br>
现在可以p传递给另一个函敎ͼ调用者)- caller()Q它调用p指向的函敎ͼ而此函数名是未知的:

void caller(void(*ptr)())
{
ptr(); /* 调用ptr指向的函?*/
}
void func();
int main()
{
p = func;
caller(p); /* 传递函数地址到调用?*/
}

如果赋了不同的值给pQ不同函数地址Q,那么调用者将调用不同地址的函数。赋值可以发生在q行Ӟq样使你能实现动态绑定?br>
调用规范

到目前ؓ止,我们只讨Z函数指针及回调而没有去注意ANSI C/C++的编译器规范。许多编译器有几U调用规范。如在Visual C++中,可以在函数类型前加_cdeclQ_stdcall或者_pascal来表C其调用规范Q默认ؓ_cdeclQ。C++ Builder也支持_fastcall调用规范。调用规范媄响编译器产生的给定函数名Q参C递的序Q从叛_左或从左到右Q,堆栈清理责QQ调用者或者被调用者)以及参数传递机Ӟ堆栈QCPU寄存器等Q?br>
调用规范看成是函数cd的一部分是很重要的;不能用不兼容的调用规范将地址赋值给函数指针。例如:

// 被调用函数是以int为参敎ͼ以int回?br>__stdcall int callee(int);

// 调用函数以函数指针ؓ参数
void caller( __cdecl int(*ptr)(int));

// 在p中企囑֭储被调用函数地址的非法操?br>__cdecl int(*p)(int) = callee; // 出错


指针p和callee()的类型不兼容Q因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针pQ尽两者有相同的返回值和参数列?br>

RichardHe 2008-05-15 14:54 发表评论
]]>
《设计模式精解》读书笔?/title><link>http://www.shnenglu.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.shnenglu.com/richardhe/articles/49843.html</guid><wfw:comment>http://www.shnenglu.com/richardhe/comments/49843.html</wfw:comment><comments>http://www.shnenglu.com/richardhe/articles/49843.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/richardhe/comments/commentRss/49843.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/richardhe/services/trackbacks/49843.html</trackback:ping><description><![CDATA[<div id="aesmqwe" class="postTitle"> <a id="AjaxHolder_ctl01_TitleUrl" class="postTitle2">《设计模式精解》读书笔?<?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: 宋体;">、模式是应该l合在一起共同解决问题的?/span><span lang="EN-US"><o:p></o:p></span></strong></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论Q以前看?/span><span lang="EN-US">GOF</span><span style="font-family: 宋体;">设计模式》的时候主要就是看目的Q然后就是代码,对协作啊Q优~点啊,效果啊都不怎么注意Q不q就看了也不知所云。学下来像背数学公式一P单个是记住了Q拿到具体环境里又懵了。看来还是得多花Ҏ间看看模式的兌啊?/span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p> </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: 宋体;">、我的错误是Q我试先创建问题领域中的篏Q然后将q些cȝ合v来Ş成最l的pȝ。—?/span><span lang="EN-US">Alexander</span></strong><strong><span style="font-family: 宋体;">把这Lq程UCؓ一?#8220;坏主?#8221;。我从来没有问过我自己:我是否拥有正的c?仅仅因ؓq些cȝh如此正确、如此明显。我拥有的是在开始分析时立刻q入脑v中的c,是我们的老师告诉我们应该在系l中L?#8220;名词”。但是我的错误就是仅仅尝试把它们单地攑֜一赗?/span><span lang="EN-US"><o:p></o:p></span></strong></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论Q连大牛都会犯这L错误Q我q样的菜鸟这样分析系l应该也比较正常吧。在看问题的层次上,我们太过于重视细节,像我写</span><span lang="EN-US">WinForm</span><span style="font-family: 宋体;">E序时先考虑有哪些组件可以用一P把思\l限制住了,忽视了类所处的环境、上下文?/span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p> </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: 宋体;">、面向对象的范式Q用对象将责Q转义到更局部的范围。对象应该对自己负责Qƈ且这U责d该被清楚的定义出来?/span><span lang="EN-US"><o:p></o:p></span></strong></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论Q这个对cȝ定义p合理的多。作者认Y件开发中有三个视角:概念Q用例)Q?/span><span lang="EN-US">></span><span style="font-family: 宋体;">规格Q接口)Q?/span><span lang="EN-US">></span><span style="font-family: 宋体;">实现Q代码)。从最低层ơ看Q类是变量</span><span lang="EN-US">+</span><span style="font-family: 宋体;">函数Q但q样太狭隘了。真正的对象应该与现实生zM的对象相|有自q行ؓ、状态,<strong>自己对自p?/strong>Q多么精的概述啊)?/span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p> </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: 宋体;">、抽象类是其他cȝ占位W?/span><span lang="EN-US"><o:p></o:p></span></strong></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论Q比较Ş象的描述。我先描q类的大致行Z调用者了解,具体的行为留到子cd玎ͼ抽象cM子类达成一个契U?/span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p> </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: 宋体;">评论Q同</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> </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: 宋体;">评论Q牛人就是幽默。这里主要是说明如何感觉一个设计是坏的设计的。我是说最q写E序胃老不舒服Q饭也吃不好……</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p> </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: 宋体;">评论Q作者自始至l一直强调是场景军_解决ҎQ而不是ؓ了实现某个模式而去q行设计Q否则就像小学生套公式一样了。场景分析好了,模式自然也就出来了(q是境界比较高的时候)?/span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p> </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>   </span>a</span></strong><strong><span style="font-family: 宋体;">、发现ƈ装变化炏V?/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>   </span>b</span></strong><strong><span style="font-family: 宋体;">、优先用对象组合,而非cȝѝ?/span><span lang="EN-US"><o:p></o:p></span></strong></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论Q关于共同点</span><span lang="EN-US">/</span><span style="font-family: 宋体;">变化点的分析Q在</span><span lang="EN-US">Bridge</span><span style="font-family: 宋体;">模式中体现的犹ؓ明显Q我在前面的</span><span lang="EN-US">Blog</span><span style="font-family: 宋体;">中介l过Q,借助分析矩阵也可以帮助理清思\。?/span><span lang="EN-US">b</span><span style="font-family: 宋体;">Ҏ面向对象设计中的基本原则Q就不用多说了?/span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p> </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: 宋体;">语句常常标志着Q(</span><span lang="EN-US">1</span></strong><strong><span style="font-family: 宋体;">Q对多态行为的需要。(</span><span lang="EN-US">2</span></strong><strong><span style="font-family: 宋体;">Q存在着N地方的责仅R请考虑用一U更普适的解决Ҏ代替</span><span lang="EN-US">switch</span></strong><strong><span style="font-family: 宋体;">语句Q比如抽象、将责Q分配l另外的对象{等?/span><span lang="EN-US"><o:p></o:p></span></strong></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论Q老听别h说丑陋的</span><span lang="EN-US">switch</span><span style="font-family: 宋体;">语句?/span><span lang="EN-US">ifelse</span><span style="font-family: 宋体;">语句Q都不是太懂Q现在好像有Ҏ了,因ؓ条g语句不灵z,很难l护吧。碰到它马上p惛_用多态的方式d理,?/span><span lang="EN-US">State</span><span style="font-family: 宋体;">模式?/span><span lang="EN-US">Visitor</span><span style="font-family: 宋体;">模式和q有兟?/span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p> </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: 宋体;">步骤Q?/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: 宋体;">Q?/span><span lang="EN-US">1</span></strong><strong><span style="font-family: 宋体;">Q、发现我在问题领域中拥有的模?/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: 宋体;">Q?/span><span lang="EN-US">2</span></strong><strong><span style="font-family: 宋体;">Q、对于这些需要分析的模式Q做下列工作Q?/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>   </span>a</span></strong><strong><span style="font-family: 宋体;">、挑Zؓ其他模式提供最多场景的模式?/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>   </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>   </span>c</span></strong><strong><span style="font-family: 宋体;">、识别Q何可能已l出现的附加模式。将它们d?#8220;需要分析的模式”中?/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>   </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: 宋体;">Q?/span><span lang="EN-US">3</span></strong><strong><span style="font-family: 宋体;">Q、按照需要将l节d的设计中。扩展方法和cd义?/span><span lang="EN-US"><o:p></o:p></span></strong></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论Q具体的说就是找到可以统领全局的模式,再用其他的模式配合它Q?#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: 宋体;">。算了吧Q我q?/span><span lang="EN-US">OOP</span><span style="font-family: 宋体;">都没学好Q这个以后再说啦?/span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p> </o:p></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span lang="EN-US">11</span><span style="font-family: 宋体;">、在学习面向对象技术的早期学习设计模式Q可以大大帮助你提高寚w向对象分析、设计的理解?/span></strong></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">评论Q这个倒是W一ơ听_不过怎么也得先把l承、封装、多态弄清楚吧。再加上一条,在学习设计模式时Q一定要先读q本《设计模式精解》,再看?/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.shnenglu.com/richardhe/aggbug/49843.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/richardhe/" target="_blank">RichardHe</a> 2008-05-14 17:53 <a href="http://www.shnenglu.com/richardhe/articles/49843.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OOD原则QSRP、OCP以及LSPhttp://www.shnenglu.com/richardhe/articles/49813.htmlRichardHeRichardHeWed, 14 May 2008 05:21:00 GMThttp://www.shnenglu.com/richardhe/articles/49813.htmlhttp://www.shnenglu.com/richardhe/comments/49813.htmlhttp://www.shnenglu.com/richardhe/articles/49813.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/49813.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/49813.html
单一职责原则QSRP : Single Response Principle)

׃个类而言Q应该仅有一个引起它变化的原因?br>在这里,职责的定义是Q? “变化的原?#8221;?/font>

对于何时遵@SRP有以下的考虑Q?br>1.如果应用E序的变化会影响到类中某一U职责,那么应该将它与另一U职责分开Q这样做可以避免客户应用E序和类中的q两职责耦合在一赗?br>2.如果应用E序的变化L会导致两个职责同时变化,那么׃必要分离它们。实际上Q分d们会引v不必要的复杂性?br>
从上可以得知Q变化的轴线仅当变化实际发生时才h真正的意义。如果没有征兆,那么d用SRPQ或者Q何其它原则都是不明智?br>
实际应用Q持久化QPersistenceQ?/strong>
? 际开发中Q考虑C务规则是会频J改变的Q而持久化的方式却不会如此频繁的变化,q且变化的原因也是完全不同的。如果把业务规则和持久化方式l定CP ׃Z后的开发、维护造成ȝ。运用分层(layerQ架构模式或者TDD开发方式可以很早分这两个职责Q特D情况下Q还可以使用FACADE或? PROXY模式对设计进行重构,分离q两个职责?br>
开闭原则(OCP : The Open-Close Principle)

描述QY件实体(cR模型、函数等{)应该是可以扩展的Q但是不可修攏V?br>
遵@开闭原则设计出的模块具有两个主要的特征。它们是Q?br>1. “对于扩展是开攄”QOpen for extensionQ?br>   q意味着模块的行为是可以扩展的。当应用的需要改变时Q我们可以对模块q行扩展Q其具有满那些改变的新行为。换句话_我们可以改变模块的功能?br>2. “对于更改是封闭的”QClosed for modificationQ?br>   Ҏ块行行扩展时Q不必改动模块的源代码或者二q制代码。模块的二进制可执行版本Q无论是可链接的库、DLL或者Java?jar文gQ都无需改动?br>
对于OCPQ关键的? 抽象
模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,所以它对于更改可以是关闭的。同Ӟ通过从这个抽象体zQ也可以扩展此模块的行ؓ?br>
在许多方面,OCP都是面向对象设计的核心所在。但实际应用中,滥用OCP原则也是错误的。正的做法是应该仅仅对E序中呈现出频繁变化的那些部分做出抽象。拒l不成熟的抽象和抽象本n一样重要?/div>

Liskov替换原则QLSPQ?/font>

描述Q子cdQsubtypeQ必能够替换掉它们的基cdQbase typeQ?br>
此原则是Barbara Liskov首次?988q写下的。所以就叫做Liskov替换原则。她如此写到Q?br>“q里需要如下替换性质Q若Ҏ个类型S的对象o1Q都存在一个类型T的对象o2Q得在所有针对T~写的程序P中,用o1替换o2后,E序P行ؓ功能不变Q则S是T的子cd?br>
LSP然我们得Z个非帔R要的l论Q一个模型,如果孤立的看Qƈ不具有真正意义上的有效性。模型的有效性只能通过它的客户E序来表现?br>
在考虑一个特定设计是否恰当时Q不能完全孤立的来看q个解决Ҏ。必要Ҏ该设计的使用者所做出的合理假设来审视它?br>
? 谁知道设计的使用者会做出什么样的合理假讑֑Q大多数q样的假N很难预测。事实上Q如果试囑֎预测所有这些假设,我们所得到的系l很可能会充满不必要? 复杂性的臭味。因此,像所有其它原则一样了Q通常最好的办法是只预那些最明显的对于LSP的违反情况,而推q所有其它的预测Q直到出现相关的脆弱性的 臭味Ӟ才去处理它们?br>
IS-A是关于行为的?/font>
LSP清晰的指出,OOD中ISQA关系是就行ؓ方式而言的,行ؓ方式是可以进行合理假讄Q是客户E序所依赖的?br>
Z契约设计
? 于契U设计(DBCQDesign By ContractQ。用DBCQ类的编写者能够显式的规定针对该类的契U。客户代码的~写者可以通过该契U获悉可以依赖的行ؓ方式。契U是通过为每个方 法声明的前置条gQpreconditions)和后|条Ӟpostconditions)来指定的。要使一个方法得以执行,前置条g必须要ؓ真。执? 完毕后,该方法要保证后置条g为真?br>
在单元测试中指定契约
也可以通过~写单元试的方式来指定契约。客户代码编写者会L看这些单元测试,q样他们可以知道对于要使用的类Q应该做什么合理的假设?br>
启发式规则和习惯用法

1.zcM的退化函?br>  在基cM实现了f()ҎQ在zcM的函数f()是退化的Q派生类中的退化函数ƈ不总表CZؓq反LSPQ但是当存在q种情况Ӟq是值得注意一下的?br>2.从派生类中抛出异?br>  在派生类的方法中d了其基类不会抛出的异常。如果基cȝ使用者不期望q些异常Q那么把它们d到派生类的方法中׃D不可替换性。此时要遵@LSPQ要么就必须改变使用者的期望Q要么派生类׃应该抛出q些异常?br>
? l:OCP是OOD中很多原则的核心。如果这个原则应用的有效Q应用程序就会具有更多的可维护性、可重用性以及健壮性。LSP是OCP成ؓ可能的主要原 则之一。正是子cd的可替换性才使得使用基类cd的模块在无需修改的情况下可以扩展。这U可替换性必L开发h员可以隐式依赖的。因此,如果没有昑ּ? 强制基类cd的契U,那么代码必良好的q且明显的表辑ևq一炏V?br>      术语IS-A的含义过于广泛以至于不能作ؓ子类型的定义。子cd的正定义是“可替换?#8221;Q这里的可替换性可以通过昑ּ或者隐式的契约来定义?/div>



RichardHe 2008-05-14 13:21 发表评论
]]>理解: 面向对象的设计原则与设计模式http://www.shnenglu.com/richardhe/articles/49794.htmlRichardHeRichardHeWed, 14 May 2008 02:37:00 GMThttp://www.shnenglu.com/richardhe/articles/49794.htmlhttp://www.shnenglu.com/richardhe/comments/49794.htmlhttp://www.shnenglu.com/richardhe/articles/49794.html#Feedback0http://www.shnenglu.com/richardhe/comments/commentRss/49794.htmlhttp://www.shnenglu.com/richardhe/services/trackbacks/49794.html记得2004q刚接触设计模式Q买了经典的<<设计模式>>一书,l细地阅读,然后在开发中模仿。一两年旉q去Q对23U设? 模式弄得q算比较熟悉Q也在Y件设计中能用则用Q比如Singleton, template method, proxy, facade{等。但L觉用的不爽,当时也说不出原因Q就是感觉在使用的过E中Q是一Uؓ了用设计模式而用上他们Q有时候是生搬套。MQ自己当 时是搞不清楚Z么要使用设计模式Q停留在别h说它牛,我就学着用而不落h之后?
    我不是一个天质聪颖的人,对Y件设计的理解Q基本上无法评自p力单独领悟出来。只有常常督促自己多买国内外软g专家写的好书Q来学习他们在这些方面的发现和ȝ。靠后天学习来I补先天不I也是没有办法中的办法?br>    l于?007q看C<<java与模?gt;>Q? 书中对设计模式的讨论Qƈ没有特别吸引我的地方Q不q是用java语言来详l讲?3U模式而已Q最多增加一些变体。深深吸引我的是"W二部分 面向对象的设计原?Q? q一部分虽然幅不多Q但清晰地说明了我们Z么要用设计模式,使用设计模式是来解决什么问题的Q用之后我们要辑ֈ什么效果。Y件的生命周期让我们认? 刎ͼ面向对象的设计要解决的核心问题是可维护性和可复用性,特别是可l护性,一个好软g的维护成本远q大于初期开发成本?br>    一个Y件设计的可维护性很差,原因在于Q过于僵、过于脆弱、复用率低、黏度过高。相反,一个好的系l设计应该是可扩展的、灵zȝ、可插入的。在软g发达 国家如美国,一些Y件界的高手,?0世纪80~90q代Q就陆箋提出一些设计原则,q些设计原则是在提高一个系l的可维护性的同时Q提高系l的可复用? 的指导原则:
    1、开闭原则:软g架构应该是对扩展开发,对修改关?br>    2、Liskov替换原则QQ何基cd以出现的地方Q子cM定可以出?br>    3、依赖倒{原则Q要依赖于抽象,不要依赖于实?br>    4、接口隔d则:应当为客h供尽可能的接口Q而不是提供大的接口?br>    5、组合、聚合复用原则:要尽量用组合、聚合,而不是承关pM辑ֈ复用的目的?br>    6、Demeter法则Q一个Y件实体应该与可能少的其他实体发生互怽用?br>    可以_<<java与模?gt;>里很好的解决了我心中两三q来的不快,让我真正理解了ؓ什么要使用设计模式。Y件开发、设计原? 与设计模式的关系Q我不恰当的比喻为战争、战略与战术的关p;要取得战争的全面胜利Q你首先要在战略上把握好Q然后才是战术上指挥好;同样Q要开发出好的 软gQ我们首先要遵@一定的设计原则Qؓ了达到我们的目的Q在开发中我们恰当的使用相应的设计模式?br>    <<java与模?gt;>写的好,而且是中国h写的Q但它缺了一个方面的描述Q我们怎样在实际的设计开发中做到q些呢?是不是一开始就要遵循这些设计原则,p使用设计模式Q还是有一个什么样的过E?
    l于今年初看C英文?lt;<敏捷软g开发:原则、模式与实践>>Q? 国外q些大牛是大牛Q在q方面就是理解深刻,实践l验丰富。作者的观点很有点唯物L证法Q就是Y件设计开始时Q我们如果没有看出抽象的必要Q可以先实现 一个简单的Q当W一ơ被需求触发而显现出抽象的必要时Q我们这时机会就来了Q需要很快提取抽象接口,遵@以上设计原则。当Ӟ作者还有很多其它好的思想Q? q里不一一列D?br>    认识和理解都需要一个过E,没有理论Q我们这些智商一般的人是很难仅仅在项目开发中快提高的;同样Q光看书不实践,我们也很隄正理解这些别人ȝ的经验,那将是雾里看花?br>    丰富l验和Y件设计思想以及软g工程思想Q对软g开发的重要性真的是如此重要Q怪不得我们国家无法开发出大型的高质量的Y件来?br>


RichardHe 2008-05-14 10:37 发表评论
]]>
ۿþ| þֹƷۺ| ҹӰþ| þþƷһպ| ˳˳ۺþþ| ޾ƷŮþþþ99С˵ | ĻþþƷˮ| þþþ| ȾþùŷһƷ| þþþþ޾Ʒ| Ʒݾþþþø99 | þþƷ| ۺϾþþƷɫ| þҹ³˿Ƭϼ| ɫͷվþ| þòþüƵ7| ޾þþһ | þþžžþƷ| Ʒ99þaaaһëƬ| þþþƷSmվ| ҹƷþþþþ˳ | 91þþƷ91þɫ| 999þþѾƷ| ˾þþƷӰԺ| ξþ99Ʒþþþþ | AV12þ| þþƷaĻ þþƷaĻؿ | þþþƷѹĻ| ˾Ʒþ| ޹Ʒþ| þþƷһ| Ʒþþþþô| ŷþþþ | þþƷ69Ʒ| 69Ʒþþþ99| һþۺ³³| һɫۺþ| ݺɫ˾þþƷۺ| þþƷAVɫ| ھƷžžþþþƷ| þþƷһ |