<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-梦幻白桦林-文章分类-C++</title><link>http://www.cppblog.com/colys/category/3604.html</link><description>LIFE AS CODE</description><language>zh-cn</language><lastBuildDate>Tue, 20 May 2008 19:07:25 GMT</lastBuildDate><pubDate>Tue, 20 May 2008 19:07:25 GMT</pubDate><ttl>60</ttl><item><title>插花问题的“动态规划法”算法[转]</title><link>http://www.cppblog.com/colys/articles/33753.html</link><dc:creator>colys</dc:creator><author>colys</author><pubDate>Mon, 08 Oct 2007 05:40:00 GMT</pubDate><guid>http://www.cppblog.com/colys/articles/33753.html</guid><wfw:comment>http://www.cppblog.com/colys/comments/33753.html</wfw:comment><comments>http://www.cppblog.com/colys/articles/33753.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/colys/comments/commentRss/33753.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/colys/services/trackbacks/33753.html</trackback:ping><description><![CDATA[// Layout.cpp : Defines the entry point for the console application.<br>/*<br>&nbsp;作者：成晓旭<br>&nbsp;时间：2001年10月11日(11:35:38-12:35:00)<br>&nbsp;内容：完成插花问题的&#8220;动态规划法&#8221;算法及注解<br>*/<br>#include "stdafx.h"<br>#include "string.h"<br>#define MAX(A,B) ((A) &gt; (B) ? (A):(B))<br>//--------------------鲜花问题--------------------<br>#define F 100<br>#define V 100<br>/*<br>&nbsp;插花问题描述:<br>&nbsp;&nbsp;&nbsp;将f束鲜花插入v个花瓶中,使达到最徍的视觉效果,<br>&nbsp;&nbsp;问题相关约定及插花要求:<br>&nbsp;&nbsp;&nbsp;鲜花被编号为1--f,花瓶被编号为1--v,花瓶按从小到<br>&nbsp;&nbsp;大顺序排列,一只花瓶只能插一支花,鲜花i插入花瓶j中的<br>&nbsp;&nbsp;视觉效果效果值已知,编号小的鲜花所放入的花瓶编号也小&nbsp;<br>&nbsp;问题求解思路:<br>&nbsp;&nbsp;&nbsp;花瓶j(1&lt;=j&lt;=v)中插入鲜花的可能编号为[1..j](编号<br>&nbsp;&nbsp;小的鲜花所放入的花瓶编号也小);<br>&nbsp;&nbsp;&nbsp;设数组p[i][j]表示鲜花i插入花瓶j的好看程度,数组<br>&nbsp;&nbsp;q[i][j]表示[1..i]束鲜花插入[1..j]个花瓶所能得到的最大<br>&nbsp;&nbsp;好看程度,初始化q[0][0] = 0;q[0][j]=0(1&lt;=j&lt;=v),则q[f][v]<br>&nbsp;&nbsp;是问题的解.<br>&nbsp;&nbsp;&nbsp;特别地,j束鲜花插入到前面的j只花瓶中,所得到的好看<br>&nbsp;&nbsp;程度是q[j][j] = p[1][1]+p[2][2]+...+[j][j].现将插花过<br>&nbsp;&nbsp;程按花瓶排列顺序划分成不同阶段,则在第j阶段,第i束鲜花<br>&nbsp;&nbsp;若放入第j号花瓶,最大好看程度是q[i-1][j-1]+p[i][j];第i束鲜<br>&nbsp;&nbsp;花若放入前j-1个花瓶中的某一个,所得的好看程度是q[i][j-1],<br>&nbsp;&nbsp;那么在第j阶段,插入第i束鲜花所能得到的最大好看程度为:<br>&nbsp;&nbsp;q[i][j] = MAX(q[i-1][j-1]+p[i][j],q[i][j-1]),要使q[i][j]<br>&nbsp;&nbsp;最大,应使q[i-1][j-1]和q[i][j-1]也最大<br>*/<br>//初始化函数<br>void Initialize(int *f,int *v,int p[][V])<br>{<br>&nbsp;int i,j;<br>&nbsp;printf("输入鲜花数量及花瓶个数:");<br>&nbsp;scanf("%d%d",f,v);<br>&nbsp;printf("顺序输入各鲜花插入各花瓶的好看程度:\n");<br>&nbsp;for(i=1;i&lt;=*f;i++)<br>&nbsp;&nbsp;for(j=1;j&lt;=*v;j++)<br>&nbsp;&nbsp;&nbsp;p[i][j] = i+j;<br>&nbsp;&nbsp;&nbsp;//scanf("%d",&amp;p[i][j]);<br>}<br>//鲜花问题处理函数<br>int Sove(int p[][V],int f,int v,int *way)<br>{<br>&nbsp;int i,j,newv,q[F][V];<br>&nbsp;q[0][0] = 0;<br>&nbsp;/*设置v个花瓶分别被插入v束鲜花时各号花瓶对应的(初始)最大好看程度*/<br>&nbsp;for(j=1;j&lt;=v;j++)<br>&nbsp;{<br>&nbsp;&nbsp;q[0][j] = 0;<br>&nbsp;&nbsp;/*设置第j束鲜花放入第j号花瓶中的最大好看程度*/<br>&nbsp;&nbsp;q[j][j] = q[j-1][j-1]+p[j][j];<br>&nbsp;}<br>&nbsp;for(j=1;j&lt;=v;j++)<br>&nbsp;&nbsp;for(i=1;i&lt;j;i++)<br>&nbsp;&nbsp;&nbsp;q[i][j] = MAX(q[i-1][j-1]+p[i][j],q[i][j-1]);<br>&nbsp;newv = v;<br>&nbsp;for(i=f;i&gt;0;i--)<br>&nbsp;{<br>&nbsp;&nbsp;while(q[i-1][newv-1]+p[i][newv] &lt; q[i][newv])<br>&nbsp;&nbsp;&nbsp;newv--;<br>&nbsp;&nbsp;//确定鲜花i插在花瓶newv中,并准备考虑前一只花瓶　<br>&nbsp;&nbsp;way[i] = newv--;<br>&nbsp;}<br>&nbsp;return(q[f][v]);<br>}<br>//--------------------鲜花问题--------------------<br>//--------------------最长子串问题--------------------<br>#define N 100<br>char a[N],b[N],str[N];<br>//计算两个序列最长公共子序列的长度<br>int Get_LongSubStr_Len(char *a,char *b,int c[][N])<br>{<br>&nbsp;int m=strlen(a),n=strlen(b),//两个序列的长度<br>&nbsp;&nbsp;i,j;//循环变量<br>&nbsp;for(i=0;i&lt;=m;i++)&nbsp;c[i][0] = 0;<br>&nbsp;for(i=1;i&lt;=n;i++)&nbsp;c[0][i] = 0;<br>&nbsp;for(i=1;i&lt;=m;i++)<br>&nbsp;&nbsp;for(j=1;j&lt;=n;j++)<br>&nbsp;&nbsp;&nbsp;if(a[i-1]==b[j-1])<br>&nbsp;&nbsp;&nbsp;&nbsp;c[i][j] = c[i-1][j-1]+1;<br>&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;&nbsp;c[i][j] = MAX(c[i-1][j],c[i][j-1]);<br>&nbsp;&nbsp;&nbsp;/*<br>&nbsp;&nbsp;&nbsp;&nbsp;if(c[i-1][j]&gt;=c[i][j-1])<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c[i][j] = c[i-1][j];<br>&nbsp;&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c[i][j] = c[i][j-1];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/<br>&nbsp;return(c[m][n]);<br>}<br>//构造最长公共子序列<br>char *Build_LongSubStr(char s[],char *a,char *b)<br>{<br>&nbsp;int i=strlen(a),j=strlen(b),<br>&nbsp;&nbsp;k,c[N][N];<br>&nbsp;k = Get_LongSubStr_Len(a,b,c);<br>&nbsp;s[k] = '\0';<br>&nbsp;while(k&gt;0)<br>&nbsp;{<br>&nbsp;&nbsp;if(c[i][j]==c[i-1][j])<br>&nbsp;&nbsp;&nbsp;i--;<br>&nbsp;&nbsp;else<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;if(c[i][j]==c[i][j-1])<br>&nbsp;&nbsp;&nbsp;&nbsp;j--;<br>&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;s[--k]=a[i-1];<br>&nbsp;&nbsp;&nbsp;&nbsp;i--;<br>&nbsp;&nbsp;&nbsp;&nbsp;j--;<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}<br>&nbsp;}<br>&nbsp;return(s);<br>}<br>//--------------------最长子串问题--------------------<br>int main(int argc, char* argv[])<br>{<br>&nbsp;int i,f,v,p[F][V],way[F];<br>&nbsp;//-----------------------------------<br>&nbsp;/*<br>&nbsp;Initialize(&amp;f,&amp;v,p);<br>&nbsp;printf("最大好看程度为%d\n",Sove(p,f,v,way));<br>&nbsp;printf("插有鲜花的花瓶是:\n");<br>&nbsp;for(i=1;i&lt;=f;i++)<br>&nbsp;&nbsp;printf("%4d",way[i]);<br>&nbsp;*/<br>&nbsp;//-----------------------------------<br>&nbsp;printf("输入两个字符串(长度&lt;%d):\n",N);<br>&nbsp;scanf("%s%s",a,b);<br>&nbsp;printf("两个串的最长公共子序列是:%s\n",Build_LongSubStr(str,a,b));<br>&nbsp;//-----------------------------------<br>&nbsp;printf("\n\n应用程序正在运行......\n");<br>&nbsp;return 0;<br>}
<br><img src ="http://www.cppblog.com/colys/aggbug/33753.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/colys/" target="_blank">colys</a> 2007-10-08 13:40 <a href="http://www.cppblog.com/colys/articles/33753.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]ini文件纯C++读写代码</title><link>http://www.cppblog.com/colys/articles/28387.html</link><dc:creator>colys</dc:creator><author>colys</author><pubDate>Thu, 19 Jul 2007 14:21:00 GMT</pubDate><guid>http://www.cppblog.com/colys/articles/28387.html</guid><wfw:comment>http://www.cppblog.com/colys/comments/28387.html</wfw:comment><comments>http://www.cppblog.com/colys/articles/28387.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/colys/comments/commentRss/28387.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/colys/services/trackbacks/28387.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: //////////////////////////////////////////////////////////////////&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&n...&nbsp;&nbsp;<a href='http://www.cppblog.com/colys/articles/28387.html'>阅读全文</a><img src ="http://www.cppblog.com/colys/aggbug/28387.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/colys/" target="_blank">colys</a> 2007-07-19 22:21 <a href="http://www.cppblog.com/colys/articles/28387.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>成员函数指针与高性能的C++委托(下)</title><link>http://www.cppblog.com/colys/articles/25793.html</link><dc:creator>colys</dc:creator><author>colys</author><pubDate>Thu, 07 Jun 2007 16:18:00 GMT</pubDate><guid>http://www.cppblog.com/colys/articles/25793.html</guid><wfw:comment>http://www.cppblog.com/colys/comments/25793.html</wfw:comment><comments>http://www.cppblog.com/colys/articles/25793.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/colys/comments/commentRss/25793.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/colys/services/trackbacks/25793.html</trackback:ping><description><![CDATA[委托（delegate）<br>　　<br>　　和成员函数指针不同，你不难发现委托的用处。最重要的，使用委托可以很容易地实现一个 Subject/Observer设计模式的改进版[GoF, p. 293]。Observer（观察者）模式显然在GUI中有很多的应用，但我发现它对应用程序核心的设计也有很大的作用。委托也可用来实现策略（Strategy）[GoF, p. 315]和状态（State）[GoF, p. 305]模式。<br>　　<br>　　现在，我来说明一个事实，委托和成员函数指针相比并不仅仅是好用，而且比成员函数指针简单得多！既然所有的.NET语言都实现了委托，你可能会猜想如此高层的概念在汇编代码中并不好实现。但事实并不是这样：委托的实现确实是一个底层的概念，而且就像普通的函数调用一样简单（并且很高效）。一个C++委托只需要包含一个this 指针和一个简单的函数指针就够了。当你建立一个委托时，你提供这个委托一个this指针，并向它指明需要调用哪一个函数。编译器可以在建立委托时计算出调整this指针需要的偏移量。这样在使用委托的时候，编译器就什么事情都不用做了。这一点更好的是，编译器可以在编译时就可以完成全部这些工作，这样的话，委托的处理对编译器来说可以说是微不足道的工作了。在x86系统下将委托处理成的汇编代码就应该是这么简单：<br>　　<br>　　mov ecx, [this]<br>　　<br>　　call [pfunc]<br>　　<br>　　但是，在标准C++中却不能生成如此高效的代码。 Borland为了解决委托的问题在它的C++编译器中加入了一个新的关键字（__closure）,用来通过简洁的语法生成优化的代码。GNU编译器也对语言进行了扩展，但和Borland的编译器不兼容。如果你使用了这两种语言扩展中的一种，你就会限制自己只使用一个厂家的编译器。而如果你仍然遵循标准C++的规则，你仍然可以实现委托，但实现的委托就不会是那么高效了。<br>　　<br>　　有趣的是，在C#和其他.NET语言中，执行一个委托的时间要比一个函数调用慢8倍（参见http://msdn.microsoft.com/library/en- us/dndotnet/html/fastmanagedcode.asp）。我猜测这可能是垃圾收集和.NET安全检查的需要。最近，微软将&#8220;统一事件模型（unified event model）&#8221;加入到Visual C++中，随着这个模型的加入，增加了__event、 __raise、__hook、__unhook、event_source和event_receiver等一些关键字。坦白地说，我对加入的这些特性很反感，因为这是完全不符合标准的，这些语法是丑陋的，因为它们使这种C++不像C++，并且会生成一堆执行效率极低的代码。<br>　　<br>　　解决这个问题的推动力：对高效委托（fast delegate）的迫切需求<br>　　<br>　　使用标准C++实现委托有一个过度臃肿的症状。大多数的实现方法使用的是同一种思路。这些方法的基本观点是将成员函数指针看成委托��但这样的指针只能被一个单独的类使用。为了避免这种局限，你需要间接地使用另一种思路：你可以使用模版为每一个类建立一个&#8220;成员函数调用器（member function invoker）&#8221;。委托包含了this指针和一个指向调用器（invoker）的指针，并且需要在堆上为成员函数调用器分配空间。<br>　　<br>　　对于这种方案已经有很多种实现，包括在CodeProject上的实现方案。各种实现在复杂性上、语法（比如，有的和C#的语法很接近）上、一般性上有所不同。最具权威的一个实现是boost::function。最近，它已经被采用作为下一个发布的C++标准版本中的一部分[Sutter1]。希望它能够被广泛地使用。<br>　　<br>　　就像传统的委托实现方法一样，我同样发觉这种方法并不十分另人满意。虽然它提供了大家所期望的功能，但是会混淆一个潜在的问题：人们缺乏对一个语言的底层的构造。 &#8220;成员函数调用器&#8221;的代码对几乎所有的类都是一样的，在所有平台上都出现这种情况是令人沮丧的。毕竟，堆被用上了。但在一些应用场合下，这种新的方法仍然无法被接受。<br>　　<br>　　我做的一个项目是离散事件模拟器，它的核心是一个事件调度程序，用来调用被模拟的对象的成员函数。大多数成员函数非常简单：它们只改变对象的内部状态，有时在事件队列（event queue）中添加将来要发生的事件，在这种情况下最适合使用委托。但是，每一个委托只被调用（invoked）一次。一开始，我使用了boost:: function，但我发现程序运行时，给委托所分配的内存空间占用了整个程序空间的三分之一还要多！&#8220;我要真正的委托！&#8221;我在内心呼喊着，&#8220;真正的委托只需要仅仅两行汇编指令啊！&#8221;<br>　　<br>　　我并不能总是能够得到我想要的，但后来我很幸运。我在这儿展示的代码（代码下载链接见译者注）几乎在所有编译环境中都产生了优化的汇编代码。最重要的是，调用一个含有单个目标的委托（single-target delegate）的速度几乎同调用一个普通函数一样快。实现这样的代码并没有用到什么高深的东西，唯一的遗憾就是，为了实现目标，我的代码和标准C++ 的规则有些偏离。我使用了一些有关成员函数指针的未公开知识才使它能够这样工作。如果你很细心，而且不在意在少数情况下的一些编译器相关（compiler-specific）的代码，那么高性能的委托机制在任何C++编译器下都是可行的。<br>　　<br>　　诀窍：将任何类型的成员函数指针转化为一个标准的形式<br>　　<br>　　我的代码的核心是一个能够将任何类的指针和任何成员函数指针分别转换为一个通用类的指针和一个通用成员函数的指针的类。由于C++没有&#8220;通用成员函数（geneic member function）&#8221;的类型，所以我把所有类型的成员函数都转化为一个在代码中未定义的CGenericClass类的成员函数。<br>　　<br>　　大多数编译器对所有的成员函数指针平等地对待，不管他们属于哪个类。所以对这些编译器来说，可以使用reinterpret_cast将一个特定的成员函数指针转化为一个通用成员函数指针。事实上，假如编译器不可以，那么这个编译器是不符合标准的。对于一些接近标准（almost-compliant）的编译器，比如Digital Mars，成员函数指针的reinterpret_cast转换一般会涉及到一些额外的特殊代码，当进行转化的成员函数的类之间没有任何关联时，编译器会出错。对这些编译器，我们使用一个名为horrible_cast的内联函数（在函数中使用了一个union来避免C++的类型检查）。使用这种方法看来是不可避免的��boost::function也用到了这种方法。<br>　　<br>　　对于其他的一些编译器（如Visual C++, Intel C++和Borland C++），我们必须将多重（multiple-）继承和虚拟（virtual-）继承类的成员函数指针转化为单一（single-）继承类的函数指针。为了实现这个目的，我巧妙地使用了模板并利用了一个奇妙的戏法。注意，这个戏法的使用是因为这些编译器并不是完全符合标准的，但是使用这个戏法得到了回报：它使这些编译器产生了优化的代码。<br>　　<br>　　既然我们知道编译器是怎样在内部存储成员函数指针的，并且我们知道在问题中应该怎样为成员函数指针调整this指针，我们的代码在设置委托时可以自己调整this指针。对单一继承类的函数指针，则不需要进行调整；对多重继承，则只需要一次加法就可完成调整；对虚拟继承...就有些麻烦了。但是这样做是管用的，并且在大多数情况下，所有的工作都在编译时完成！<br>　　<br>　　这是最后一个诀窍。我们怎样区分不同的继承类型？并没有官方的方法来让我们区分一个类是多重继承的还是其他类型的继承。但是有一种巧妙的方法，你可以查看我在前面给出了一个列表（见中篇）——对MSVC，每种继承方式产生的成员函数指针的大小是不同的。所以，我们可以基于成员函数指针的大小使用模版！比如对多重继承类型来说，这只是个简单的计算。而在确定unknown_inheritance（16字节）类型的时候，也会采用类似的计算方法。<br>　　<br>　　对于微软和英特尔的编译器中采用不标准12字节的虚拟继承类型的指针的情况，我引发了一个编译时错误（compile-time error），因为需要一个特定的运行环境（workaround）。如果你在MSVC中使用虚拟继承，要在声明类之前使用 FASTDELEGATEDECLARE宏。而这个类必须使用unknown_inheritance（未知继承类型）指针（这相当于一个假定的 __unknown_inheritance关键字）。例如：<br>　　<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">FASTDELEGATEDECLARE(CDerivedClass)<br>　　<br>　　</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;CDerivedClass&nbsp;:&nbsp;</span><span style="color: #0000ff;">virtual</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;CBaseClass1,&nbsp;</span><span style="color: #0000ff;">virtual</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;CBaseClass2&nbsp;{<br>　　<br>　　</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;:&nbsp;(etc)</span><span style="color: #008000;"><br></span><span style="color: #000000;">　　<br>　　};</span></div>
<br>　　<br>　　这个宏和一些常数的声明是在一个隐藏的命名空间中实现的，这样在其他编译器中使用时也是安全的。MSVC（7.0或更新版本）的另一种方法是在工程中使用/vmg编译器选项。而Inter的编译器对/vmg编译器选项不起作用，所以你必须在虚拟继承类中使用宏。我的这个代码是因为编译器的bug才可以正确运行，你可以查看代码来了解更多细节。而在遵从标准的编译器中不需要注意这么多，况且在任何情况下都不会妨碍FASTDELEGATEDECLARE宏的使用。<br>　　<br>　　一旦你将类的对象指针和成员函数指针转化为标准形式，实现单一目标的委托（single-target delegate）就比较容易了（虽然做起来感觉冗长乏味）。你只要为每一种具有不同参数的函数制作相应的模板类就行了。实现其他类型的委托的代码也大都与此相似，只是对参数稍做修改罢了。<br>　　<br>　　这种用非标准方式转换实现的委托还有一个好处，就是委托对象之间可以用等式比较。目前实现的大多数委托无法做到这一点，这使这些委托不能胜任一些特定的任务，比如实现多播委托（multi-cast delegates） [Sutter3]。<br>　　<br>　　静态函数作为委托目标（delegate target）<br>　　<br>　　理论上，一个简单的非成员函数（non-member function），或者一个静态成员函数（static member function）可以被作为委托目标（delegate target）。这可以通过将静态函数转换为一个成员函数来实现。我有两种方法实现这一点，两种方法都是通过使委托指向调用这个静态函数的&#8220;调用器（invoker）&#8221;的成员函数的方法来实现的。<br><img src ="http://www.cppblog.com/colys/aggbug/25793.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/colys/" target="_blank">colys</a> 2007-06-08 00:18 <a href="http://www.cppblog.com/colys/articles/25793.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>成员函数指针与高性能的C++委托(上)</title><link>http://www.cppblog.com/colys/articles/25790.html</link><dc:creator>colys</dc:creator><author>colys</author><pubDate>Thu, 07 Jun 2007 16:17:00 GMT</pubDate><guid>http://www.cppblog.com/colys/articles/25790.html</guid><wfw:comment>http://www.cppblog.com/colys/comments/25790.html</wfw:comment><comments>http://www.cppblog.com/colys/articles/25790.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/colys/comments/commentRss/25790.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/colys/services/trackbacks/25790.html</trackback:ping><description><![CDATA[&nbsp;<br><br><span style="font-weight: bold;">引子</span><br><br>标准C++中没有真正的面向对象的函数指针。这一点对C++来说是不幸的，因为面向对象的指针（也叫做&#8220;闭包（closure）&#8221;或&#8220;委托（delegate）&#8221;）在一些语言中已经证明了它宝贵的价值。在Delphi (Object Pascal)中，面向对象的函数指针是Borland可视化组建库（VCL，Visual Component Library）的基础。而在目前，C#使&#8220;委托&#8221;的概念日趋流行，这也正显示出C#这种语言的成功。在很多应用程序中，&#8220;委托&#8221;简化了松耦合对象的设计模式[GoF]。这种特性无疑在标准C++中也会产生很大的作用。<br><br>很遗憾，C++中没有&#8220;委托&#8221;，它只提供了成员函数指针（member function pointers）。很多程序员从没有用过函数指针，这是有特定的原因的。因为函数指针自身有很多奇怪的语法规则（比如&#8220;-&gt;*&#8221;和&#8220;.*&#8221;操作符），而且很难找到它们的准确含义，并且你会找到更好的办法以避免使用函数指针。更具有讽刺意味的是：事实上，编译器的编写者如果实现&#8220;委托&#8221;的话会比他费劲地实现成员函数指针要容易地多！<br><br>在这篇文章中，我要揭开成员函数指针那&#8220;神秘的盖子&#8221;。在扼要地重述成员函数指针的语法和特性之后，我会向读者解释成员函数指针在一些常用的编译器中是怎样实现的，然后我会向大家展示编译器怎样有效地实现&#8220;委托&#8221;。最后我会利用这些精深的知识向你展示在C++编译器上实现优化而可靠的&#8220;委托&#8221;的技术。比如，在Visual C++(6.0, .NET, and .NET 2003)中对单一目标委托（single-target delegate）的调用，编译器仅仅生成两行汇编代码！<br><br>函数指针<br><br>下面我们复习一下函数指针。在C和C++语言中，一个命名为my_func_ptr的函数指针指向一个以一个int和一个char*为参数的函数，这个函数返回一个浮点值，声明如下：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">float</span><span style="color: #000000;">&nbsp;(</span><span style="color: #000000;">*</span><span style="color: #000000;">my_func_ptr)(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">char</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">);</span></div>
<br><br>//为了便于理解，我强烈推荐你使用typedef关键字。<br><br>//如果不这样的话，当函数指针作为一个函数的参数传递的时候，<br><br>// 程序会变得晦涩难懂。<br><br>// 这样的话，声明应如下所示：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">typedef&nbsp;</span><span style="color: #0000ff;">float</span><span style="color: #000000;">&nbsp;(</span><span style="color: #000000;">*</span><span style="color: #000000;">MyFuncPtrType)(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">char</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">);<br><br>MyFuncPtrType&nbsp;my_func_ptr;</span></div>
<br><br>应注意，对每一个函数的参数组合，函数指针的类型应该是不同的。在Microsoft Visual C++（以下称MSVC）中，对三种不同的调用方式有不同的类型：__cdecl, __stdcall, 和__fastcall。如果你的函数指针指向一个型如float some_func(int, char *)的函数，这样做就可以了：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">my_func_ptr&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;some_func;<br><br></span><span style="color: #008000;">//</span><span style="color: #008000;">当你想调用它所指向的函数时，你可以这样写：</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>(</span><span style="color: #000000;">*</span><span style="color: #000000;">my_func_ptr)(</span><span style="color: #000000;">7</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Arbitrary&nbsp;String</span><span style="color: #000000;">"</span><span style="color: #000000;">);</span></div>
<br><br>你可以将一种类型的函数指针转换成另一种函数指针类型，但你不可以将一个函数指针指向一个void *型的数据指针。其他的转换操作就不用详叙了。一个函数指针可以被设置为0来表明它是一个空指针。所有的比较运算符（==, !=, &lt;, &gt;, &lt;=, &gt;=）都可以使用，可以使用&#8220;==0&#8221;或通过一个显式的布尔转换来测试指针是否为空（null）。<br><br>在C语言中，函数指针通常用来像qsort一样将函数作为参数，或者作为Windows系统函数的回调函数等等。函数指针还有很多其他的应用。函数指针的实现很简单：它们只是&#8220;代码指针（code pointer）&#8221;，它们体现在汇编语言中是用来保存子程序代码的首地址。而这种函数指针的存在只是为了保证使用了正确的调用规范。<br><br>成员函数指针<br><br>在C++程序中，很多函数是成员函数，即这些函数是某个类中的一部分。你不可以像一个普通的函数指针那样指向一个成员函数，正确的做法应该是，你必须使用一个成员函数指针。一个成员函数的指针指向类中的一个成员函数，并和以前有相同的参数，声明如下：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">float</span><span style="color: #000000;">&nbsp;(SomeClass::</span><span style="color: #000000;">*</span><span style="color: #000000;">my_memfunc_ptr)(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">char</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">);<br><br></span><span style="color: #008000;">//</span><span style="color: #008000;">对于使用const关键字修饰的成员函数，声明如下：</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">float</span><span style="color: #000000;">&nbsp;(SomeClass::</span><span style="color: #000000;">*</span><span style="color: #000000;">my_const_memfunc_ptr)(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">char</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">)&nbsp;</span><span style="color: #0000ff;">const</span><span style="color: #000000;">;</span></div>
<br><br>注意使用了特殊的运算符（::*），而&#8220;SomeClass&#8221;是声明中的一部分。成员函数指针有一个可怕的限制：它们只能指向一个特定的类中的成员函数。对每一种参数的组合，需要有不同的成员函数指针类型，而且对每种使用const修饰的函数和不同类中的函数，也要有不同的函数指针类型。在MSVC中，对下面这四种调用方式都有一种不同的调用类型：__cdecl, __stdcall, __fastcall, 和 __thiscall。（__thiscall是缺省的方式，有趣的是，在任何官方文档中从没有对__thiscall关键字的详细描述，但是它经常在错误信息中出现。如果你显式地使用它，你会看到&#8220;它被保留作为以后使用（it is reserved for future use）&#8221;的错误提示。）如果你使用了成员函数指针，你最好使用typedef以防止混淆。<br><br>将函数指针指向型如float SomeClass::some_member_func(int, char *)的函数，你可以这样写：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">my_memfunc_ptr&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">SomeClass::some_member_func;</span></div>
<br><br>很多编译器（比如MSVC）会让你去掉&#8220;&amp;&#8221;，而其他一些编译器（比如GNU G++）则需要添加&#8220;&amp;&#8221;，所以在手写程序的时候我建议把它添上。若要调用成员函数指针，你需要先建立SomeClass的一个实例，并使用特殊操作符&#8220;-&gt;*&#8221;，这个操作符的优先级较低，你需要将其适当地放入圆括号内。<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">SomeClass&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">x&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;SomeClass;<br><br>(x</span><span style="color: #000000;">-&gt;*</span><span style="color: #000000;">my_memfunc_ptr)(</span><span style="color: #000000;">6</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Another&nbsp;Arbitrary&nbsp;Parameter</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br><br></span><span style="color: #008000;">//</span><span style="color: #008000;">如果类在栈上，你也可以使用&#8220;.*&#8221;运算符。</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>SomeClass&nbsp;y;<br><br>(y.</span><span style="color: #000000;">*</span><span style="color: #000000;">my_memfunc_ptr)(</span><span style="color: #000000;">15</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Different&nbsp;parameters&nbsp;this&nbsp;time</span><span style="color: #000000;">"</span><span style="color: #000000;">);</span></div>
<br><br>不要怪我使用如此奇怪的语法——看起来C++的设计者对标点符号有着由衷的感情！C++相对于C增加了三种特殊运算符来支持成员指针。&#8220;::*&#8221;用于指针的声明，而&#8220;-&gt;*&#8221;和&#8220;.*&#8221;用来调用指针指向的函数。这样看起来对一个语言模糊而又很少使用的部分的过分关注是多余的。（你当然可以重载&#8220;-&gt;*&#8221;这些运算符，但这不是本文所要涉及的范围。）<br><br>一个成员函数指针可以被设置成0，并可以使用&#8220;==&#8221;和&#8220;!=&#8221;比较运算符，但只能限定在同一个类中的成员函数的指针之间进行这样的比较。任何成员函数指针都可以和0做比较以判断它是否为空。与函数指针不同，不等运算符（&lt;, &gt;, &lt;=, &gt;=）对成员函数指针是不可用的。<br><br>成员函数指针的怪异之处<br><br>成员函数指针有时表现得很奇怪。首先，你不可以用一个成员函数指针指向一个静态成员函数，你必须使用普通的函数指针才行（在这里&#8220;成员函数指针&#8221;会产生误解，它实际上应该是&#8220;非静态成员函数指针&#8221;才对）。其次，当使用类的继承时，会出现一些比较奇怪的情况。比如，下面的代码在MSVC下会编译成功（注意代码注释）：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">＃i&nbsp;nclude&nbsp;&#8220;stdio.h&#8221;<br><br></span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;SomeClass&nbsp;{<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br><br></span><span style="color: #0000ff;">virtual</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;some_member_func(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;x,&nbsp;</span><span style="color: #0000ff;">char</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">p)&nbsp;{<br><br>printf(</span><span style="color: #000000;">"</span><span style="color: #000000;">In&nbsp;SomeClass</span><span style="color: #000000;">"</span><span style="color: #000000;">);&nbsp;};<br><br>};<br><br></span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;DerivedClass&nbsp;:&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;SomeClass&nbsp;{<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br><br></span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;如果你把下一行的注释销掉，带有&nbsp;line&nbsp;(*)的那一行会出现错误<br><br></span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;virtual&nbsp;void&nbsp;some_member_func(int&nbsp;x,&nbsp;char&nbsp;*p)&nbsp;{&nbsp;printf("In&nbsp;DerivedClass");&nbsp;};</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>};<br><br></span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;main()&nbsp;{<br><br></span><span style="color: #008000;">//</span><span style="color: #008000;">声明SomeClass的成员函数指针</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>typedef&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;(SomeClass::</span><span style="color: #000000;">*</span><span style="color: #000000;">SomeClassMFP)(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">char</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">);<br><br>SomeClassMFP&nbsp;my_memfunc_ptr;<br><br>my_memfunc_ptr&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">DerivedClass::some_member_func;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;----&nbsp;line&nbsp;(*)</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br><br>}</span></div>
<br><br>奇怪的是，&amp;DerivedClass::some_member_func是一个SomeClass类的成员函数指针，而不是DerivedClass类的成员函数指针！（一些编译器稍微有些不同：比如，对于Digital Mars C++，在上面的例子中，&amp;DerivedClass::some_member_func会被认为没有定义。）但是，如果在DerivedClass类中重写（override）了some_member_func函数，代码就无法通过编译，因为现在的&amp;DerivedClass::some_member_func已成为DerivedClass类中的成员函数指针！<br><br>成员函数指针之间的类型转换是一个讨论起来非常模糊的话题。在C++的标准化的过程中，在涉及继承的类的成员函数指针时，对于将成员函数指针转化为基类的成员函数指针还是转化为子类成员函数指针的问题和是否可以将一个类的成员函数指针转化为另一个不相关的类的成员函数指针的问题，人们曾有过很激烈的争论。然而不幸的是，在标准委员会做出决定之前，不同的编译器生产商已经根据自己对这些问题的不同的回答实现了自己的编译器。根据标准（第5.2.10/9节），你可以使用reinterpret_cast在一个成员函数指针中保存一个与本来的类不相关的类的成员函数。有关成员函数指针转换的问题的最终结果也没有确定下来。你现在所能做的还是像以前那样——将成员函数指针转化为本类的成员函数的指针。在文章的后面我会继续讨论这个问题，因为这正是各个编译器对这样一个标准没有达成共识的一个话题。<br><br>在一些编译器中，在基类和子类的成员函数指针之间的转换时常有怪事发生。当涉及到多重继承时，使用reinterpret_cast将子类转换成基类时，对某一特定编译器来说有可能通过编译，而也有可能通不过编译，这取决于在子类的基类列表中的基类的顺序！下面就是一个例子：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Derived:&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Base1,&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Base2&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;情况&nbsp;(a)</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Derived2:&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Base2,&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Base1&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;情况&nbsp;(b)</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>typedef&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;(Derived::</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;Derived_mfp)();<br><br>typedef&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;(Derived2::</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;Derived2_mfp)();<br><br>typedef&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;(Base1::</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;Base1mfp)&nbsp;();<br><br>typedef&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;(Base2::</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;Base2mfp)&nbsp;();<br><br>Derived_mfp&nbsp;x;</span></div>
<br><br>对于情况(a)，static_cast&lt;Base1mfp&gt; (x) 是合法的，而static_cast&lt;Base2mfp&gt; (x) 则是错误的。然而情况(b)却与之相反。你只可以安全地将子类的成员函数指针转化为第一个基类的成员函数指针！如果你要实验一下，MSVC会发出C4407号警告，而Digital Mars C++会出现编译错误。如果用reinterpret_cast代替static_cast，这两个编译器都会发生错误，但是两种编译器对此有着不同的原因。但是一些编译器对此细节置之不理，大家可要小心了！<br><br>标准C++中另一条有趣的规则是：你可以在类定义之前声明它的成员函数指针。这对一些编译器会有一些无法预料的副作用。我待会讨论这个问题，现在你只要知道要尽可能得避免这种情况就是了。<br><br>需要值得注意的是，就像成员函数指针，标准C++中同样提供了成员数据指针（member data pointer）。它们具有相同的操作符，而且有一些实现原则也是相同的。它们用在stl::stable_sort的一些实现方案中，而对此很多其他的应用我就不再提及了。<br><br>成员函数指针的使用<br><br>现在你可能会觉得成员函数指针是有些奇异。但它可以用来做什么呢？对此我在网上做了非常广泛的调查。最后我总结出使用成员函数指针的两点原因：<br><br>&nbsp;&nbsp;&nbsp; * 用来做例子给<br>&nbsp;&nbsp;&nbsp; * C++初学者看，帮助它们学习语法；或者 为了实现&#8220;委托（<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; delegate）&#8221;！ <br><br>成员函数指针在STL和Boost库的单行函数适配器（one-line function adaptor）中的使用是微不足道的，而且允许你将成员函数和标准算法混合使用。但是它们最重要的应用是在不同类型的应用程序框架中，比如它们形成了MFC消息系统的核心。<br><br>当你使用MFC的消息映射宏（比如ON_COMMAND）时，你会组装一个包含消息ID和成员函数指针（型如：CCmdTarget::*成员函数指针）的序列。这是MFC类必须继承CCmdTarget才可以处理消息的原因之一。但是，各种不同的消息处理函数具有不同的参数列表（比如OnDraw处理函数的第一个参数的类型为CDC *），所以序列中必须包含各种不同类型的成员函数指针。MFC是怎样做到这一点的呢？MFC利用了一个可怕的编译器漏洞（hack），它将所有可能出现的成员函数指针放到一个庞大的联合（union）中，从而避免了通常需要进行的C++类型匹配检查。（看一下afximpl.h和cmdtarg.cpp中名为MessageMapFunctions的union，你就会发现这一恐怖的事实。）因为MFC有如此重要的一部分代码，所以事实是，所有的编译器都为这个漏洞开了绿灯。（但是，在后面我们会看到，如果一些类用到了多重继承，这个漏洞在MSVC中就不会起作用，这正是在使用MFC时只能必须使用单一继承的原因。）<br><br>在boost::function中有类似的漏洞（但不是太严重）。看起来如果你想做任何有关成员函数指针的比较有趣的事，你就必须做好与这个语言的漏洞进行挑战的准备。要是你想否定C++的成员函数指针设计有缺陷的观点，看来是很难的。<br><br>在写这篇文章中，我有一点需要指明：&#8220;允许成员函数指针之间进行转换（cast），而不允许在转换完成后调用其中的函数&#8221;，把这个规则纳入C++的标准中是可笑的。首先，很多流行的编译器对这种转换不支持（所以，转换是标准要求的，但不是可移植的）。其次，所有的编译器，如果转换成功，调用转换后的成员函数指针时仍然可以实现你预期的功能：那编译器就没有所谓的&#8220;undefined behavior（未定义的行为）&#8221;这类错误出现的必要了（调用（Invocation）是可行的，但这不是标准！）。第三，允许转换而不允许调用是完全没有用处的，只有转换和调用都可行，才能方便而有效地实现委托，从而使这种语言受益。<br><br>为了让你确信这一具有争议的论断，考虑一下在一个文件中只有下面的一段代码，这段代码是合法的：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;SomeClass;<br><br>typedef&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;(SomeClass::</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;SomeClassFunction)(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">);<br><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Invoke(SomeClass&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">pClass,&nbsp;SomeClassFunction&nbsp;funcptr)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(pClass</span><span style="color: #000000;">-&gt;*</span><span style="color: #000000;">funcptr)();<br>};</span></div>
<br><br>注意到编译器必须生成汇编代码来调用成员函数指针，其实编译器对SomeClass类一无所知。显然，除非链接器进行了一些极端精细的优化措施，否则代码会忽视类的实际定义而能够正确地运行。而这造成的直接后果是，你可以&#8220;安全地&#8221;调用从完全不同的其他类中转换过来的成员函数指针。<br><br>为解释我的断言的另一半——转 换并不能按照标准所说的方式进行，我需要在细节上讨论编译器是怎样实现成员函数指针的。我同时会解释为什么使用成员函数指针的规则具有如此严格的限制。获 得详细论述成员函数指针的文档不是太容易，并且大家对错误的言论已经习以为常了，所以，我仔细检查了一系列编译器生成的汇编代码&#8230;&#8230;<br><br><img src ="http://www.cppblog.com/colys/aggbug/25790.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/colys/" target="_blank">colys</a> 2007-06-08 00:17 <a href="http://www.cppblog.com/colys/articles/25790.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++成员函数指针的应用</title><link>http://www.cppblog.com/colys/articles/25785.html</link><dc:creator>colys</dc:creator><author>colys</author><pubDate>Thu, 07 Jun 2007 15:33:00 GMT</pubDate><guid>http://www.cppblog.com/colys/articles/25785.html</guid><wfw:comment>http://www.cppblog.com/colys/comments/25785.html</wfw:comment><comments>http://www.cppblog.com/colys/articles/25785.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/colys/comments/commentRss/25785.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/colys/services/trackbacks/25785.html</trackback:ping><description><![CDATA[<font size="3">&nbsp;C++中，成员指针是最为复杂的语法结构。但在事件驱动和多线程应用中被广泛用于调用回叫函数。在多线程应用中，每个线程都通过指向成员函数的指针来调用该函数。在这样的应用中，如果不用成员指针，编程是非常困难的。 <br><br>　　刚遇到这种语法时也许会让你止步不前。但你会发现，使用恰当的类型定义之后，复杂的语法是可以简化的。本文引导你了解成员函数指针的声明，赋值和调用回叫函数。 <br><br>　　<strong>成员函数指针的声明</strong> <br><br>　　一个成员函数指针包括成员函数的返回类型，后随::操作符类名，指针名和函数的参数。初看上去，语法有点复杂。其实可以把它理解为一个指向原函数的指针，格式是：函数返回类型，类名，::操作符，指针星号，指针名，函数参数。 <br><br>　　一个指向外部函数的指针声明为： <br><br>　　void (*pf)(char *, const char *); <br><br>　　void strcpy(char * dest, const char * source); <br><br>　　pf=strcpy; <br><br>　　一个指向类A成员函数的指针声明为： <br><br>　　void (A::*pmf)(char *, const char *); <br><br>　　声明的解释是：pmf是一个指向A成员函数的指针，返回无类型值，函数带有二个参数，参数的类型分别是char * 和 const char *。除了在星号前增加A:: ，与声明外部函数指针的方法一样。 <br><br>　　<strong>赋值</strong> <br><br>　　给成员指针赋值的方法是将函数名通过指针符号&amp;赋予指针名。如下所示： <br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;"><font>class</font></span><span style="color: #000000;"><font>&nbsp;A<br>{<br><br>　　</font></span><span style="color: #0000ff;"><font>public</font></span><span style="color: #000000;"><font>:<br><br>　　　</font></span><span style="color: #0000ff;"><font>void</font></span><span style="color: #000000;"><font>&nbsp;strcpy(</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>,&nbsp;</font></span><span style="color: #0000ff;"><font>const</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>);<br><br>　　　</font></span><span style="color: #0000ff;"><font>void</font></span><span style="color: #000000;"><font>&nbsp;strcat(</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>,&nbsp;</font></span><span style="color: #0000ff;"><font>const</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>);<br><br>};<br>pmf&nbsp;</font></span><span style="color: #000000;"><font>=</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>&amp;</font></span><span style="color: #000000;"><font>A::strcpy;&nbsp;</font></span></div>
<br><br>　　有些老的编译器可以通过没有&amp;号的赋值方式，但标准C++强制要求加上&amp;号。 <br><br>　　<strong>使用类型定义</strong> <br><br>　　可以用类型定义来隐藏复杂的成员指针语法。例如，下面的语句定义了PMA是一个指向A成员函数的指针，函数返回无类型值，函数参数类型为char * 和 const char *： <br><br>　　typedef void(A::*PMA)(char *, const char *); <br><br>　　PMA pmf= &amp;A::strcat; // pmf是PMF类型(类A成员指针)的变量 <br><br>　　下文会看到使用类型定义特别有利于声明成员指针数组。 <br><br>　　<strong>通过成员指针调用成员函数</strong> <br><br>
可以在不必知道函数名的情况下，通过成员指针调用对象的成员函数。例如，函数dispatcher有一个变量pmf，通过它调用类成员函数，不管它调用
的是strcpy()函数还是strcat()函数。指向外部原函数的指针和指向类成员函数的指针是有很大区别的。后者必须指向被调函数的宿主对象。因
此，除了要有成员指针外，还要有合法对象或对象指针。 <br><br>　　现举例做进一步说明。假设A有二个实例，成员函数指针支持多态性。这样在成员指针调用虚成员函数时是动态处理的(即所谓后联编 - 译注)。注意，不可调用构造和析构函数。示例如下： <br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;"><font>&nbsp;&nbsp;&nbsp; A&nbsp;a1,&nbsp;a2;<br><br>　　A&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>p</font></span><span style="color: #000000;"><font>=</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>&amp;</font></span><span style="color: #000000;"><font>a1;&nbsp;</font></span><span style="color: #008000;"><font>//</font></span><span style="color: #008000;"><font>创建指向A的指针<br><br>　　</font></span><span style="color: #008000;"><font>//</font></span><span style="color: #008000;"><font>创建指向成员的指针并初始化</font></span><span style="color: #008000;"><font><br></font></span><span style="color: #000000;"><font><br>　　</font></span><span style="color: #0000ff;"><font>void</font></span><span style="color: #000000;"><font>&nbsp;(A::</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>pmf)(</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>,&nbsp;</font></span><span style="color: #0000ff;"><font>const</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>)&nbsp;</font></span><span style="color: #000000;"><font>=</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>&amp;</font></span><span style="color: #000000;"><font>A::strcpy;<br><br>　　</font></span><span style="color: #008000;"><font>//</font></span><span style="color: #008000;"><font>要将成员函数绑定到pmf，必须定义呼叫的对象。<br><br>　　</font></span><span style="color: #008000;"><font>//</font></span><span style="color: #008000;"><font>可以用*号引导：</font></span><span style="color: #008000;"><font><br></font></span><span style="color: #000000;"><font><br>　　</font></span><span style="color: #0000ff;"><font>void</font></span><span style="color: #000000;"><font>&nbsp;dispatcher(A&nbsp;a,&nbsp;</font></span><span style="color: #0000ff;"><font>void</font></span><span style="color: #000000;"><font>&nbsp;(A::</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>pmf)(</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>,&nbsp;</font></span><span style="color: #0000ff;"><font>const</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>))<br><br>　　{<br><br>　　　</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;str[</font></span><span style="color: #000000;"><font>4</font></span><span style="color: #000000;"><font>];<br><br>　　　(a.</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>pmf)(str,&nbsp;&#8220;abc&#8221;);&nbsp;</font></span><span style="color: #008000;"><font>//</font></span><span style="color: #008000;"><font>将成员函数绑定到pmf</font></span><span style="color: #008000;"><font><br></font></span><span style="color: #000000;"><font><br>　　}<br><br>　　</font></span><span style="color: #008000;"><font>//</font></span><span style="color: #008000;"><font>或用A的指针表达方式指向成员指针：</font></span><span style="color: #008000;"><font><br></font></span><span style="color: #000000;"><font><br>　　</font></span><span style="color: #0000ff;"><font>void</font></span><span style="color: #000000;"><font>&nbsp;dispatcher(A&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>&nbsp;p,&nbsp;</font></span><span style="color: #0000ff;"><font>void</font></span><span style="color: #000000;"><font>&nbsp;(A::</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>pmf)(</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>,&nbsp;</font></span><span style="color: #0000ff;"><font>const</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>))<br><br>　　{<br><br>　　　</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;str[</font></span><span style="color: #000000;"><font>4</font></span><span style="color: #000000;"><font>];&nbsp;(p</font></span><span style="color: #000000;"><font>-&gt;*</font></span><span style="color: #000000;"><font>pmf)(str,&nbsp;&#8220;abc&#8221;);<br><br>　　}<br><br>　　</font></span><span style="color: #008000;"><font>//</font></span><span style="color: #008000;"><font>函数的调用方法为：</font></span><span style="color: #008000;"><font><br></font></span><span style="color: #000000;"><font><br>　　dispatcher(a,&nbsp;pmf);&nbsp;</font></span><span style="color: #008000;"><font>//</font></span><span style="color: #008000;"><font>&nbsp;.*&nbsp;方式</font></span><span style="color: #008000;"><font><br></font></span><span style="color: #000000;"><font><br>　　dispatcher(</font></span><span style="color: #000000;"><font>&amp;</font></span><span style="color: #000000;"><font>a,&nbsp;pmf);&nbsp;</font></span><span style="color: #008000;"><font>//</font></span><span style="color: #008000;"><font>&nbsp;-&gt;*&nbsp;方式&nbsp;</font></span></div>
<br>高级使用技巧 <br><br>　　以上是成员函数的基本知识。现在介绍它的高级使用技巧。 <br><br>　　<strong>成员指针数组</strong> <br><br>　　在下例，声明了一个含有二个成员指针的数组，并分配类的成员函数地址给成员指针： <br><br>　　PMA pmf[2]= {&amp;A::strcpy, &amp;A::strcat};&nbsp;<br>也就是<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void (A::*PMA[2])(char *, const char *)= {&amp;A::strcpy, &amp;A::strcat}; <br><br>　　这样的数组在菜单驱动应用中很有用。选择菜单项后，应用将调用相应的回叫函数，如下所示： <br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;"><font>&nbsp;&nbsp;&nbsp; enum</font></span><span style="color: #000000;"><font>&nbsp;MENU_OPTIONS&nbsp;{&nbsp;COPY,&nbsp;CONCAT&nbsp;};<br><br>　　</font></span><span style="color: #0000ff;"><font>int</font></span><span style="color: #000000;"><font>&nbsp;main()<br>　　{<br>　　　MENU_OPTIONS&nbsp;option;&nbsp;</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;str[</font></span><span style="color: #000000;"><font>4</font></span><span style="color: #000000;"><font>];<br>　　　</font></span><span style="color: #008000;"><font>//</font></span><span style="color: #008000;"><font>从外部资源读取选项</font></span><span style="color: #008000;"></span><span style="color: #000000;"><font><br>　　　</font></span><span style="color: #0000ff;"><font>switch</font></span><span style="color: #000000;"><font>&nbsp;(option)<br>　　　{<br>　　　　</font></span><span style="color: #0000ff;"><font>case</font></span><span style="color: #000000;"><font>&nbsp;COPY:<br><br>　　　　　(pa</font></span><span style="color: #000000;"><font>-&gt;*</font></span><span style="color: #000000;"><font>pmf[COPY])(str,&nbsp;&#8220;abc&#8221;);<br><br>　　　　　</font></span><span style="color: #0000ff;"><font>break</font></span><span style="color: #000000;"><font>;<br><br>　　　　</font></span><span style="color: #0000ff;"><font>case</font></span><span style="color: #000000;"><font>&nbsp;CONCAT:<br><br>　　　　　(pa</font></span><span style="color: #000000;"><font>-&gt;*</font></span><span style="color: #000000;"><font>pmf[CONCAT])(str,&nbsp;&#8220;abc&#8221;);<br><br>　　　　　</font></span><span style="color: #0000ff;"><font>break</font></span><span style="color: #000000;"><font>;<br><br>　　　　　</font></span><span style="color: #008000;"><font>//</font></span><span style="color: #008000;"><font>&#8230;</font></span><span style="color: #008000;"><font><br></font></span><span style="color: #000000;"><font><br>　　　}<br>　　}&nbsp;</font></span></div>
<br><br>　　<strong>Const 类型的成员函数</strong> <br><br>　　成员指针的类型应该与成员函数类型一致。上面例子中的pmf 可以指向A的任意函数，只要该函数不是const类型。如下所示，如果将touppercase()的地址分配给pmf，将导致编译出错，因为touppercase() 的类型是const。 <br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;"><font>&nbsp;&nbsp; Class&nbsp;A<br>　 {<br><br>　　</font></span><span style="color: #0000ff;"><font> public</font></span><span style="color: #000000;"><font>:<br><br>　　　&nbsp; </font></span><span style="color: #0000ff;"><font>void</font></span><span style="color: #000000;"><font>&nbsp;strpcy(</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>,&nbsp;</font></span><span style="color: #0000ff;"><font>const</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>);<br><br>　　　</font></span><span style="color: #0000ff;"><font>&nbsp; void</font></span><span style="color: #000000;"><font>&nbsp;strcat(</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>,&nbsp;</font></span><span style="color: #0000ff;"><font>const</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>);<br><br>　　　</font></span><span style="color: #0000ff;"><font>&nbsp; void</font></span><span style="color: #000000;"><font>&nbsp;touppercase(</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>,&nbsp;</font></span><span style="color: #0000ff;"><font>const</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>)&nbsp;</font></span><span style="color: #0000ff;"><font>const</font></span><span style="color: #000000;"><font>;<br><br>　　};<br><br>　　pmf</font></span><span style="color: #000000;"><font>=&amp;</font></span><span style="color: #000000;"><font>A::touppercase;&nbsp;</font></span><span style="color: #008000;"><font>//</font></span><span style="color: #008000;"><font>出错，类型不匹配<br><br>　　</font></span><span style="color: #008000;"><font>//</font></span><span style="color: #008000;"><font>解决的方法是声明一个const类型的成员指针：</font></span><span style="color: #008000;"><font><br></font></span><span style="color: #000000;"><font><br>　　</font></span><span style="color: #0000ff;"><font>void</font></span><span style="color: #000000;"><font>&nbsp;(A::pcmf)(</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>,&nbsp;</font></span><span style="color: #0000ff;"><font>const</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #0000ff;"><font>char</font></span><span style="color: #000000;"><font>&nbsp;</font></span><span style="color: #000000;"><font>*</font></span><span style="color: #000000;"><font>)&nbsp;</font></span><span style="color: #0000ff;"><font>const</font></span><span style="color: #000000;"><font>;<br><br>　　pcmf</font></span><span style="color: #000000;"><font>=&amp;</font></span><span style="color: #000000;"><font>A::touppercase;&nbsp;</font></span><span style="color: #008000;"><font>//</font></span><span style="color: #008000;"><font>&nbsp;现在可以了&nbsp;</font></span></div>
<br><br>　　有些差劲的编译器允许一个非const类型的成员指针指向const类型的成员函数。这在标准C++是不允许的。 </font><img src ="http://www.cppblog.com/colys/aggbug/25785.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/colys/" target="_blank">colys</a> 2007-06-07 23:33 <a href="http://www.cppblog.com/colys/articles/25785.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++ typedef 函数指针</title><link>http://www.cppblog.com/colys/articles/25381.html</link><dc:creator>colys</dc:creator><author>colys</author><pubDate>Sun, 03 Jun 2007 07:07:00 GMT</pubDate><guid>http://www.cppblog.com/colys/articles/25381.html</guid><wfw:comment>http://www.cppblog.com/colys/comments/25381.html</wfw:comment><comments>http://www.cppblog.com/colys/articles/25381.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/colys/comments/commentRss/25381.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/colys/services/trackbacks/25381.html</trackback:ping><description><![CDATA[<span style="font-family: 宋体;"><font size="3">函数指针<span lang="EN-US"><o:p></o:p></span></font></span>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3">　　　　　　一个函数在编译时被分配一个入口地址，将这个入口地址称为函数的指针，可<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3">　　　　　　以用一个指针变量指向该函数指针，然后通过该变量来调用函数。<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3">　　　　　　有关说明：<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3">　　　　　　<span lang="EN-US">1</span>、函数指针的声明格式：<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3">　　　　　　　 函数返回值类型（＊指针变量名）（参数类型列表）<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3">　　　　　　<span lang="EN-US"><span>&nbsp;&nbsp; </span></span>或者是：<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3">　　　　　　<span lang="EN-US"><span>&nbsp;&nbsp; </span>typedef</span>　函数返回值类型　（＊指针变量名）（参数类型列表）<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2</span><span style="font-family: 宋体;">、一个函数指针只能指向一种类型的函数，即具有相同的返回值和相同的参　　　　　　　　　　　　<span lang="EN-US"><o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3">　　　　　　　 数的函数<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3">　　　　　　３、关于函数指针的加减运算没有意义<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3">　　　　　　 函数指针数组定义：<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">函数定义：<span lang="EN-US"><o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun1(void *p);<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun2(void *p);<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun3(void *p);<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3">　　　　　　 函数指针数组定义：<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3">　　　　　　 <span lang="EN-US">void(*fun[3])(void*);//typedef void(*pfun)(void*);pfun fun[3];<o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3">　　　　　　 指针赋值：<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>fun[0] = fun1;<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>fun[1] = fun2;<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>fun[2] = fun3;<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">函数调用：<span lang="EN-US"><o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3">　　　　　　 <span lang="EN-US">fun[0](&amp;a);<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//int a;<o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>fun[1](&amp;b);<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//int b;<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>fun[3](&amp;c);<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//int c;<o:p></o:p></font></span></p>
<h5 style="margin: auto 0cm; text-indent: 18pt;"><font face="宋体"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;</span></span><span style="font-weight: normal; font-size: 10.5pt;" lang="EN-US"><span>&nbsp;&nbsp;&nbsp;</span></span><span style="font-weight: normal; font-size: 10.5pt;">声明一个指向成员函数的指针<span lang="EN-US"><o:p></o:p></span></span></font></h5>
<p style="margin-left: 68.25pt; text-indent: 2.1pt;"><span style="font-size: 10.5pt;"><font face="宋体">一个指向成员函数的指针包括成员函数的返回类型，带<em><span lang="EN-US">::</span></em>符号的类名称，函数参数表。虽然这一语法看似复杂，其实它和普通的指针是一样的。指向外部函数的指针可如下声明：<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p style="margin-left: 68.25pt;"><span style="font-size: 10.5pt;" lang="EN-US"><font face="宋体">void (*pf)(char *, const char *);<br>void strcpy(char * dest, const char * source);<br>pf=strcpy;<o:p></o:p></font></span></p>
<p style="margin-left: 68.25pt;"><span style="font-size: 10.5pt;"><font face="宋体">相应指向类<span lang="EN-US">A</span>的成员函数的指针如下表示：<span lang="EN-US"> <o:p></o:p></span></font></span></p>
<pre style="text-indent: 65.1pt;"><span style="font-size: 10.5pt;" lang="EN-US"><font face="宋体">void (A::*pmf)(char *, const char *);<o:p></o:p></font></span></pre>
<p style="margin-left: 65.1pt;"><span style="font-size: 10.5pt;"><font face="宋体">以上<em><span lang="EN-US">pmf</span></em>是指向类<span lang="EN-US">A</span>的一个成员函数的指针，传递两个变量<em><span lang="EN-US">char *</span></em>和 <em><span lang="EN-US">const char *</span></em>，没有返回值。注意星号前面的<span lang="EN-US">A::</span>符号，这和前面的声明是一致的。<span lang="EN-US"> <o:p></o:p></span></font></span></p>
<h5 style="margin: auto 0cm; text-indent: 67pt;"><span style="font-weight: normal;"><font face="宋体">赋值<span lang="EN-US"><o:p></o:p></span></font></span></h5>
<h5 style="margin: auto 0cm auto 64.25pt;"><font face="宋体"><span style="font-weight: normal;">为了给一个指向成员函数的指针赋值，可以采用成员函数名并再其前面加一个<span lang="EN-US">&amp;</span>的方式</span><span style="font-weight: normal; font-size: 10.5pt;" lang="EN-US"><o:p></o:p></span></font></h5>
<h5 style="margin: auto 0cm; text-indent: 65.1pt;"><span style="font-weight: normal; font-size: 10.5pt;"><font face="宋体">使用<span lang="EN-US">typedef<o:p></o:p></span></font></span></h5>
<p style="margin-left: 65.1pt;"><span style="font-size: 10.5pt;"><font face="宋体">你可以使用<span lang="EN-US">typedef</span>来隐藏一些指向成员函数的复杂指针。例如，下面的代码定义了一个类<span lang="EN-US">A</span>中的成员函数的指针<em><span lang="EN-US">PMA</span></em>，并传递<em><span lang="EN-US">char *</span></em>和<em><span lang="EN-US">const char *</span></em>参数。<span lang="EN-US"><o:p></o:p></span></font></span></p>
<pre style="margin-left: 65.1pt;"><span style="font-size: 10.5pt;" lang="EN-US"><font face="宋体">typedef void(A::*PMA)(char *, const char *);<br>PMA pmf= &amp;A::strcat; // use a typedef to define a pointer to member<o:p></o:p></font></span></pre>
<p style="text-indent: 65.1pt;"><span style="font-size: 10.5pt;"><font face="宋体">使用<em><span lang="EN-US">typedef</span></em>特别有用，尤其是对于指向成员函数的数组指针。<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt; text-indent: -21pt;"><span style="font-family: 宋体;" lang="EN-US"><span><font size="3">■</font><span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><font size="3"><span style="font-family: 宋体;" lang="EN-US">void</span><span style="font-family: 宋体;">类型的指针<span lang="EN-US"><o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US">void</span><span style="font-family: 宋体;">含义：<span lang="EN-US"><o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US">void</span><span style="font-family: 宋体;">是&#8220;无类型&#8221;，<span lang="EN-US">void*</span>则为无类型指针，<span lang="EN-US">void*</span>可以指向任何类型的数据。<span lang="EN-US"><o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US">void a</span><span style="font-family: 宋体;">；<span lang="EN-US">//</span>此变量没有任何实际意义，无法编译通过&#8220;<span lang="EN-US">illegal use of type</span>&#8221;<span lang="EN-US"><o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US">void </span><span style="font-family: 宋体;">的作用：<span lang="EN-US"><o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span><span style="font-family: 宋体;">、对程序返回的限定<span lang="EN-US"><o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>2</span><span style="font-family: 宋体;">、对函数参数的限定<span lang="EN-US"><o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><span style="font-family: 宋体;"><font size="3">我们知道，如何指针<span lang="EN-US">p1</span>和<span lang="EN-US">p2</span>的类型相同，那么我们可以直接在<span lang="EN-US">p1</span>和<span lang="EN-US">p2</span>间赋值，如果不同，必须使用强制类型转换。<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><span style="font-family: 宋体;"><font size="3">如：<span lang="EN-US">float *p1;<span>&nbsp;&nbsp; </span>int *p2;<o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt; text-indent: 21pt;"><span style="font-family: 宋体;"><font size="3">若：<span lang="EN-US">p1 = p2; </span>编译出错：&#8220;<span lang="EN-US">can not covert from int* to float*</span>&#8221;<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt; text-indent: 21pt;"><span style="font-family: 宋体;"><font size="3">必须为：<span lang="EN-US">p1 = (float*)p2;<o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">而<span lang="EN-US">void*</span>不同，任何类型的指针都可以直接赋为它，不需要强制类型转换：<span lang="EN-US"><o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">如：<span lang="EN-US">void *p1;<span>&nbsp;&nbsp; </span>int *p2;<o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">可作：<span lang="EN-US">p1 =p2;<o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">无类型可以包容有类型，有类型不能包容无类型：<span lang="EN-US"><o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">必须为：<span lang="EN-US">p2 = (int*)p1;<o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>viod </span><span style="font-family: 宋体;">和<span lang="EN-US"> void*</span>使用规则总结：<span lang="EN-US"><o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 81pt; text-indent: -18pt;"><span style="font-family: 宋体;" lang="EN-US"><span><font size="3">●</font><span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><font size="3"><span style="font-family: 宋体;">如果函数没有返回值，那么应声明为</span><span lang="EN-US"><font face="Times New Roman">void</font></span><span style="font-family: 宋体;">类型</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt;"><font size="3"><span style="font-family: 宋体;">在</span><span lang="EN-US"><font face="Times New Roman">C</font></span><span style="font-family: 宋体;">语言中，凡不加返回值类型限定的函数，就会被编译器作为返回整型值处理。但是许多程序员却误以为其为</span><span lang="EN-US"><font face="Times New Roman">void</font></span><span style="font-family: 宋体;">类型</span><span lang="EN-US"><font face="Times New Roman">. </font></span><st1:personname w:st="on" productid="林锐"><span style="font-family: 宋体;">林锐</span></st1:personname><span style="font-family: 宋体;">博士《高质量</span><span lang="EN-US"><font face="Times New Roman">C/C++</font></span><span style="font-family: 宋体;">编程》中提到：</span><span lang="EN-US"><font face="Times New Roman">&#8220;C++</font></span><span style="font-family: 宋体;">语言有很严格的类型安全检查，不允许上述情况（指函数不加类型声明）发生</span><span lang="EN-US"><font face="Times New Roman">&#8221;</font></span><span style="font-family: 宋体;">。可是编译器并不一定这么认定，譬如在</span><span lang="EN-US"><font face="Times New Roman">Visual C++6.0</font></span><span style="font-family: 宋体;">中上述</span><span lang="EN-US"><font face="Times New Roman">add</font></span><span style="font-family: 宋体;">函数的编译无错也无警告且运行正确，所以不能寄希望于编译器会做严格的类型检查。</span><span style="font-family: ˎ̥;" lang="EN-US"><o:p></o:p></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt;"><font size="3"><span style="font-family: 宋体;">因此，为了避免混乱，我们在编写</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">C/C++</font></span><span style="font-family: 宋体;">程序时，对于任何函数都必须一个不漏地指定其类型。如果函数没有返回值，一定要声明为</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">void</font></span><span style="font-family: 宋体;">类型。这既是程序良好可读性的需要，也是编程规范性的要求。另外，加上</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">void</font></span><span style="font-family: 宋体;">类型声明后，也可以发挥代码的</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">&#8220;</font></span><span style="font-family: 宋体;">自注释</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">&#8221;</font></span><span style="font-family: 宋体;">作用。代码的</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">&#8220;</font></span><span style="font-family: 宋体;">自注释</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">&#8221;</font></span><span style="font-family: 宋体;">即代码能自己注释自己。</span><span style="font-family: ˎ̥;" lang="EN-US"><o:p></o:p></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><font size="3"><span style="font-family: ˎ̥;" lang="EN-US"><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="font-family: 宋体;">●</span><span style="font-family: ˎ̥;"><font face="Times New Roman"> </font></span><span style="font-family: 宋体;">如果函数无参数，那么应声明其参数为</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">void<o:p></o:p></font></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><font size="3"><span style="font-family: ˎ̥;" lang="EN-US"><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="font-family: 宋体;">●</span><span style="font-family: ˎ̥;"><font face="Times New Roman"> </font></span><span style="font-family: 宋体;">小心使用</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">void</font></span><span style="font-family: 宋体;">指针类型</span><span style="font-family: ˎ̥;" lang="EN-US"><o:p></o:p></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><font size="3"><span style="font-family: ˎ̥;" lang="EN-US"><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="font-family: 宋体;">按照</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">ANSI</font></span><span style="font-family: 宋体;">的标准，不能对</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">void</font></span><span style="font-family: 宋体;">指针进行算法操作，即下列操作是不合法的：</span><span style="font-family: ˎ̥;" lang="EN-US"><o:p></o:p></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><span style="font-family: ˎ̥;" lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void *pvoid;<o:p></o:p></font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><font size="3"><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pvoid ++;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//ansi</font></span><span style="font-family: 宋体;">错误</span><span style="font-family: ˎ̥;" lang="EN-US"><o:p></o:p></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><font size="3"><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pvoid += 1;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//ansi </font></span><span style="font-family: 宋体;">错误</span><span style="font-family: ˎ̥;" lang="EN-US"><o:p></o:p></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><font size="3"><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ansi</font></span><span style="font-family: 宋体;">标准之所以这样认定，是因为它坚持，进行算法操作的指针必须是确定知道其指向数据类型的大小的。</span><span style="font-family: ˎ̥;" lang="EN-US"><o:p></o:p></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><font size="3"><span style="font-family: ˎ̥;" lang="EN-US"><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="font-family: 宋体;">但</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">GUN</font></span><span style="font-family: 宋体;">（</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">GUN</font></span><span style="font-family: 宋体;">&#8217;</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">s<span>&nbsp; </span>Not<span>&nbsp; </span>Unix</font></span><span style="font-family: 宋体;">）则不这么认为，它指定</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">void*</font></span><span style="font-family: 宋体;">的算法操作与</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">char*</font></span><span style="font-family: 宋体;">一致。因此在</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">GUN</font></span><span style="font-family: 宋体;">编译器中上述语句是正确的。</span><span style="font-family: ˎ̥;" lang="EN-US"><o:p></o:p></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><font size="3"><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;</span></font></span><span style="font-family: 宋体;">在实际的程序中，为了迎合</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">ansi</font></span><span style="font-family: 宋体;">标准，并提高程序的可移植性，我们可以这样实现同样功能的代码：</span><span style="font-family: ˎ̥;" lang="EN-US"><o:p></o:p></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><span style="font-family: ˎ̥;" lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void *pvoid;<o:p></o:p></font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><span style="font-family: ˎ̥;" lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>(char*)pvoid++;<o:p></o:p></font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><span style="font-family: ˎ̥;" lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>(char*)pvoid += 1;<o:p></o:p></font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><font size="3"><span style="font-family: ˎ̥;" lang="EN-US"><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="font-family: 宋体;">● </span><span style="font-family: 宋体;">如果函数的参数可以是任意类型指针，那么应声明其参数为</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">void *<o:p></o:p></font></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><font size="3"><span style="font-family: ˎ̥;" lang="EN-US"><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="font-family: 宋体;">典型的如内存操作函数</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">memcpy</font></span><span style="font-family: 宋体;">和</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">memset</font></span><span style="font-family: 宋体;">的函数原型分别为：</span><span style="font-family: ˎ̥;" lang="EN-US"><o:p></o:p></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><span style="font-family: ˎ̥;" lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;</span>void* memcpy(void *dest, const void *src, size_t len);<o:p></o:p></font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><span style="font-family: ˎ̥;" lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void* memset(void *buffer,int c, size_t num);<o:p></o:p></font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><font size="3"><span style="font-family: ˎ̥;" lang="EN-US"><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="font-family: 宋体;">这样，任何类型的指针都可以传入</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">memcpy</font></span><span style="font-family: 宋体;">和</span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">memset</font></span><span style="font-family: 宋体;">中，这也真实地体现了内存操作函数的意义，因为它操作的对象仅仅是一片内存，而不论内存是什类型。</span><span style="font-family: ˎ̥;" lang="EN-US"><o:p></o:p></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 84pt; text-indent: -84pt;"><font size="3"><span style="font-family: ˎ̥;" lang="EN-US"><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="font-family: 宋体;">● </span><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman">void</font></span><span style="font-family: 宋体;">不能代表一个真实的变量</span><span style="font-family: ˎ̥;" lang="EN-US"><o:p></o:p></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><font size="3"><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void a; //</font></span><span style="font-family: 宋体;">错误</span><span style="font-family: ˎ̥;" lang="EN-US"><o:p></o:p></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><font size="3"><span style="font-family: ˎ̥;" lang="EN-US"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>function(void a); //</font></span><span style="font-family: 宋体;">错误</span><span style="font-family: ˎ̥;" lang="EN-US"><o:p></o:p></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt; text-indent: -21pt;"><span style="font-family: 宋体;" lang="EN-US"><span><font size="3">■</font><span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><font size="3"><span style="font-family: 宋体;" lang="EN-US">this</span><span style="font-family: 宋体;">指针<span lang="EN-US"><o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 57.75pt;"><span style="font-family: 宋体;"><font size="3">《深入浅出<span lang="EN-US">MFC</span>》中解释：<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt; text-indent: -5.25pt;"><font size="3"><span style="font-family: 宋体;" lang="EN-US"><span>&nbsp;</span></span><span style="font-family: 宋体;">定义类<span lang="EN-US">CRect</span>，定义两个对象<span lang="EN-US">rect1</span>、<span lang="EN-US">rect2</span>，各有自己的<span lang="EN-US">m_color</span>成员变量，但<span lang="EN-US">rect1.setcolor</span>和<span lang="EN-US">rect2.setcolor</span>却都是通往唯一的<span lang="EN-US">CRect::setcolor</span>成员函数，那么<span lang="EN-US">CRect::setcolor</span>如何处理不同对象的<span lang="EN-US">m_color</span>？答案是由一个隐藏参数，名为<span lang="EN-US">this</span>指针。当你调用：<span lang="EN-US"><o:p></o:p></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3">rect1.setcolro(2);<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3">rect2.setcolor(3);<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><span style="font-family: 宋体;"><font size="3">时，编译器实际上为你做出来一下的代码：<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3">CRect::setcolor(2,(CRect*)&amp;rect1);<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3">CRect::setcolor(3,(CRect*)&amp;rect2);<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><span style="font-family: 宋体;"><font size="3">多出来的参数，就是所谓的<span lang="EN-US">this</span>指针。<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3">class CRect<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3">{<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt; text-indent: 10.5pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3">&#8230;&#8230;<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt; text-indent: 10.5pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3">public:<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt; text-indent: 10.5pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3"><span>&nbsp; </span>void setcolor(int color){m_color = color};<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3">};<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><span style="font-family: 宋体;"><font size="3">被编译后，其实为：<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3">class CRect<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3">{<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt; text-indent: 10.5pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3">&#8230;&#8230;<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt; text-indent: 10.5pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3">public:<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt; text-indent: 10.5pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3"><span>&nbsp; </span>void setcolor(int color,(CRect*)this){this-&gt;m_color = color};<o:p></o:p></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 63pt;"><span style="font-family: 宋体;" lang="EN-US"><font size="3">};</font></span></p><img src ="http://www.cppblog.com/colys/aggbug/25381.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/colys/" target="_blank">colys</a> 2007-06-03 15:07 <a href="http://www.cppblog.com/colys/articles/25381.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>根据路径创建不规则窗体</title><link>http://www.cppblog.com/colys/articles/24791.html</link><dc:creator>colys</dc:creator><author>colys</author><pubDate>Thu, 24 May 2007 15:08:00 GMT</pubDate><guid>http://www.cppblog.com/colys/articles/24791.html</guid><wfw:comment>http://www.cppblog.com/colys/comments/24791.html</wfw:comment><comments>http://www.cppblog.com/colys/articles/24791.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/colys/comments/commentRss/24791.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/colys/services/trackbacks/24791.html</trackback:ping><description><![CDATA[<p>&nbsp;//根据路径创建不规则窗体<br>&nbsp;CDC* pDC;</p>
<p>&nbsp;pDC = this-&gt;GetDC();<br>&nbsp;::BeginPath(pDC-&gt;m_hDC);<br>&nbsp;<br>&nbsp;//设置为透明模式&nbsp;<br>&nbsp;::SetBkMode(pDC-&gt;m_hDC, TRANSPARENT);&nbsp;&nbsp;<br>&nbsp;//<br>&nbsp;RECT rect;<br>&nbsp;this-&gt;GetClientRect(&amp;rect);<br>&nbsp;/*三角型<br>&nbsp;int TopCenterPoint=rect.left + (rect.right - rect.left) /2;<br>&nbsp;pDC-&gt;MoveTo(TopCenterPoint, rect.top);<br>&nbsp;pDC-&gt;LineTo(rect.left, rect.bottom - GLOBAL_OVERLEN);<br>&nbsp;pDC-&gt;LineTo(rect.right, rect.bottom - GLOBAL_OVERLEN);<br>&nbsp;pDC-&gt;LineTo(TopCenterPoint,rect.top);<br>&nbsp;*/</p>
<p>&nbsp;/*比较奇怪的矩形<br>&nbsp;pDC-&gt;MoveTo(rect.left, rect.top);<br>&nbsp;pDC-&gt;LineTo(rect.right, rect.top);</p>
<p>&nbsp;pDC-&gt;LineTo(rect.right, rect.bottom - GLOBAL_OVERLEN);</p>
<p>&nbsp;pDC-&gt;LineTo(rect.left + (rect.right - rect.left) / 2, rect.bottom - GLOBAL_OVERLEN);<br>&nbsp;pDC-&gt;LineTo(rect.left + (rect.right - rect.left) / 2, rect.bottom);<br>&nbsp;pDC-&gt;LineTo(rect.left + (rect.right - rect.left) / 2 - GLOBAL_OVERWIDTH, rect.bottom - GLOBAL_OVERLEN);</p>
<p>&nbsp;pDC-&gt;LineTo(rect.left, rect.bottom - GLOBAL_OVERLEN);<br>&nbsp;pDC-&gt;LineTo(rect.left, rect.top);<br>&nbsp;*/<br>&nbsp;//<br>&nbsp;<br>&nbsp;::EndPath(pDC-&gt;m_hDC);<br>&nbsp;hRgn = ::PathToRegion(pDC-&gt;m_hDC);<br>&nbsp;this-&gt;SetWindowRgn(hRgn, TRUE);</p><img src ="http://www.cppblog.com/colys/aggbug/24791.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/colys/" target="_blank">colys</a> 2007-05-24 23:08 <a href="http://www.cppblog.com/colys/articles/24791.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>读写磁盘文件专题</title><link>http://www.cppblog.com/colys/articles/24491.html</link><dc:creator>colys</dc:creator><author>colys</author><pubDate>Sun, 20 May 2007 15:11:00 GMT</pubDate><guid>http://www.cppblog.com/colys/articles/24491.html</guid><wfw:comment>http://www.cppblog.com/colys/comments/24491.html</wfw:comment><comments>http://www.cppblog.com/colys/articles/24491.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/colys/comments/commentRss/24491.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/colys/services/trackbacks/24491.html</trackback:ping><description><![CDATA[<p align=left><span>在</span><span>Windows</span><span>平台上，读写磁盘文件是相当多应用程序经常会涉及到的一种功能。该主题涉及到采用</span><span>C/C++/MFC/Win32(API)</span><span>中提供的接口函数来操作磁盘文件的方法，以及其中需要注意的地方。</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>=============================================================</span></p>
<p align=left><span><span>0.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>磁盘文件数据存储方式</span></p>
<p align=left><span>在介绍各种操作文件方式之前，需要先介绍磁盘上文件数据的组织方式。</span></p>
<p align=left><span>实际上，文件是在计算机内存中以二进制表示的数据在外部存储介质上的另一种存放形式。</span><strong><span>文件通常分为二进制文件和文本文件。</span></strong><span>二进制文件是包含在</span><span>ASCII</span><span>及扩展</span><span>ASCII</span><span>字符中编写的数据或程序指令的文件。一般是可执行文件</span><span>(Exe)</span><span>、图形、图像、声音等文件。而文本文件</span><span>(</span><span>通常也成为</span><span>ASCII</span><span>文件</span><span>)</span><span>，它的每一字节存放的是可表示为一个字符的</span><span>ASCII</span><span>代码的文件。这里把文件区分为二进制文件和文本文件，但实际上它们都是以二进制数据的方式来存储的。文本文件里所存储的每一个字节都可以转化为一个</span><span>可读</span><span>的字符。譬如</span><span>&#8217;</span><span>a</span><span>&#8217;</span><span>,</span><span>&#8217;</span><span>b</span><span>&#8217;</span><span>，</span><span>但在内存中并不会存储'</span><span>a'</span><span>, '</span><span>b'</span><span>，而是存储它们的</span><span>ASCII</span><span>码：</span><span>61</span><span>和</span><span>62</span><span>。</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>1.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>C</span><span>语言对文件操作的支持</span></p>
<p align=left><span>在</span><span>C</span><span>语言中，对于文件的操作都是利用</span><span>FILE</span><span>结构体来进行的。包括文件的打开、文件的读写、文件的关闭、文件指针的定位等。</span></p>
<p align=left><span><span>(1)<span>&nbsp;&nbsp; </span></span></span><span>文件的打开</span></p>
<p align=left><span>文件的打开需要用</span><span>fopen</span><span>函数。该函数带</span><span>2</span><span>个参数，返回值为指向之前定义的</span><span>FILE</span><span>结构体指针。语法定义为</span></p>
<p align=left><span>FILE* fopen(const char* filename, const char* mod);</span></p>
<p align=left><span>参数</span><span>1</span><span>：表示要打开文件的完整路径，例如</span><span>: &#8220;C:\\Test\\Zero_Test.txt&#8221;.</span></p>
<p align=left><span>参数</span><span>2</span><span>：打开文件的方式。取值为下表所示。</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=111>
            <p align=left><span>文件打开模式</span></p>
            </td>
            <td vAlign=top width=457>
            <p align=left><span>意义</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=111>
            <p align=left><span>r/rb</span></p>
            </td>
            <td vAlign=top width=457>
            <p align=left><span>为读取而打开。如果文件不存在或不能找到，函数调用失败。</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=111>
            <p align=left><span>w/wb</span></p>
            </td>
            <td vAlign=top width=457>
            <p align=left><span>为写入而打开一个空文件。如果给定的文件已经存在，则清空其内容。</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=111>
            <p align=left><span>a/ab</span></p>
            </td>
            <td vAlign=top width=457>
            <p align=left><span>为写入而打开一个文件。如果文件存在，则在文件尾部添加新数据，在写新数据之前不会移除原有的</span><span>EOF</span><span>标志。如果文件不存在，则新建一个空文件以待写入。</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=111>
            <p align=left><span>r+/rb+</span></p>
            </td>
            <td vAlign=top width=457>
            <p align=left><span>打开文件用于写入操作和读取操作，文件必须存在。</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=111>
            <p align=left><span>w+/wb+</span></p>
            </td>
            <td vAlign=top width=457>
            <p align=left><span>为写入操作和读取操作打开一个空文件。如果文件已经存在，则清空其内容。</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=111>
            <p align=left><span>a+/ab+</span></p>
            </td>
            <td vAlign=top width=457>
            <p align=left><span>打开文件用于读取操作和添加操作。并且添加操作在添加新数据之前会移除该文件中已有的</span><span>EOF</span><span>标志，然后当写入操作完成之后再恢复</span><span>EOF</span><span>标志。如果指定文件不存在，那么首先将新建一个文件。</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p align=left><span>在上表中，没有带</span><span>b</span><span>的模式表示打开的是文本文件，而带</span><span>b</span><span>的模式表示打开的是二进制文件。</span></p>
<p align=left><span>通常在定义结构</span><span>FILE</span><span>时会将其初始化为</span><span>NULL</span><span>。在打开文件将函数</span><span>fopen</span><span>的返回值赋值给它。之后需要判断文件是否成功打开，可采用如下方式：</span></p>
<p align=left><span>if ((pFile = fopen(&#8220;C:\\Test\\Zero_Test.txt&#8221;, &#8220;w&#8221;) == NULL)</span></p>
<p align=left><span><span>&nbsp;&nbsp; </span>Print(&#8220;Opening File Error.&#8221;);<span>&nbsp;&nbsp;&nbsp; </span>// FILE* pFile = NULL;</span></p>
<p align=left><span>else</span></p>
<p align=left><span><span>&nbsp;&nbsp; </span></span><span>正常写入数据到文件</span><span>&#8230;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>（</span><span>2</span><span>）文件的关闭</span></p>
<p align=left><span>在使用完一个文件之后应该关闭它，以防止它再被误用。&#8220;关闭&#8221;就是使文件指针变量不再指向该文件，使其脱钩。函数使用方式</span><span>:</span></p>
<p align=left><span>fclose(pFile);</span></p>
<p align=left><span>需要注意的是</span><span>fclose</span><span>的参数必须是有效的文件指针变量，否则运行时会挂掉。</span></p>
<p align=left><span>应该养成在文件数据操作完成后关闭文件的习惯，如果不关闭文件将会丢失数据。同时，在向文件写数据时，是现将数据输出到缓冲区，待缓冲区充满后才正式输出给文件。如果程序运行结束而缓冲区并未充满，则缓冲区中的数据将会丢失。因此利用函数</span><span>fclose</span><span>来关闭文件可以避免这个问题。</span></p>
<p align=left><span>当然，还有另外一个函数</span><span>fflush</span><span>，也可以用来将缓冲区里的数据输出到文件中。函数调用方式</span><span>:</span></p>
<p align=left><span>fflush(pFile);</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;</span><span>（</span><span>3</span><span>）文件的读写</span></p>
<p align=left><span>当涉及到大量数据的读写时，可以采用函数</span><span>fread</span><span>和</span><span>fwrite</span><span>。</span><span>这两个函数通常用于二进制文件的读写。</span><span>函数调用方式如下：</span></p>
<p align=left><span>fread(buffer, size, count, fp);</span></p>
<p align=left><span>fwrite(buffer, size, count, fp);</span></p>
<p align=left><span>buffer</span><span>是一个指针，是用来存放数据的变量指针，例如内置类型变量的指针，结构体变量指针等。</span></p>
<p align=left><span>size</span><span>是要读写的字节数。</span><span>特别要注意的是，此处并不是数组的大小，结构体内变量的个数等。最好采用</span><span>sizeof</span><span>操作符来求得</span><span>buffer</span><span>的内存字节数。</span></p>
<p align=left><span>count</span><span>是</span><span>size</span><span>大小的重复次数。</span></p>
<p align=left><span>fp</span><span>是文件指针。</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>对于这样的结构体：</span></p>
<p align=left><span>typedef struct {</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>int clr_sel;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// color mode</span><span>的选择</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>int fixval_sel;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// fixed value</span><span>的选择</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>float thres_val;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// threshold value</span><span>的确定</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>float thres_scrlbar_pos;<span> </span>// threshold scroll bar</span><span>位置的确定</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>int method_sel;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// analysis method</span><span>的选择</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>int usediff_sel;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// use difference of MSA/TSE</span><span>方式的选择</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>int usescat_sel;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// use MSA/TSE of scatter signal </span><span>方式的选择</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>int remxtalk_sel;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// remove crosstalk </span><span>方式的选择</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>float timewin_scrlbar_pos;&nbsp;// time window scroll bar</span><span>位置的确定</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>int timewin_sel;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// time window</span><span>模式的选择</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>int freq_sel;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// frequency selection</span><span>模式的选择</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>int path_sel;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// path selection</span><span>模式的选择</span><span><span>&nbsp;&nbsp; </span></span></p>
<p align=left><span>} IMG_SETTINGS;</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>IMG_SETTINGS m_img_settings;</span></p>
<p align=left><span>文件读写操作时，可采用如下方式：</span></p>
<p align=left><span>fwrite(&amp;m_img_settings, sizeof(m_img_settings), 1, pwFile);</span></p>
<p align=left><span>fread(&amp;m_img_settings, sizeof(m_img_settings), 1, prFile);</span></p>
<p align=left><span>====================================================</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>如果想读写特定格式的文件，可采用</span><span>fprintf</span><span>和</span><span>fscanf</span><span>函数。</span><span>这两个函数通常是针对文本文件进行操作。</span></p>
<p align=left><span>函数调用方式如下：</span></p>
<p align=center><span>fprintf(pFile, &#8220;%d&#8230;&#8221;, i, j, k &#8230;);</span></p>
<p align=center><span>fscanf(pFile, &#8220;%d&#8230;&#8221;, i j, k&#8230;);</span></p>
<p align=left><span>格式化时可以规定读写数据的精度，类型，以及数据之间的分割符等。</span></p>
<p align=left><span>例如：</span></p>
<p align=left><span>fprintf(pFile, " %d %d %d %d %d %d %d %d \n", sigSize, samp_points, samp_rate,40, avrag_num, 0, 0, 0);</span></p>
<p align=left><span>fprintf(pFile, "%d %.2f %s %d %d %d %d \n", vpathdef[i].frequency1/1000,</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>vpathdef[i].amplitude, sig_type.c_str(),&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;</span>psnset-&gt;sensorArray[vpathdef[i].actuator-1].channel-tol,</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>psnset-&gt;sensorArray[vpathdef[i].sensor-1].channel-tol,</span><span><span>&nbsp; </span>vpathdef[i].gain, 30);<span>&nbsp;&nbsp; </span></span></p>
<p align=left><span>特别需要注意的是，如果写入的数据的类型相同时，不可为了简便，将格式化的字符串写成如此形式</span><span>(.., &#8221;%d&#8221;,i,j,k&#8230;); // i,j,k&#8230;</span><span>同整型类型。</span></p>
<p align=left><span>如果这样操作的话，读写的数据便只有数据</span><span>i</span><span>了。</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>最后针对一个面试题来对</span><span>C</span><span>语言操作文件的方式作一个总结</span><span>.</span></p>
<p align=left><span>面试题：给你一个整数，例如</span><span>12345</span><span>，将这个整数保存到文件中，要求在以记事本打开该文件时，显示的是</span><span>:12345</span><span>。</span></p>
<p align=left><span>给出三种代码：</span></p>
<p align=left><span><span>（1）<span>&nbsp;&nbsp;&nbsp; </span></span></span><span>代码</span><span>1</span></p>
<p align=left><span>FILE* pwFile = NULL;</span></p>
<p align=left><span>pwFile = fopen(&#8220;c:\\Test.txt&#8221;, &#8220;w&#8221;);&nbsp;// create and open file with text mode.</span></p>
<p align=left><span>int i = 12345;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// However, write number with binary mode.</span></p>
<p align=left><span>fwrite(&amp;i, 4, 1, pwFile);<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// sizeof(int) = 4.</span></p>
<p align=left><span>fclose(fwFile);</span></p>
<p align=left><span>========================</span></p>
<p align=left><span><span>（2）<span>&nbsp;&nbsp;&nbsp; </span></span></span><span>代码</span><span>2</span></p>
<p align=left><span>FILE* pwFile = NULL;</span></p>
<p align=left><span>pwFile = fopen(&#8220;c:\\Test.txt&#8221;, &#8220;w&#8221;);</span></p>
<p align=left><span>char ch[5] = {1+48, 2+48, 3+48, 4+48, 5+48 };</span></p>
<p align=left><span>fwrite(ch, 1, 5, pwFile);<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// sizeof(char) = 1, 5*sizeof(char) = 5</span></p>
<p align=left><span>fclose(pwFile);</span></p>
<p align=left><span>=======================</span></p>
<p align=left><span><span>（3）<span>&nbsp;&nbsp;&nbsp; </span></span></span><span>代码</span><span>3</span></p>
<p align=left><span>FILE* pwFile = NULL;</span></p>
<p align=left><span>pwFile = fopen(&#8220;c:\\Test.txt&#8221;, &#8220;w&#8221;);</span></p>
<p align=left><span>int i = 12345;</span></p>
<p align=left><span>char ch[5];</span></p>
<p align=left><span>itoa(i, ch, 10);<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// transform int number to string</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>fwrite(ch, 1, 5, fwFile);</span></p>
<p align=left><span>fclose(pFile);</span></p>
<p align=left><span>==============================</span></p>
<p align=left><span><span>（4）<span>&nbsp;&nbsp;&nbsp; </span></span></span><span>代码</span><span>4</span></p>
<p align=left><span>FILE* pwFile = NULL;</span></p>
<p align=left><span>pwFile = fopen(&#8220;c:\\Test.txt&#8221;, &#8220;w&#8221;);</span></p>
<p align=left><span>int i = 12345;</span></p>
<p align=left><span>fprintf(pwFile, &#8220;%d&#8221;, i);<span>&nbsp;&nbsp; </span>// write number with specified format.</span></p>
<p align=left><span>fclose(pwFile);</span></p>
<p align=left><span>==============================</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>将</span><span>4</span><span>种代码在</span><span>vc</span><span>中进行编译运行，可以发现<span>只有方式（</span></span><span>1</span><span>）不满足题目要求</span><span>。<br></span></p>
<br>to be continued...<br><img src ="http://www.cppblog.com/colys/aggbug/24491.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/colys/" target="_blank">colys</a> 2007-05-20 23:11 <a href="http://www.cppblog.com/colys/articles/24491.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>菜单编程专题</title><link>http://www.cppblog.com/colys/articles/24487.html</link><dc:creator>colys</dc:creator><author>colys</author><pubDate>Sun, 20 May 2007 14:54:00 GMT</pubDate><guid>http://www.cppblog.com/colys/articles/24487.html</guid><wfw:comment>http://www.cppblog.com/colys/comments/24487.html</wfw:comment><comments>http://www.cppblog.com/colys/articles/24487.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/colys/comments/commentRss/24487.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/colys/services/trackbacks/24487.html</trackback:ping><description><![CDATA[<p align=left><span><span>1.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>菜单响应的类顺序</span></p>
<p align=left><span>依次是视类、文档类、框架类，最后才是应用程序类。</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>2.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Windows</span><span>消息的分类</span><span> </span></p>
<p align=left><span>实际上，菜单命令也是一种消息。在</span><span>Windows</span><span>中，消息分为以下</span><span>3</span><span>种：</span></p>
<p align=left><span><span>（1）<span>&nbsp;&nbsp;&nbsp; </span></span></span><span>标准消息</span></p>
<p align=left><span>除了</span><span>WM_COMMAND</span><span>之外，所有以</span><span>WM_</span><span>开头的消息都是标准消息。从</span><span>CWnd</span><span>派生的类，都可以接收到该类消息。</span></p>
<p align=left><span><span>（2）<span>&nbsp;&nbsp;&nbsp; </span></span></span><span>命令消息</span></p>
<p align=left><span><span>&nbsp;&nbsp; </span></span><span>来自菜单、加速键或工具栏按钮的消息。这类消息都是以</span><span>WM_COMMAND</span><span>形式呈现。在</span><span>MFC</span><span>中，通过菜单项的标识</span><span>(ID)</span><span>来区分不同的命令消息；在</span><span>SDK</span><span>中，通过消息的</span><span>wParam</span><span>参数来标识。从</span><span>CCmdTarget</span><span>派生的类，都可以接收到这类消息。</span></p>
<p align=left><span><span>（3）<span>&nbsp;&nbsp;&nbsp; </span></span></span><span>通告消息</span></p>
<p align=left><span><span>&nbsp;&nbsp; </span></span><span>由控件产生的消息，例如按钮的单击、列表框的选择等都会产生这类消息，目的是为了向其父窗口通知事件的发生。这类消息也是以</span><span>WM_COMMAND</span><span>形式呈现的。从</span><span>CCmdTarget</span><span>派生的类，都可以接收到这类消息。</span></p>
<p align=left><span>由于</span><span>CWnd</span><span>是从</span><span>CCmdTarget</span><span>派生的，故从</span><span>CWnd</span><span>派生的类，它们既可以接收标准消息，也可以接收命令消息和通告消息。而对于那些从</span><span>CCmdTarget</span><span>派生的类，则只能接收命令消息和通告消息，不能接收标准消息。</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>3.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>