﻿<?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++博客-Beginning to 编程-随笔分类-程序摘录</title><link>http://www.cppblog.com/richardzeng/category/1011.html</link><description>VC++ 方面编程文章</description><language>zh-cn</language><lastBuildDate>Mon, 19 May 2008 22:23:26 GMT</lastBuildDate><pubDate>Mon, 19 May 2008 22:23:26 GMT</pubDate><ttl>60</ttl><item><title>Windows GDI CDC 使用问题</title><link>http://www.cppblog.com/richardzeng/archive/2006/07/16/10112.html</link><dc:creator>Beginning to 编程</dc:creator><author>Beginning to 编程</author><pubDate>Sat, 15 Jul 2006 19:06:00 GMT</pubDate><guid>http://www.cppblog.com/richardzeng/archive/2006/07/16/10112.html</guid><wfw:comment>http://www.cppblog.com/richardzeng/comments/10112.html</wfw:comment><comments>http://www.cppblog.com/richardzeng/archive/2006/07/16/10112.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardzeng/comments/commentRss/10112.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardzeng/services/trackbacks/10112.html</trackback:ping><description><![CDATA[
		<p>最近在学习Hoops   的引擎（<a href="http://www.hoops3d.com">http://www.hoops3d.com</a> )<br /><br />模拟它的MVO架构，不过没有原代码，所以很难a<br />设计了一个交互绘图基本类，<br />但是还有错误，鼠标左键点击两下<br /> winGDI.cpp中出错。<br />请大虾指教一二。<br />我已经在这个问题上花了很多的心思。<br />其中最重要的就是Painter抽象类的设计<br />他的子类QBufferDC继承CDC<br />而SGView包含Painter指针，方便在SGView ::drawEntity调用。<br /><br /><br /><br />SGObject - 对象的抽象类，也就是几何对象<br />SGModel- 负责对象管理，没有实现所有的功能，准备用SceneTree来实现<br />SGView - 负责显示SGModel中的数据，关键的函数<br />void SGView::drawEntity(SGObject* pObj)<br />{<br />   pObj-&gt;draw(m_pPainter,this)<br />}<br /><br />Painter - 封装CDC的功能,见代码<br /><br />在CSGView创建的时候创建Painter对象<br />很可能这里有问题！！！！！<br />void CSGView::OnCreate(..)<br />{<br />     CDC* pDC = GetDC();<br />   Painter* painter = new QBufferDC(pDC);<br />   m_pSGView-&gt;setPainter(painter);<br />}<br /><br /><br />MFC 相关的Document/View架构<br />CSGDocument - 管理SGModel<br /><br />CSGView - 和SGView建立联系，并负责把windows的消息发送给SGView<br />见原代码<br /><br />SGActionManager - 负责工具的管理<br />SGBaseAction - 工具的抽象基类<br />SGActionDrawLine - 绘制直线的工具<br /><br /><br /><br />源代码连接：<br /><a href="/Files/richardzeng/MVOTest.rar">http://www.cppblog.com/Files/richardzeng/MVOTest.rar</a><br /></p>
<img src ="http://www.cppblog.com/richardzeng/aggbug/10112.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardzeng/" target="_blank">Beginning to 编程</a> 2006-07-16 03:06 <a href="http://www.cppblog.com/richardzeng/archive/2006/07/16/10112.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>资源和资源管理类的设计问题</title><link>http://www.cppblog.com/richardzeng/archive/2006/05/27/7741.html</link><dc:creator>Beginning to 编程</dc:creator><author>Beginning to 编程</author><pubDate>Sat, 27 May 2006 14:19:00 GMT</pubDate><guid>http://www.cppblog.com/richardzeng/archive/2006/05/27/7741.html</guid><wfw:comment>http://www.cppblog.com/richardzeng/comments/7741.html</wfw:comment><comments>http://www.cppblog.com/richardzeng/archive/2006/05/27/7741.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/richardzeng/comments/commentRss/7741.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardzeng/services/trackbacks/7741.html</trackback:ping><description><![CDATA[
		<p>
				<br />我要设计的应用程序其中的一个模块就是封装 windows GDI中的画笔，画刷等GDI object<br />把GDI object 再封装成resource，以实现多种样式多线条的画笔及画刷资源</p>
		<p>画笔，画刷等资源继承resource<br />为了避免发生资源泄露和resource的管理<br />设计ResourceManager类，负责资源的创建，加载和卸载以及删除</p>
		<p>两个抽象类 Resource 和 ResourceManager<br />两个具体类 ConcreateResource 和 ConcreateResourceManager<br />分别派生于上面的抽象类</p>
		<p>以上设计是看了 OGRE 游戏引擎的资源管理部分，<br />对它的资源管理类ResourceManager不是很理解</p>
		<p>resource 派生了pen,brush等类<br />pen类可以来自文件，也可以自己创建SubPen 添加到SubPenList中</p>
		<p>ResourceManager 负责创建资源Resource<br />1. 如果我在抽象的 ResourceManager 声明 createRes函数，并返回基类resource<br />势必会要强制转换，然后在用到具体的Resource时候又要转换回来</p>
		<p>2. 如果我在具体类 ConcreateResourceManager 声明 createConcreateRes函数<br />那么就白费了我应用设计模式设计这么多类</p>
		<p>
				<br />// abstract class for resource<br />class Resource{<br />public:<br /> // standard constructor<br />  Resource(const string&amp; name, const string&amp; group)<br /> :mName(name),mGroup(group){}<br />  ~Resource(){}<br />protected:<br /> // prevent default construct<br />  Resource():mName(""),mGroup(""){}<br />  string mName;<br />  string mGroup;<br />  static unsigned long mHandle;<br />};</p>
		<p>// subclass of resource<br />// concreateResource such as PEN<br />class Pen:<br /> public Resource{<br /> Pen(const string&amp; name, const string&amp; group)<br />  :Resource(name,group){}<br />  ~Pen(){}<br /><br />  void loadfromFile(string&amp; filename);<br /><br />// add into vector<br />  void addSubPen(SubPen* sub){<br />     mSubPenList.push_back(sub);<br />}<br />public:<br />typedef std::vector&lt;SubPen&gt; SubPenList;<br />SubPenList mSubPenList;</p>
		<p>};<br />class <br />// abstract class for resource manager<br />class ResourceManager{<br />public:<br />  ResourceManager(){}<br />  ~ResourceManager(){}<br />public:<br />// here , I cannot understand OGRE degsin<br />  Resource* createRes(const string&amp; name,const string&amp; group);<br /> // resource map <br />　typedef std::map&lt;string,Resource*&gt; ResourceMap;<br />   ResourceMap mResources;</p>
		<p>};</p>
		<p>// subclass ResourceManager<br />class ConcreateResourceManager<br /> :public ResourceManager<br />{<br /> ConcreateResourceManager(){}<br /> ~ConcreateResourceManager(){}</p>
		<p>      // how can design here!!<br />       Pen* createPen(const string&amp; name,const string&amp; group){}<br />}</p>
		<p> </p>
<img src ="http://www.cppblog.com/richardzeng/aggbug/7741.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardzeng/" target="_blank">Beginning to 编程</a> 2006-05-27 22:19 <a href="http://www.cppblog.com/richardzeng/archive/2006/05/27/7741.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++中Singleton的实现[zhuan]</title><link>http://www.cppblog.com/richardzeng/archive/2006/04/21/5999.html</link><dc:creator>Beginning to 编程</dc:creator><author>Beginning to 编程</author><pubDate>Fri, 21 Apr 2006 01:31:00 GMT</pubDate><guid>http://www.cppblog.com/richardzeng/archive/2006/04/21/5999.html</guid><wfw:comment>http://www.cppblog.com/richardzeng/comments/5999.html</wfw:comment><comments>http://www.cppblog.com/richardzeng/archive/2006/04/21/5999.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardzeng/comments/commentRss/5999.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardzeng/services/trackbacks/5999.html</trackback:ping><description><![CDATA[
		<div>
				<br />这些东西在网上都很多了，但是我觉得他们的使用都不符合我的要求，所以自己动手丰衣足食，写一个自己能用的，够用就好。</div>
		<table cellspacing="0" cellpadding="0" border="1">
				<tbody>
						<tr>
								<td valign="top" width="568">
										<div>#include &lt;iostream&gt;</div>
										<div> </div>
										<div>using namespace std;</div>
										<div> </div>
										<div>//单件模板类</div>
										<div>template&lt;typename T&gt; class Singleton</div>
										<div>{</div>
										<div>protected:</div>
										<div> </div>
										<div>  static T* m_Instance;</div>
										<div> </div>
										<div>  Singleton(){}</div>
										<div>  virtual~Singleton(){}</div>
										<div> </div>
										<div>public:</div>
										<div> </div>
										<div>  //实例的获得</div>
										<div>  static T* Instance()</div>
										<div>  {</div>
										<div>    if(m_Instance==0)</div>
										<div>      m_Instance=new T;</div>
										<div> </div>
										<div>    return m_Instance;</div>
										<div>  }</div>
										<div> </div>
										<div>  //单件类的释放</div>
										<div>  virtual void Release()</div>
										<div>  {</div>
										<div>    if(m_Instance!=0)</div>
										<div>    {</div>
										<div>      delete m_Instance;</div>
										<div>      m_Instance=0;</div>
										<div>    }</div>
										<div>  }</div>
										<div>};</div>
										<div> </div>
										<div>//单件模板测试类</div>
										<div>class Test:public Singleton&lt;Test&gt;</div>
										<div>{</div>
										<div>  friend class Singleton&lt;Test&gt;; //声明为友员，不然会出错</div>
										<div>protected:</div>
										<div>  Test()</div>
										<div>  {</div>
										<div>    a=b=c=0;</div>
										<div>  }</div>
										<div>  virtual ~Test(){}</div>
										<div> </div>
										<div>public :</div>
										<div> </div>
										<div>  int a;</div>
										<div>  int b;</div>
										<div>  int c;</div>
										<div>};</div>
										<div> </div>
										<div>//初始化静态成员。。。</div>
										<div>template&lt;&gt; Test*Singleton&lt;Test&gt;::m_Instance=0;</div>
										<div> </div>
										<div> </div>
										<div>//以下为测试代码</div>
										<div>void main()</div>
										<div>{</div>
										<div>  Test*t=Test::Instance();</div>
										<div> </div>
										<div>  t-&gt;a=5;</div>
										<div>  t-&gt;b=25;</div>
										<div>  t-&gt;c=35;</div>
										<div>  cout&lt;&lt;"t: a="&lt;&lt;t-&gt;a&lt;&lt;" b="&lt;&lt;t-&gt;b&lt;&lt;" c="&lt;&lt;t-&gt;c&lt;&lt;endl;</div>
										<div> </div>
										<div>  Test*t2;</div>
										<div>  t2=Test::Instance();</div>
										<div>  cout&lt;&lt;"t2 a="&lt;&lt;t2-&gt;a&lt;&lt;" b="&lt;&lt;t2-&gt;b&lt;&lt;" c="&lt;&lt;t2-&gt;c&lt;&lt;endl;</div>
										<div> </div>
										<div>  t2-&gt;Release();</div>
										<div>}</div>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cppblog.com/richardzeng/aggbug/5999.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardzeng/" target="_blank">Beginning to 编程</a> 2006-04-21 09:31 <a href="http://www.cppblog.com/richardzeng/archive/2006/04/21/5999.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>波形显示不是很难 /zhuan</title><link>http://www.cppblog.com/richardzeng/archive/2006/03/16/4229.html</link><dc:creator>Beginning to 编程</dc:creator><author>Beginning to 编程</author><pubDate>Thu, 16 Mar 2006 03:00:00 GMT</pubDate><guid>http://www.cppblog.com/richardzeng/archive/2006/03/16/4229.html</guid><wfw:comment>http://www.cppblog.com/richardzeng/comments/4229.html</wfw:comment><comments>http://www.cppblog.com/richardzeng/archive/2006/03/16/4229.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/richardzeng/comments/commentRss/4229.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardzeng/services/trackbacks/4229.html</trackback:ping><description><![CDATA[封装了一下波形显示，发现不是很难<BR><IMG alt="" hspace=0 src="http://mythma.bokee.com/inc/wave.JPG" align=baseline border=0><BR><BR><A HREF="/Files/richardzeng/WaveShow_src.rar"><FONT color=#002c99>WaveShow_src.rar</FONT></A> <img src ="http://www.cppblog.com/richardzeng/aggbug/4229.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardzeng/" target="_blank">Beginning to 编程</a> 2006-03-16 11:00 <a href="http://www.cppblog.com/richardzeng/archive/2006/03/16/4229.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>大小写转换的方法【C/C++】 /zhuan</title><link>http://www.cppblog.com/richardzeng/archive/2006/03/15/4195.html</link><dc:creator>Beginning to 编程</dc:creator><author>Beginning to 编程</author><pubDate>Wed, 15 Mar 2006 05:31:00 GMT</pubDate><guid>http://www.cppblog.com/richardzeng/archive/2006/03/15/4195.html</guid><wfw:comment>http://www.cppblog.com/richardzeng/comments/4195.html</wfw:comment><comments>http://www.cppblog.com/richardzeng/archive/2006/03/15/4195.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardzeng/comments/commentRss/4195.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardzeng/services/trackbacks/4195.html</trackback:ping><description><![CDATA[<DIV class=postbody>
<DIV>ASCII码表大家都很熟悉了吧，利用码的排列规律，我们可以很容易的实现一些操作，比如判断是否是数字、大小写转换等。</DIV>
<DIV>这里写大小写转换的函数:</DIV>
<DIV>
<TABLE style="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" align=center>
<TBODY>
<TR>
<TD>char toUpper(const char&amp; ch)<BR>{<BR>&nbsp;&nbsp;&nbsp; return ch &amp; 0x5F;<BR>}<BR>char toLower(const char&amp; ch)<BR>{<BR>&nbsp;&nbsp;&nbsp; return ch | 0x20;<BR>}</TD></TR></TBODY></TABLE></DIV>
<DIV>&nbsp;</DIV>
<DIV>函数原理：大小写字母的差是32，比如大写的A是65，小写的A是97，所以我们把右边数第6位置0或者1就能实现大小写转换。转换成大写时，把第6位置0，用ch &amp; 0x5F实现。转换成小写时置1，用ch | 0x20实现。怎么样，相当的简单吧，由此，我们可以写string类的toUpper和toLower函数了。^_^，更多技巧尽在探索中。</DIV>
<DIV></DIV>
<DIV></DIV></DIV><img src ="http://www.cppblog.com/richardzeng/aggbug/4195.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardzeng/" target="_blank">Beginning to 编程</a> 2006-03-15 13:31 <a href="http://www.cppblog.com/richardzeng/archive/2006/03/15/4195.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浅谈CMPP协议（一） /zhuan</title><link>http://www.cppblog.com/richardzeng/archive/2006/03/15/4186.html</link><dc:creator>Beginning to 编程</dc:creator><author>Beginning to 编程</author><pubDate>Wed, 15 Mar 2006 04:25:00 GMT</pubDate><guid>http://www.cppblog.com/richardzeng/archive/2006/03/15/4186.html</guid><wfw:comment>http://www.cppblog.com/richardzeng/comments/4186.html</wfw:comment><comments>http://www.cppblog.com/richardzeng/archive/2006/03/15/4186.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardzeng/comments/commentRss/4186.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardzeng/services/trackbacks/4186.html</trackback:ping><description><![CDATA[<DIV class=post>
<DIV class=postTitle><A class=postTitle2 id=viewpost1_TitleUrl HREF="/wangxiaoguang/archive/2006/03/15/4160.html">浅谈CMPP协议（一）</A> </DIV>&nbsp;&nbsp;&nbsp; <FONT face=Arial>CMPP协议的全称是中国移动通信互联网短信网关接口协议，它是联想亚信公司根据SMMP协议为中国移动量身定做的，是符合中国国情的一个短信协议，闲话不多说了，说说CMPP的主要功能吧。（1）短信发送（short message mobile originate）MO，就是手机给SP发短信；（2）短信接受（short message mobile terminated）MT，这个就是SP给手机发的短信了，通常我们手机上收到的不良短信就是SP给我们的MT。CMPP协议的通信基础是TCP/IP为底层通信承载的，连接方式是长连接方式。SP与ISMG之间，SMSC和ISMG之间的交互过程中均采用异步方式，即任一个网元在收到请求消息后应立即回应。</FONT> 
<DIV>&nbsp;&nbsp; 下面看看它的消息定义：CMPP中的消息分为消息头和消息体。消息头定义如下 </DIV>
<DIV>&nbsp;</DIV>
<DIV>&nbsp;</DIV>
<DIV>
<TABLE cellSpacing=0 cellPadding=0 border=1>
<TBODY>
<TR>
<TD vAlign=top width=120>
<P align=center><FONT color=#000000><FONT size=1><SPAN style="FONT-FAMILY: 宋体">字段名</SPAN><B><SPAN lang=EN-US style="FONT-SIZE: 12pt; TEXT-TRANSFORM: uppercase"></SPAN></B></FONT></FONT></P></TD>
<TD vAlign=top width=60>
<P align=center><FONT color=#000000><FONT size=1><SPAN style="FONT-FAMILY: 宋体">字节数</SPAN><B><SPAN lang=EN-US style="FONT-SIZE: 12pt; TEXT-TRANSFORM: uppercase"></SPAN></B></FONT></FONT></P></TD>
<TD vAlign=top width=84>
<P align=center><FONT color=#000000><FONT size=1><SPAN style="FONT-SIZE: 12pt; TEXT-TRANSFORM: uppercase; FONT-FAMILY: 宋体">类型</SPAN><SPAN lang=EN-US style="FONT-SIZE: 12pt; TEXT-TRANSFORM: uppercase"></SPAN></FONT></FONT></P></TD>
<TD vAlign=top width=271>
<P align=center><FONT color=#000000><FONT size=1><SPAN style="FONT-FAMILY: 宋体">描述</SPAN><B><SPAN lang=EN-US style="FONT-SIZE: 12pt; TEXT-TRANSFORM: uppercase"></SPAN></B></FONT></FONT></P></TD></TR>
<TR>
<TD vAlign=top width=120>
<P><FONT color=#000000><FONT face="Times New Roman"><FONT size=1><SPAN lang=EN-US>Total_Length<SPAN>&nbsp;&nbsp; </SPAN></SPAN><SPAN lang=EN-US style="FONT-SIZE: 12pt"></SPAN></FONT></FONT></FONT></P></TD>
<TD vAlign=top width=60>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>4</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=84>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>Unsigned Integer</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=271>
<P><FONT color=#000000><FONT size=1><SPAN style="FONT-FAMILY: 宋体">消息总长度</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">(</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">含消息头及消息体</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">)</FONT></SPAN></FONT></FONT></P></TD></TR>
<TR>
<TD vAlign=top width=120>
<P><SPAN lang=EN-US><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>Command_Id</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=60>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>4</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=84>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>Unsigned Integer</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=271>
<P><FONT color=#000000><FONT size=1><SPAN style="FONT-FAMILY: 宋体">命令或响应类型</SPAN><SPAN lang=EN-US></SPAN></FONT></FONT></P></TD></TR>
<TR>
<TD vAlign=top width=120>
<P><SPAN lang=EN-US><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>Sequence_Id</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=60>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>4</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=84>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>Unsigned Integer</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=271>
<P><FONT color=#000000><FONT size=1><SPAN style="FONT-FAMILY: 宋体">消息流水号</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">,</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">顺序累加</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">,</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">步长为</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">1,</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">循环使用（一对请求和应答消息的流水号必须相同）</SPAN></FONT></FONT><SPAN lang=EN-US></SPAN></P></TD></TR></TBODY></TABLE></DIV>
<DIV>&nbsp;</DIV>
<DIV>那么下面就是SP连接到ISMG上了，看它的Bind连接消息定义</DIV>
<DIV>&nbsp;</DIV>
<DIV>
<TABLE cellSpacing=0 cellPadding=0 border=1>
<TBODY>
<TR>
<TD vAlign=top width=156>
<P align=center><FONT color=#000000><FONT size=1><SPAN style="FONT-FAMILY: 宋体">字段名</SPAN><B><SPAN lang=EN-US style="FONT-SIZE: 12pt; TEXT-TRANSFORM: uppercase"></SPAN></B></FONT></FONT></P></TD>
<TD vAlign=top width=60>
<P align=center><FONT color=#000000><FONT size=1><SPAN style="FONT-FAMILY: 宋体">字节数</SPAN><B><SPAN lang=EN-US style="FONT-SIZE: 12pt; TEXT-TRANSFORM: uppercase"></SPAN></B></FONT></FONT></P></TD>
<TD vAlign=top width=96>
<P align=center><FONT color=#000000><FONT size=1><SPAN style="TEXT-TRANSFORM: uppercase; FONT-FAMILY: 宋体">属性</SPAN><SPAN lang=EN-US style="TEXT-TRANSFORM: uppercase"></SPAN></FONT></FONT></P></TD>
<TD vAlign=top width=240>
<P align=center><FONT color=#000000><FONT size=1><SPAN style="FONT-FAMILY: 宋体">描述</SPAN><B><SPAN lang=EN-US style="FONT-SIZE: 12pt; TEXT-TRANSFORM: uppercase"></SPAN></B></FONT></FONT></P></TD></TR>
<TR>
<TD vAlign=top width=156>
<P><FONT color=#000000><FONT face="Times New Roman"><FONT size=1><SPAN lang=EN-US>Source_Addr<SPAN>&nbsp; </SPAN></SPAN><SPAN lang=EN-US style="FONT-SIZE: 12pt"></SPAN></FONT></FONT></FONT></P></TD>
<TD vAlign=top width=60>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>6</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=96>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>Octet String</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=240>
<P><FONT color=#000000><FONT size=1><SPAN style="FONT-FAMILY: 宋体">源地址，此处为</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">SP_Id</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">，即</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">SP</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">的企业代码。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 12pt"></SPAN></FONT></FONT></P></TD></TR>
<TR>
<TD vAlign=top width=156>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>AuthenticatorSource</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=60>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>16</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=96>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>Octet String</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=240>
<P><FONT color=#000000><FONT size=1><SPAN style="FONT-FAMILY: 宋体">用于鉴别源地址。其值通过单向</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">MD5 hash</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">计算得出，表示如下：</SPAN><SPAN lang=EN-US></SPAN></FONT></FONT></P>
<P><FONT color=#000000><FONT face="Times New Roman"><FONT size=1><SPAN lang=EN-US>AuthenticatorS</SPAN><SPAN lang=EN-US style="FONT-SIZE: 12pt">ource</SPAN><SPAN lang=EN-US> =</SPAN></FONT></FONT></FONT></P>
<P><FONT color=#000000><FONT size=1><SPAN lang=EN-US><FONT face="Times New Roman">MD5</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">（</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Source_Addr+9 </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">字节的</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">0 +shared secret+timestamp</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">）</SPAN><SPAN lang=EN-US></SPAN></FONT></FONT></P>
<P><FONT color=#000000><FONT size=1><SPAN lang=EN-US><FONT face="Times New Roman">Shared secret </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">由中国移动与源地址实体事先商定，</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">timestamp</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">格式为：</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">MMDDHHMMSS</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">，即月日时分秒，</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">10</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">位。</SPAN><SPAN lang=EN-US></SPAN></FONT></FONT></P></TD></TR>
<TR>
<TD vAlign=top width=156>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>Version</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=60>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>1</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=96>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>Unsigned Integer</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=240>
<P><FONT color=#000000><FONT size=1><SPAN style="FONT-FAMILY: 宋体">双方协商的版本号</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">(</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">高位</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">4bit</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">表示主版本号</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">,</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">低位</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">4bit</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">表示次版本号</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">)</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">，对于</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">3.0</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">的版本，高</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">4bit</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">为</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">3</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">，低</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">4</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">位为</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">0</FONT></SPAN></FONT></FONT></P></TD></TR>
<TR>
<TD vAlign=top width=156>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>Timestamp</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=60>
<P><SPAN lang=EN-US style="FONT-SIZE: 12pt"><FONT color=#000000><FONT face="Times New Roman"><FONT size=1>4</FONT></FONT></FONT></SPAN></P></TD>
<TD vAlign=top width=96>
<P><FONT color=#000000><FONT face="Times New Roman"><FONT size=1><SPAN lang=EN-US>Unsigned Integer</SPAN><SPAN lang=EN-US style="FONT-SIZE: 12pt"></SPAN></FONT></FONT></FONT></P></TD>
<TD vAlign=top width=240>
<P><FONT size=1><FONT color=#000000><SPAN style="FONT-FAMILY: 宋体">时间戳的明文</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">,</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">由客户端产生</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">,</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">格式为</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">MMDDHHMMSS</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">，即月日时分秒，</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">10</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">位数字的整型，右对齐</SPAN><FONT face="Times New Roman"> </FONT><SPAN style="FONT-FAMILY: 宋体">。</SPAN></FONT><SPAN lang=EN-US></SPAN></FONT></P></TD></TR></TBODY></TABLE></DIV>
<DIV>&nbsp;</DIV>
<DIV>根据上的定义我们可以写出的代码，如下，在VC环境下编写的</DIV>
<DIV><BR>/*<BR>&nbsp;*函数功能：建立和CMPP网关的直接通路<BR>&nbsp;*输入条件：SP用户名const char *UserName,SP密码const char *PWD&nbsp; <BR>&nbsp;*/</DIV>
<DIV>void Ccmpp_API::CmppConnect(const char *UserName, const char *PWD)<BR>{</DIV>
<DIV>&nbsp;char netbuf[100];<BR>&nbsp;CMPP_CONNECT *bufer;<BR>&nbsp;bufer=(CMPP_CONNECT*)netbuf;<BR>&nbsp;memset(bufer, 0, 100);</DIV>
<DIV>&nbsp;bufer-&gt;nTotalLength = htonl(39);//CMPP_CONNECT消息总长度<BR>&nbsp;bufer-&gt;nCommandId = htonl(CMPP_CONNECT_tag);//消息标志</DIV>
<DIV>&nbsp;//自动产生SeqId号<BR>&nbsp;&nbsp;&nbsp; if (sequenceid == 123456789i32)<BR>&nbsp;{<BR>&nbsp;&nbsp;sequenceid = 1;<BR>&nbsp;}else{<BR>&nbsp;&nbsp;sequenceid++;<BR>&nbsp;}<BR>&nbsp;bufer-&gt;nSeqId = htonl(sequenceid);<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;int MD5Len;<BR>&nbsp;MD5_CTX md5;//MD5源字串<BR>&nbsp;CTime TimeData = CTime::GetCurrentTime();<BR>&nbsp;CString timestamp = TimeData.Format("%m%d%H%M%S");<BR>&nbsp;unsigned char md5source[29]; <BR>&nbsp;int Len1 = strlen(UserName);<BR>&nbsp;int Len2 = strlen(PWD);&nbsp;<BR>&nbsp;MD5Len = Len1 + 9 +Len2 + timestamp.GetLength();<BR>&nbsp;memset(md5source, 0, MD5Len);<BR>&nbsp;<BR>&nbsp;memcpy(bufer-&gt;sSourceAddr, UserName, Len1);<BR>&nbsp;memcpy(md5source, UserName, Len1);<BR>&nbsp;<BR>&nbsp;for (int j = 0; j&lt;Len2; j++)<BR>&nbsp;{<BR>&nbsp;&nbsp;md5source[j + Len1 + 9] = PWD[j];<BR>&nbsp;}</DIV>
<DIV>&nbsp;for (int i=0;i&lt;timestamp.GetLength();i++)<BR>&nbsp;{<BR>&nbsp;&nbsp;md5source[i + Len2 + Len1 + 9]=timestamp[i];<BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;//进行md5加密转换<BR>&nbsp;md5.MD5Update(md5source, MD5Len);<BR>&nbsp;md5.MD5Final(md5source);<BR>&nbsp;memcpy(bufer-&gt;sAuthSource, md5source, 29);<BR>&nbsp;bufer-&gt;cVersion = 0x30;<BR>&nbsp;bufer-&gt;nTimeStamp = htonl(atoi(timestamp));<BR>&nbsp;CmppSocket.Send(bufer, 39, 0);//把消息打包发送<BR>&nbsp;return;<BR>}</DIV>
<DIV>&nbsp;</DIV>
<DIV>今天就到这，下次再写，欢迎交流！</DIV>
<DIV class=postDesc>posted on 2006-03-15 07:58 <A HREF="/wangxiaoguang/">炙热的太阳</A> 阅读(38) <A HREF="/wangxiaoguang/archive/2006/03/15/4160.html#Post">评论(2)</A> &nbsp;<A HREF="/wangxiaoguang/admin/EditPosts.aspx?postid=4160">编辑</A>&nbsp;<A HREF="/wangxiaoguang/AddToFavorite.aspx?id=4160">收藏</A> <A title=功能强大的网络收藏夹，一秒钟操作就可以轻松实现保存带来的价值、分享带来的快乐 href="javascript:d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&amp;u='+escape(d.location.href)+'&amp;c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();">收藏至365Key</A> </DIV></DIV>
<P align=justify><IMG height=1 src="/wangxiaoguang/aggbug/4160.html?webview=1" width=1> <!--
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
<rdf:Description
rdf:about="http://www.cppblog.com/wangxiaoguang/archive/2006/03/15/4160.html"
dc:identifier="http://www.cppblog.com/wangxiaoguang/archive/2006/03/15/4160.html"
dc:title="浅谈CMPP协议（一）"
trackback:ping="http://www.cppblog.com/wangxiaoguang/services/trackbacks/4160.aspx" />
</rdf:RDF>
--><!--done--><BR><B>FeedBack:</B> </P>
<DIV class=feedbackNoItems></DIV>
<DIV class=feedbackItem>
<DIV class=feedbackListTitle><A title="permalink: re: 浅谈CMPP协议（一）" HREF="/wangxiaoguang/archive/2006/03/15/4160.html#4168">#</A>&nbsp;<A name=4168></A>re: 浅谈CMPP协议（一）</DIV>
<DIV class=feedbackListSubtitle>2006-03-15 10:38 | <A id=Comments1_CommentList__ctl0_NameLink HREF="/sandy/" target=_blank>小明</A><BR>
<DIV align=left>you say: <BR>SP与ISMG之间，SMSC和ISMG之间的交互过程中均采用异步方式，即任一个网元在收到请求消息后应立即回应。 <BR><BR>既然是异步方式，就不是收到请求后立即回应，否则就是同步方式了 <BR><BR>据我所知，CMPP采用的基于滑动窗口的异步方式，默认情况下可以发最多16的CMPP package,而不必等待他们的resp.&nbsp;&nbsp;<A onclick='SetReplyAuhor("小明")' HREF="/wangxiaoguang/archive/2006/03/15/4160.html#post">回复</A><BR><A id=Comments1_CommentList__ctl0_DeleteLink href="javascript:__doPostBack('Comments1$CommentList$_ctl0$DeleteLink','')"></A>&nbsp;&nbsp;<A id=Comments1_CommentList__ctl0_EditLink></A></DIV></DIV></DIV>
<DIV class=feedbackItem>
<DIV class=feedbackListTitle><A title="permalink: re: 浅谈CMPP协议（一）" HREF="/wangxiaoguang/archive/2006/03/15/4160.html#4177">#</A>&nbsp;<A name=4177></A>re: 浅谈CMPP协议（一）<A name=Post></A></DIV>
<DIV class=feedbackListSubtitle>2006-03-15 11:21 | <A id=Comments1_CommentList__ctl1_NameLink HREF="/wangxiaoguang/" target=_blank>炙热的太阳</A><BR>
<DIV align=left>是的，你说的没有错。 <BR><BR>消息是采用并发方式发送，加以滑动窗口流量控制，窗口大小参数W可配置，现阶段的配置为16，即接收方在应答前一次收到的消息最多不超过16条。这是它们之间的通信方式。 <BR><BR>而SP与ISMG之间，SMSC和ISMG之间的交互过程中均采用异步方式，即任一个网元在收到请求消息后应立即回应。这是它们交互过程中的应答方式。即收到一个消息就应该回一个回应消息，而不管对方是否收到，所以上面讲的并没有错哟。 <BR><BR><BR>&nbsp;&nbsp;<A onclick='SetReplyAuhor("炙热的太阳")' HREF="/wangxiaoguang/archive/2006/03/15/4160.html#post">回复</A><BR></DIV></DIV></DIV><img src ="http://www.cppblog.com/richardzeng/aggbug/4186.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardzeng/" target="_blank">Beginning to 编程</a> 2006-03-15 12:25 <a href="http://www.cppblog.com/richardzeng/archive/2006/03/15/4186.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为什么在VS2005重载输出运算符那么难 /zhuan</title><link>http://www.cppblog.com/richardzeng/archive/2006/03/15/4179.html</link><dc:creator>Beginning to 编程</dc:creator><author>Beginning to 编程</author><pubDate>Wed, 15 Mar 2006 03:57:00 GMT</pubDate><guid>http://www.cppblog.com/richardzeng/archive/2006/03/15/4179.html</guid><wfw:comment>http://www.cppblog.com/richardzeng/comments/4179.html</wfw:comment><comments>http://www.cppblog.com/richardzeng/archive/2006/03/15/4179.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardzeng/comments/commentRss/4179.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardzeng/services/trackbacks/4179.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 最近在VS2005下实现一个模版堆栈时，想重载一下输出运算符。结果老是遇到问题，如何都过不去，想不想去都不明白。还望高手指教。　　一开始同样的程序在VC2005和VC6.0下编译都没问题，但是一到链接的时候就出现问题了。都提示如下错误：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;error&nbsp;LNK2019:&nbsp;无法解析的外部符号&nbsp;"cla...&nbsp;&nbsp;<a href='http://www.cppblog.com/richardzeng/archive/2006/03/15/4179.html'>阅读全文</a><img src ="http://www.cppblog.com/richardzeng/aggbug/4179.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardzeng/" target="_blank">Beginning to 编程</a> 2006-03-15 11:57 <a href="http://www.cppblog.com/richardzeng/archive/2006/03/15/4179.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC中回调函数使用的变身大法 /zhuan</title><link>http://www.cppblog.com/richardzeng/archive/2006/03/14/4142.html</link><dc:creator>Beginning to 编程</dc:creator><author>Beginning to 编程</author><pubDate>Tue, 14 Mar 2006 09:33:00 GMT</pubDate><guid>http://www.cppblog.com/richardzeng/archive/2006/03/14/4142.html</guid><wfw:comment>http://www.cppblog.com/richardzeng/comments/4142.html</wfw:comment><comments>http://www.cppblog.com/richardzeng/archive/2006/03/14/4142.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardzeng/comments/commentRss/4142.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardzeng/services/trackbacks/4142.html</trackback:ping><description><![CDATA[对于回调函数的编写始终是写特殊处理功能程序时用到的技巧之一。先介绍一下回调的使用基本方法与原理。<BR><BR>　　1、在这里设：回调函数为A()(这是最简单的情况，不带参数，但我们应用的实际情况常常很会复杂)，使用回调函数的操作函数为B()， 但B函数是需要参数的，这个参数就是指向函数A的地址变量，这个变量一般就是函数指针。使用方法为：<BR><BR><CODE>int A(char *p); // 回调函数 <BR>typedef int(*CallBack)(char *p) ; // 声明CallBack 类型的函数指针 <BR>CallBack myCallBack ; // 声明函数指针变量<BR>myCallBack = A; // 得到了函数A的地址 </CODE><BR>　　B函数一般会写为 B(CallBack lpCall,char * P,........); // 此处省略了p后的参数形式 。 <BR><BR>　　所以回调机制可解为，函数B要完成一定功能，但他自己是无法实现全部功能的。 需要借助于函数A来完成，也就是回调函数。B的实现为：<BR><BR><CODE>B(CallBack lpCall,char *pProvide)<BR>{<BR>　........... // B 的自己实现功能语句<BR>　lpCall(PpProvide); // 借助回调完成的功能 ，也就是A函数来处理的。 <BR>　........... // B 的自己实现功能语句<BR>}<BR>// -------------- 使用例子 -------------<BR>char *p = "hello!";<BR>CallBack myCallBack ; <BR>myCallBack = A ;<BR>B(A, p);</CODE><BR>　　以上就是回调的基本应用，本文所说的变身，其实是利用传入不同的函数地址，实现调用者类与回调函数所在类的不同转换。<BR><BR>　　1、问题描述<BR><BR>　　CUploadFile 类完成数据上传，与相应的界面进度显示。<BR><BR>　　主要函数Send(...) 和回调函数 GetCurState() ; <BR><BR><CODE>class CUploadFile : public CDialog<BR>{<BR>　......<BR>　int Send(LPCTSTR lpServerIP, LPCTSTR lpServerPort, LPCTSTR UploadFilePath) ; <BR>　static int GetCurState(int nCurDone, int nInAll, void * pParam) ; <BR>　...... <BR>}<BR>int CUploadFile ::Send(LPCTSTR lpServerIP, LPCTSTR lpServerPort, LPCTSTR UploadFilePath)<BR>{<BR>　... // 导出传输数据的函数 <BR>　int ret = Upload( (LPSTR)(LPCTSTR)m_strData,<BR>　　　　GetCurState, // 在这个回调函数中处理界面 <BR>　　　　this, // CUploadFile 的自身指针 ，也就是pParam 所接受的参数 <BR>　　　　(LPSTR)(LPCTSTR)UploadFilePath,<BR>　　　　"", <BR>　　　　"", <BR>　);<BR>}<BR>int CUploadFile ::GetCurState(int nCurData, int nInAll, void * pParam) <BR>{<BR>　.........<BR>　UploadFile *pThis = (UploadFile *)pParam; // nCurData 当前以传出的数据量 <BR>　// nInAll 总的数据量<BR>　// 有了pThis可以对界面进行各种操作了。 <BR>　.............<BR>}</CODE><BR>　　但大家仔细观察就可以发现，这个类把数据传送和界面显示聚和到了一起，不容易得到复用。而且在复用过程中需要改动较多的地方 。<BR><BR>　　请大家记住现在的回调函数传入的类本身的静态成员函数。 <BR><BR>　　现在我们把数据的传送和界面的显示分离。回调则要传入的是界面处理类的静态函数。<BR><BR>　　界面处理类 CShowGUI,数据上传类 CUploadData <BR><BR><CODE>class CUploadData <BR>{<BR>　......<BR>　typedef int(*SetUploadCaller)(int nCurData, int nInAll, void * pParam);<BR>　int UploadFile(LPCTSTR lpFileNamePath,LPVOID lparam,SetUploadCaller Caller );<BR>　// 接受外界出入的参数,主要是回调函数的地址通过参数Caller, <BR>　int Send(LPCTSTR lpServerIP, LPCTSTR lpServerPort, LPCTSTR UploadFilePath) ; <BR>　...... // 注意此时不在需要GetCurState 函数了 。<BR>}<BR><BR>class CShowGUI: public CDialog<BR>{<BR>　.......<BR>　typedef int(*SetUploadCaller)(int nCurData, int nInAll, void * pParam);<BR>　void SetCallBack(LPCTSTR strPath);<BR>　static int GetCurState(int nCurData, int nInAll, void * pParam) ; <BR>　CUploadData m_Uploa<BR>　d ; // 数据上传类是界面显示类的一个成员变量。 <BR>　.......<BR>}<BR><BR>void CShowGUI :: SetCallBack(LPCTSTR strPath)<BR>{<BR>　CUploadData myUploadData ;<BR>　SetUploadCaller myCaller; // 声明一个函数指针变量 <BR>　myCaller = CurState ; // 取得界面处理函数的地址<BR>　myUploadData .UploadFile(strPath,this,myCaller); // 界面处理类的函数传入,实现了数据传入与界面处理的分离 .<BR>}</CODE><BR>　　通过上面的演示做到了界面与数据的分离,回调函数分别扮演了不同角色,所以随着处理问题的不同应灵活应用，但同样因为处理数据类不知道界面处理类或外部调用类的类型，而更无法灵活地处理界面的不同显示方式。这方面还希望喜欢钻研技术的朋友继续研究。<A href="http://tech.163.com/"><IMG height=12 alt=陈刚 src="http://tech.163.com/newimg/arc_d.gif" width=12 border=0></A> <!-- page --><img src ="http://www.cppblog.com/richardzeng/aggbug/4142.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardzeng/" target="_blank">Beginning to 编程</a> 2006-03-14 17:33 <a href="http://www.cppblog.com/richardzeng/archive/2006/03/14/4142.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++指针探讨 (二) 函数指针 /zhuan</title><link>http://www.cppblog.com/richardzeng/archive/2006/03/14/4137.html</link><dc:creator>Beginning to 编程</dc:creator><author>Beginning to 编程</author><pubDate>Tue, 14 Mar 2006 08:44:00 GMT</pubDate><guid>http://www.cppblog.com/richardzeng/archive/2006/03/14/4137.html</guid><wfw:comment>http://www.cppblog.com/richardzeng/comments/4137.html</wfw:comment><comments>http://www.cppblog.com/richardzeng/archive/2006/03/14/4137.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardzeng/comments/commentRss/4137.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardzeng/services/trackbacks/4137.html</trackback:ping><description><![CDATA[在C/C++中，数据指针是最直接，也最常用的，因此，理解起来也比较容易。而函数指针，作为运行时动态调用（比如回调函数 CallBack Function）是一种常见的，而且是很好用的手段。
<P>　　我们先简单的说一下函数指针。（这一部份没什么价值，纯是为了引出下一节的内容）<BR>&nbsp;&nbsp;&nbsp; <BR>　2 常规函数指针</P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">fp)();<IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN></DIV>
<P>　　fp 是一个典型的函数指针，用于指向无参数，无返回值的函数。</P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">fp2)(</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">);<IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN></DIV>
<P>　　fp2 也是一个函数指针，用于指向有一个整型参数，无返回值的函数。<BR>　　当然，有经验人士一般都会建议使用typedef来定义函数指针的类型，如：</P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">&nbsp;FP)();<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FP&nbsp;fp3;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;和上面的fp一样的定义。</SPAN></DIV>
<P>　　函数指针之所以让初学者畏惧，最主要的原因是它的括号太多了；某些用途的函数指针，往往会让人陷在括号堆中出不来，这里就不举例了，因为不是本文讨论的范围；typedef 方法可以有效的减少括号的数量，以及理清层次，所以受到推荐。本文暂时只考虑简单的函数指针，因此暂不用到typedef。<BR><BR>　　假如有如下两个函数：</P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;f1()<BR><IMG id=Codehighlighter1_14_61_Open_Image onclick="this.style.display='none'; Codehighlighter1_14_61_Open_Text.style.display='none'; Codehighlighter1_14_61_Closed_Image.style.display='inline'; Codehighlighter1_14_61_Closed_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><IMG id=Codehighlighter1_14_61_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_14_61_Closed_Text.style.display='none'; Codehighlighter1_14_61_Open_Image.style.display='inline'; Codehighlighter1_14_61_Open_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_14_61_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnblogs.com/Images/dot.gif"></SPAN><SPAN id=Codehighlighter1_14_61_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;&lt;</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">call&nbsp;f&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;&lt;</SPAN><SPAN style="COLOR: #000000">&nbsp;std::endl;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;f2(</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">&nbsp;a)<BR><IMG id=Codehighlighter1_85_147_Open_Image onclick="this.style.display='none'; Codehighlighter1_85_147_Open_Text.style.display='none'; Codehighlighter1_85_147_Closed_Image.style.display='inline'; Codehighlighter1_85_147_Closed_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><IMG id=Codehighlighter1_85_147_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_85_147_Closed_Text.style.display='none'; Codehighlighter1_85_147_Open_Image.style.display='inline'; Codehighlighter1_85_147_Open_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_85_147_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnblogs.com/Images/dot.gif"></SPAN><SPAN id=Codehighlighter1_85_147_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;&lt;</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">call&nbsp;f2(&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;&lt;</SPAN><SPAN style="COLOR: #000000">&nbsp;a&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;&lt;</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;)</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;&lt;</SPAN><SPAN style="COLOR: #000000">&nbsp;std::endl;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN></DIV>
<P>　　现在需要通过函数指针来调用，我们需要给指针指定函数：</P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">&nbsp;&nbsp;fp&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">f1;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;也可以用：fp&nbsp;=&nbsp;f1;</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;fp2</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">f2;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;也可以用：fp2=&nbsp;f2;</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;(</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">fp3)()&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">f1;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;也可以用：void&nbsp;(*fp3)()&nbsp;=&nbsp;f1;&nbsp;&nbsp;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">调用时如下：</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;fp();&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;或&nbsp;(*fp)();</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;fp2(</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">);&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;或&nbsp;(*fp2)(1);</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;fp3();&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;或&nbsp;(*fp3)();<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN></DIV>
<P>　　对于此两种调用方法，效果完全一样，我推荐用前一种。后一种不仅仅是多打了键盘，而且也损失了一些灵活性。这里暂且不说它。<BR>&nbsp;&nbsp;<BR>　　C++强调类型安全。也就是说，不同类型的变量是不能直接赋值的，否则轻则警告，重则报错。这是一个很有用的特性，常常能帮我们找到问题。因此，有识之士认为，C++中的任何一外警告都不能忽视。甚至有人提出，编译的时候不能出现任何警告信息，也就是说，警告应该当作错误一样处理。<BR>&nbsp;&nbsp;<BR>　　比如，我们把f1赋值给fp2，那么C++编译器(vc7.1)就会报错：</P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">&nbsp;&nbsp;fp2&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">f1;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;error&nbsp;C2440:&nbsp;“=”&nbsp;:&nbsp;无法从“void&nbsp;(__cdecl&nbsp;*)(void)”转换为“void&nbsp;(__cdecl&nbsp;*)(int)”</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;fp1&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">f1;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;OK<IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN></DIV>
<P>　　这样，编译器可以帮我们找出编码上的错误，节省了我们的排错时间。<BR>&nbsp;&nbsp;<BR>　　考虑一下C++标准模板库的sort函数：</P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;快速排序函数</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;template&lt;typename RandomAccessIterator, typename BinaryPredicate&gt;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;sort(<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RandomAccessIterator&nbsp;_First,&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;需排序数据的第一个元素位置</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RandomAccessIterator&nbsp;_Last,&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;需排序数据的最后一个元素位置（不参与排序）</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BinaryPredicate&nbsp;_Comp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;排序使用的比较算法(可以是函数指针、函数对象等)</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);</SPAN></DIV>
<P>　　比如，我们有一个整型数组：</P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG id=Codehighlighter1_13_23_Open_Image onclick="this.style.display='none'; Codehighlighter1_13_23_Open_Text.style.display='none'; Codehighlighter1_13_23_Closed_Image.style.display='inline'; Codehighlighter1_13_23_Closed_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><IMG id=Codehighlighter1_13_23_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_13_23_Closed_Text.style.display='none'; Codehighlighter1_13_23_Open_Image.style.display='inline'; Codehighlighter1_13_23_Open_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><SPAN style="COLOR: #000000">&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">&nbsp;n[</SPAN><SPAN style="COLOR: #000000">5</SPAN><SPAN style="COLOR: #000000">]&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN id=Codehighlighter1_13_23_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnblogs.com/Images/dot.gif"></SPAN><SPAN id=Codehighlighter1_13_23_Open_Text><SPAN style="COLOR: #000000">{</SPAN><SPAN style="COLOR: #000000">3</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">2</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">8</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">9</SPAN><SPAN style="COLOR: #000000">}</SPAN></SPAN><SPAN style="COLOR: #000000">;<IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN></DIV>
<P>　　要对它进行升序排序，我们需定义一个比较函数：</P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">bool</SPAN><SPAN style="COLOR: #000000">&nbsp;less(</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">&nbsp;a,&nbsp;</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">&nbsp;b)<BR><IMG id=Codehighlighter1_28_53_Open_Image onclick="this.style.display='none'; Codehighlighter1_28_53_Open_Text.style.display='none'; Codehighlighter1_28_53_Closed_Image.style.display='inline'; Codehighlighter1_28_53_Closed_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><IMG id=Codehighlighter1_28_53_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_28_53_Closed_Text.style.display='none'; Codehighlighter1_28_53_Open_Image.style.display='inline'; Codehighlighter1_28_53_Open_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_28_53_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnblogs.com/Images/dot.gif"></SPAN><SPAN id=Codehighlighter1_28_53_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;a&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">&nbsp;b;&nbsp;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;}</SPAN></SPAN></DIV>
<P>　　然后用：</P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">&nbsp;&nbsp;sort(n,&nbsp;n</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">5</SPAN><SPAN style="COLOR: #000000">,&nbsp;less);</SPAN></DIV>
<P>　　要是想对它进行降序排序，我们只要换一个比较函数就可以了。C/C++的标准模板已经提供了less和great函数，因此我们可以直接用下面的语句来比较：&nbsp;&nbsp;</P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">&nbsp;&nbsp;sort(n,&nbsp;n</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">5</SPAN><SPAN style="COLOR: #000000">,&nbsp;great);</SPAN></DIV>
<P><BR>　　这样，不需要改变sort函数的定义，就可以按任意方法进行排序，是不是很灵活？&nbsp;&nbsp;<BR>　　这种用法以C++的标准模板库(STL)中非常流行。另外，操作系统中也经常使用回调(CallBack)函数，实际上，所谓回调函数，本质就是函数指针。</P>
<P>　　看起来很简单吧，这是最普通的C语言指针的用法。本来这是一个很美妙的事情，但是当C++来临时，世界就开始变了样。<BR>　　假如，用来进行sort的比较函数是某个类的成员，那又如何呢？<BR></P><img src ="http://www.cppblog.com/richardzeng/aggbug/4137.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardzeng/" target="_blank">Beginning to 编程</a> 2006-03-14 16:44 <a href="http://www.cppblog.com/richardzeng/archive/2006/03/14/4137.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++指针探讨 (三) 成员函数指针 /zhuan</title><link>http://www.cppblog.com/richardzeng/archive/2006/03/14/4135.html</link><dc:creator>Beginning to 编程</dc:creator><author>Beginning to 编程</author><pubDate>Tue, 14 Mar 2006 08:42:00 GMT</pubDate><guid>http://www.cppblog.com/richardzeng/archive/2006/03/14/4135.html</guid><wfw:comment>http://www.cppblog.com/richardzeng/comments/4135.html</wfw:comment><comments>http://www.cppblog.com/richardzeng/archive/2006/03/14/4135.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardzeng/comments/commentRss/4135.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardzeng/services/trackbacks/4135.html</trackback:ping><description><![CDATA[C语言的指针相当的灵活方便，但也相当容易出错。许多C语言初学者，甚至C语言老鸟都很容易栽倒在C语言的指针下。但不可否认的是，指针在C语言中的位置极其重要，也许可以偏激一点的来说：没有指针的C程序不是真正的C程序。<BR>　　然而C++的指针却常常给我一种束手束脚的感觉。C++比C语言有更严格的静态类型，更加强调类型安全，强调编译时检查。因此，对于C语言中最容易错用的指针，更是不能放过：C++的指针被分成数据指针，数据成员指针，函数指针，成员函数指针，而且不能随便相互转换。而且这些指针的声明格式都不一样：
<P>
<TABLE style="WIDTH: 320px; BORDER-COLLAPSE: collapse" cellSpacing=0 cellPadding=3 border=1>
<TBODY>
<TR>
<TD>数据指针</TD>
<TD>T&nbsp;*</TD></TR>
<TR>
<TD>成员数据指针</TD>
<TD>T::*</TD></TR>
<TR>
<TD>函数指针</TD>
<TD>R&nbsp;(*)(...)</TD></TR>
<TR>
<TD>成员函数指针</TD>
<TD>R (T::*)(...)</TD></TR></TBODY></TABLE><BR>　　尽管C++中仍然有万能指针void*，但它却属于被批斗的对象，而且再也不能“万能”了。它不能转换成成员指针。</P>
<P>　　这样一来，C++的指针就变得很尴尬：我们需要一种指针能够指向同一类型的数据，不管这个数据是普通数据，还是成员数据；我们更需要一种指针能够指向同一类型的函数，不管这个函数是静态函数，还是成员函数。但是没有，至少从现在的C++标准中，还没有看到。<BR>　<BR><A id=BlogTitleLink href="http://ly4cn.cnblogs.com/"><FONT color=#6b86b3>沐枫网志</FONT></A>&nbsp;<A title=C++指针探讨(三)成员函数指针 href="http://ly4cn.cnblogs.com/archive/2006/03/13/349180.html">C++指针探讨(三)成员函数指针</A><BR><BR>　　自从有了类，我们开始按照　数据＋操作　的方式来组织数据结构；自从有了模板，我们又开始把 数据 和 算法 分离，以便重用，实在够折腾人的。但不管怎么折腾，现在大多数函数都不再单身，都嫁给了类，进了围城。可是我们仍然需要能够自由调用这些成员函数。<BR>　　考虑一下windows下的定时调用。SetTimer函数的原型是这样的：</P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">UINT_PTR&nbsp;SetTimer(<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;HWND&nbsp;hWnd,<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;UINT_PTR&nbsp;nIDEvent,<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;UINT&nbsp;uElapse,<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;TIMERPROC&nbsp;lpTimerFunc<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>);<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN></DIV>　　其中，参数就不解释了，这个函数估计大多数windows开发人员都知道。lpTimerFunc是个会被定时调用的函数指针。假如我们不通过WM_TIMER消息来触发定时器，而是通过lpTimerFunc来定时工作，那么我们就只能使用普通函数或静态函数，而无论如何都不能使用成员函数，哪怕通过静态函数转调也不行。<BR><BR>　　再考虑一下线程的创建：<BR>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">uintptr_t&nbsp;_beginthread(&nbsp;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">(&nbsp;</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">start_address&nbsp;)(&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">&nbsp;),<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;unsigned&nbsp;stack_size,<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">arglist&nbsp;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>);<IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN></DIV>　　start_address仍然只支持普通函数。不过这回好了，它允许回调函数一个void*参数，它将会arglist作为参数来调用start_address。于是，聪明的C++程序员，就利用arglist传递this指针，从而利用静态函数成功的调用到了成员函数了：<BR>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000">&nbsp;mythread<BR><IMG id=Codehighlighter1_15_113_Open_Image onclick="this.style.display='none'; Codehighlighter1_15_113_Open_Text.style.display='none'; Codehighlighter1_15_113_Closed_Image.style.display='inline'; Codehighlighter1_15_113_Closed_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><IMG id=Codehighlighter1_15_113_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_15_113_Closed_Text.style.display='none'; Codehighlighter1_15_113_Open_Image.style.display='inline'; Codehighlighter1_15_113_Open_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></SPAN><SPAN id=Codehighlighter1_15_113_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnblogs.com/Images/dot.gif"></SPAN><SPAN id=Codehighlighter1_15_113_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">:<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">static</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;doit(</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">&nbsp;pThis)<BR><IMG id=Codehighlighter1_65_90_Open_Image onclick="this.style.display='none'; Codehighlighter1_65_90_Open_Text.style.display='none'; Codehighlighter1_65_90_Closed_Image.style.display='inline'; Codehighlighter1_65_90_Closed_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_65_90_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_65_90_Closed_Text.style.display='none'; Codehighlighter1_65_90_Open_Image.style.display='inline'; Codehighlighter1_65_90_Open_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_65_90_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnblogs.com/Images/dot.gif"></SPAN><SPAN id=Codehighlighter1_65_90_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>　　　　((mythread*)pThis)</SPAN><SPAN style="COLOR: #000000">-&gt;</SPAN><SPAN style="COLOR: #000000">doit();<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG id=Codehighlighter1_107_111_Open_Image onclick="this.style.display='none'; Codehighlighter1_107_111_Open_Text.style.display='none'; Codehighlighter1_107_111_Closed_Image.style.display='inline'; Codehighlighter1_107_111_Closed_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_107_111_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_107_111_Closed_Text.style.display='none'; Codehighlighter1_107_111_Open_Image.style.display='inline'; Codehighlighter1_107_111_Open_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;doit()</SPAN><SPAN id=Codehighlighter1_107_111_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnblogs.com/Images/dot.gif"></SPAN><SPAN id=Codehighlighter1_107_111_Open_Text><SPAN style="COLOR: #000000">{<IMG src="http://www.cnblogs.com/Images/dot.gif">}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</SPAN></SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>main()<BR><IMG id=Codehighlighter1_124_211_Open_Image onclick="this.style.display='none'; Codehighlighter1_124_211_Open_Text.style.display='none'; Codehighlighter1_124_211_Closed_Image.style.display='inline'; Codehighlighter1_124_211_Closed_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><IMG id=Codehighlighter1_124_211_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_124_211_Closed_Text.style.display='none'; Codehighlighter1_124_211_Open_Image.style.display='inline'; Codehighlighter1_124_211_Open_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></SPAN><SPAN id=Codehighlighter1_124_211_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnblogs.com/Images/dot.gif"></SPAN><SPAN id=Codehighlighter1_124_211_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;<IMG src="http://www.cnblogs.com/Images/dot.gif"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;mythread</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">&nbsp;pmt&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;mythread;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;_beginthread(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">mythread::doit,&nbsp;</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">,&nbsp;(void*)pmt);<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;<IMG src="http://www.cnblogs.com/Images/dot.gif"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</SPAN></SPAN></DIV>
<P>　　但是显然，C++程序员肯定不会因此而满足。这里头有许多被C++批判的不安定因素。它使用了C++中被认为不安全的类型转换，不安全的void*指针，等等等等。但这是系统为C语言留下的调用接口，这也就认了。那么假如，我们就在C++程序中如何来调用成员函数指针呢？<BR>　　如下例，我们打算对vector中的所有类调用其指定的成员函数：</P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">#include&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">vector</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>#include&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">algorithm</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>#include&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">functional</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>#include&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">iostream</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">using</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">namespace</SPAN><SPAN style="COLOR: #000000">&nbsp;std;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000">&nbsp;A<BR><IMG id=Codehighlighter1_111_249_Open_Image onclick="this.style.display='none'; Codehighlighter1_111_249_Open_Text.style.display='none'; Codehighlighter1_111_249_Closed_Image.style.display='inline'; Codehighlighter1_111_249_Closed_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><IMG id=Codehighlighter1_111_249_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_111_249_Closed_Text.style.display='none'; Codehighlighter1_111_249_Open_Image.style.display='inline'; Codehighlighter1_111_249_Open_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></SPAN><SPAN id=Codehighlighter1_111_249_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnblogs.com/Images/dot.gif"></SPAN><SPAN id=Codehighlighter1_111_249_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">&nbsp;value;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">:<BR><IMG id=Codehighlighter1_142_153_Open_Image onclick="this.style.display='none'; Codehighlighter1_142_153_Open_Text.style.display='none'; Codehighlighter1_142_153_Closed_Image.style.display='inline'; Codehighlighter1_142_153_Closed_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_142_153_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_142_153_Closed_Text.style.display='none'; Codehighlighter1_142_153_Open_Image.style.display='inline'; Codehighlighter1_142_153_Open_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;A(</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">&nbsp;v)</SPAN><SPAN id=Codehighlighter1_142_153_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnblogs.com/Images/dot.gif"></SPAN><SPAN id=Codehighlighter1_142_153_Open_Text><SPAN style="COLOR: #000000">{value&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;v;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG id=Codehighlighter1_167_191_Open_Image onclick="this.style.display='none'; Codehighlighter1_167_191_Open_Text.style.display='none'; Codehighlighter1_167_191_Closed_Image.style.display='inline'; Codehighlighter1_167_191_Closed_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_167_191_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_167_191_Closed_Text.style.display='none'; Codehighlighter1_167_191_Open_Image.style.display='inline'; Codehighlighter1_167_191_Open_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;doit()</SPAN><SPAN id=Codehighlighter1_167_191_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnblogs.com/Images/dot.gif"></SPAN><SPAN id=Codehighlighter1_167_191_Open_Text><SPAN style="COLOR: #000000">{&nbsp;cout&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;&lt;</SPAN><SPAN style="COLOR: #000000">&nbsp;value&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;&lt;</SPAN><SPAN style="COLOR: #000000">&nbsp;endl;}</SPAN></SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">static</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;call_doit(A</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">&nbsp;rThis)<BR><IMG id=Codehighlighter1_228_247_Open_Image onclick="this.style.display='none'; Codehighlighter1_228_247_Open_Text.style.display='none'; Codehighlighter1_228_247_Closed_Image.style.display='inline'; Codehighlighter1_228_247_Closed_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_228_247_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_228_247_Closed_Text.style.display='none'; Codehighlighter1_228_247_Open_Image.style.display='inline'; Codehighlighter1_228_247_Open_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_228_247_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnblogs.com/Images/dot.gif"></SPAN><SPAN id=Codehighlighter1_228_247_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rThis.doit();<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</SPAN></SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">&nbsp;main()<BR><IMG id=Codehighlighter1_265_587_Open_Image onclick="this.style.display='none'; Codehighlighter1_265_587_Open_Text.style.display='none'; Codehighlighter1_265_587_Closed_Image.style.display='inline'; Codehighlighter1_265_587_Closed_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><IMG id=Codehighlighter1_265_587_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_265_587_Closed_Text.style.display='none'; Codehighlighter1_265_587_Open_Image.style.display='inline'; Codehighlighter1_265_587_Open_Text.style.display='inline';" src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></SPAN><SPAN id=Codehighlighter1_265_587_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnblogs.com/Images/dot.gif"></SPAN><SPAN id=Codehighlighter1_265_587_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;vector</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">A</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN><SPAN style="COLOR: #000000">&nbsp;va;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;va.push_back(A(</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">));<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;va.push_back(A(</SPAN><SPAN style="COLOR: #000000">2</SPAN><SPAN style="COLOR: #000000">));<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;va.push_back(A(</SPAN><SPAN style="COLOR: #000000">3</SPAN><SPAN style="COLOR: #000000">));<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;va.push_back(A(</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">));<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">方法1:<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">for_each(va.begin(),&nbsp;va.end(),&nbsp;&amp;A::doit);&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">error<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">方法2:</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;for_each(va.begin(),&nbsp;va.end(),&nbsp;</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">A::call_doit);<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">方法3:</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;for_each(va.begin(),&nbsp;va.end(),&nbsp;mem_fun_ref</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">,&nbsp;A</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">A::doit));<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;system(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">Pause</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://ly4cn.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></SPAN></DIV>
<P>　　方法1，编译不能通过。for_each只允许具有一个参数的函数指针或函数对象，哪怕A::doit默认有一个this指针参数也不行。不是for_each没考虑到这一点，而是根本做不到！<BR>　　方法2，显然是受到了beginthread的启发，使用一个静态函数来转调用，哈哈成功了。但是不爽！这不是C++。<BR>　　方法3，呼，好不容易啊，终于用mem_fun_ref包装成功了成员函数指针。<BR>　　似乎方法3不错，又是类型安全的，又可以通用－－慢着，首先，它很丑，哪有调用普通C函数指针那么漂亮啊（见方法2），用了一大串包装，又是尖括号又是圆括号，还少不了&amp;号！其次，它只能包装不超过一个参数的函数！尽管它在for_each中够用了，但是你要是想用在超过一个参数的场合，那只有一句话：不可能的任务。<BR></P>
<P>　　是的，在标准C++中，这是不可能的任务。但事情并不总是悲观的，至少有许多第三方库提供了超越mem_fun的包装。如boost::function等等。但是它也有限制：它所支持的参数仍然是有限的，只有十多个，尽管够你用的了；同样，它也是丑陋的，永远不要想它能够简单的用&amp;来搞定。<BR><BR>　　也许，以失去美丽的代价，来换取质量上的保证，这也是C++对于函数指针的一种无奈吧……<BR><BR>　　期待C++0x版本。它通过可变模板参数，能够让mem_fun的参数达到无限个……<BR><BR>－－－－－－－－<BR>&nbsp;&nbsp; BTW: C++Builder扩展了一个关键字 closure ，允许成员函数指针如同普通函数指针一样使用。也许C++0x能考虑一下……</P><img src ="http://www.cppblog.com/richardzeng/aggbug/4135.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardzeng/" target="_blank">Beginning to 编程</a> 2006-03-14 16:42 <a href="http://www.cppblog.com/richardzeng/archive/2006/03/14/4135.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ACM学习网站 </title><link>http://www.cppblog.com/richardzeng/archive/2006/03/10/3959.html</link><dc:creator>Beginning to 编程</dc:creator><author>Beginning to 编程</author><pubDate>Fri, 10 Mar 2006 03:01:00 GMT</pubDate><guid>http://www.cppblog.com/richardzeng/archive/2006/03/10/3959.html</guid><wfw:comment>http://www.cppblog.com/richardzeng/comments/3959.html</wfw:comment><comments>http://www.cppblog.com/richardzeng/archive/2006/03/10/3959.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardzeng/comments/commentRss/3959.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardzeng/services/trackbacks/3959.html</trackback:ping><description><![CDATA[<DIV class=postTitle>&nbsp;</DIV>同济大学的 Online Judge - <A href="http://acm.tongji.edu.cn/">http://acm.tongji.edu.cn/</A><BR>浙江大学的 Online Judge - <A href="http://acm.zju.edu.cn/">http://acm.zju.edu.cn/</A><BR>北京大学的 Online Judge - <A href="http://acm.pku.edu.cn/">http://acm.pku.edu.cn/</A><BR>吉林大学的 Online Judge - <A href="http://acm.jlu.edu.cn/">http://acm.jlu.edu.cn/</A><BR>四川大学的 Online Judge - <A href="http://cs.scu.edu.cn/acm">http://cs.scu.edu.cn/acm</A><BR>汕头大学的 Online Judge - <A href="http://acm.stu.edu.cn/">http://acm.stu.edu.cn/</A><BR>中科大的 Online Judge - <A href="http://acm.ustc.edu.cn/index.php">http://acm.ustc.edu.cn/index.php</A><BR>哈工大的 Online Judge - <A href="http://acm.hit.edu.cn/acm.php">http://acm.hit.edu.cn/acm.php</A><BR>西班牙的 Universidad de Valladolid - <A href="http://acm.uva.es/">http://acm.uva.es/</A><BR>俄罗斯乌拉尔大学 - <A href="http://acm.timus.ru/">http://acm.timus.ru/</A> <img src ="http://www.cppblog.com/richardzeng/aggbug/3959.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardzeng/" target="_blank">Beginning to 编程</a> 2006-03-10 11:01 <a href="http://www.cppblog.com/richardzeng/archive/2006/03/10/3959.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>结构化设计的救命稻草－回调机制  / 转 Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=253623</title><link>http://www.cppblog.com/richardzeng/archive/2006/03/09/3929.html</link><dc:creator>Beginning to 编程</dc:creator><author>Beginning to 编程</author><pubDate>Thu, 09 Mar 2006 03:27:00 GMT</pubDate><guid>http://www.cppblog.com/richardzeng/archive/2006/03/09/3929.html</guid><wfw:comment>http://www.cppblog.com/richardzeng/comments/3929.html</wfw:comment><comments>http://www.cppblog.com/richardzeng/archive/2006/03/09/3929.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardzeng/comments/commentRss/3929.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardzeng/services/trackbacks/3929.html</trackback:ping><description><![CDATA[<P>摘要：开发模式的确立是软件开发过程中不可缺少的一部分，就目前来说，面向过程和面向对象是两种主要的设计方法，虽然面向对象OOP是比较流行的字眼，但不表示面向过程就一定好无作为，毕竟面向过程设计方法也有适合其应用的软件系统：以功能操作为主，扩展性要求不高，无需过多考虑复用以及软件的通用性能。那是不是面向过程的设计方法对于诸如系统框架扩展问题就丝毫没有办法了呢？</P>
<P>按照面向过程的基本原则，划分系统功能模块、模块细分到函数、生成系统整体的结构模型，似乎在整个过程中没有任何东西可以用来提供系统扩展，其实解决的方法还是有的，这根救命稻草就是回调机制。</P>
<P>一谈到回调机制，当然就少不了我们的主角：系统API(通常都是)和回调函数，这两者缺一不可。其实回调的基本思想就是由系统给我们提供一些接口，也就是常使用的API，这种函数可以将某个其他函数的地址作为其参数之一，而且可以利用该地址对这个函数进行调用，而被调用的函数就是我们通常所说的回调函数了。<BR>下面给个回调函数使用的小例子：<BR>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<BR>//相当于我们提到的系统API<BR>mainFunc( void*&nbsp; userFunc )//当然参数不会这么简单，只是模拟<BR>{<BR>&nbsp;while (...)<BR>&nbsp;{<BR>&nbsp;&nbsp;printf("ok!");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //调用回调函数了<BR>&nbsp;&nbsp;if (userFunc!=NULL)&nbsp; <BR>&nbsp;&nbsp;&nbsp;userFunc();<BR>&nbsp;}<BR>}<BR>可以看出MainFunc可以根据函数userFunc的地址调用它。<BR>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<BR>这样使用者只需要定义一个函数：void myFunc()，然后按照mainFunc(&amp;myFunc)(&amp;只表示传递的是函数的地址，无具体含义)，就可以让我们的mainFunc来调用myFunc从而实现相应的功能，这样当然可以完成我们预期的目的－扩展现有系统。</P>
<P>在windows系统中，支持这种回调机制的系统API不占少数，像实现ListControl排序的SortItem()函数，还有操作Font使用的函数EnumFontFamilies()都有提供这种回调机制，使得我们的用户有机会添加自己期望的功能实现。当然，使用回调函数并不是一个轻松的事情，如果我们的系统中存在了大量的回调函数是很难管理的，这个就与系统中存在大量全局变量一样，出现多个函数争相访问同一个变量我们就很难使用简单的逻辑来处理，容易陷入混乱，因此，尽管回调机制可以在某种程度上达到我们的目的，但切不可乱加使用，不然后果很难预料。</P>
<P>当然至于详细的回调函数实现，还需要大家潜心研究，这里我只是总结一下：<BR>1 回调函数是由开发者按照一定的原型进行定义的函数(每个回调函数都必须遵循这个原型来设计)</P>
<P>例如：<BR>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<BR>BOOL CALLBACK DialogProc( <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp; HWND hwndDlg, // handle of dialog box <BR>&nbsp;&nbsp;&nbsp;&nbsp; UINT uMsg, // message <BR>&nbsp;&nbsp;&nbsp;&nbsp; WPARAM wParam, // first message parameter <BR>&nbsp;&nbsp;&nbsp;&nbsp; LPARAM lParam // second message parameter <BR>&nbsp;&nbsp;&nbsp;&nbsp; ); <BR>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<BR><FONT color=#ff3300>说明：<BR></FONT>回调函数必须有关键词 CALLBACK<BR>回调函数本身必须是全局函数或者静态函数，不可定义为某个特定的类的成员函数</P>
<P>2 回调函数并不由开发者直接调用执行(只是使用系统接口API函数作为起点)<BR>3 回调函数通常作为参数传递给系统API，由该API来调用<BR>4 回调函数可能被系统API调用一次，也可能被循环调用多次(SortItem就是自调用)</P>
<P>最后说句题外话，其实windows系统中还有另一种机制－消息机制，也是一个比较不错的工具，能够为很多实际的问题提供解决方法，这个以后再总结了。</P><img src ="http://www.cppblog.com/richardzeng/aggbug/3929.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardzeng/" target="_blank">Beginning to 编程</a> 2006-03-09 11:27 <a href="http://www.cppblog.com/richardzeng/archive/2006/03/09/3929.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>五大内存分区  /http://blog.csdn.net/welcome_ck/archive/2004/12/24/227961.aspx</title><link>http://www.cppblog.com/richardzeng/archive/2006/03/09/3928.html</link><dc:creator>Beginning to 编程</dc:creator><author>Beginning to 编程</author><pubDate>Thu, 09 Mar 2006 03:20:00 GMT</pubDate><guid>http://www.cppblog.com/richardzeng/archive/2006/03/09/3928.html</guid><wfw:comment>http://www.cppblog.com/richardzeng/comments/3928.html</wfw:comment><comments>http://www.cppblog.com/richardzeng/archive/2006/03/09/3928.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardzeng/comments/commentRss/3928.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardzeng/services/trackbacks/3928.html</trackback:ping><description><![CDATA[<P><BR>&nbsp;&nbsp;&nbsp; 在C++中，内存分成5个区，他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。<BR>&nbsp;&nbsp;&nbsp; 栈，就是那些由编译器在需要的时候分配，在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。<BR>&nbsp;&nbsp;&nbsp; 堆，就是那些由new分配的内存块，他们的释放编译器不去管，由我们的应用程序去控制，一般一个new就要对应一个delete。如果程序员没有释放掉，那么在程序结束后，操作系统会自动回收。<BR>&nbsp;&nbsp;&nbsp; 自由存储区，就是那些由malloc等分配的内存块，他和堆是十分相似的，不过它是用free来结束自己的生命的。<BR>&nbsp;&nbsp;&nbsp; 全局/静态存储区，全局变量和静态变量被分配到同一块内存中，在以前的C语言中，全局变量又分为初始化的和未初始化的，在C++里面没有这个区分了，他们共同占用同一块内存区。<BR>&nbsp;&nbsp;&nbsp; 常量存储区，这是一块比较特殊的存储区，他们里面存放的是常量，不允许修改（当然，你要通过非正当手段也可以修改，而且方法很多）<BR><STRONG>明确区分堆与栈<BR></STRONG>&nbsp;&nbsp;&nbsp; 在bbs上，堆与栈的区分问题，似乎是一个永恒的话题，由此可见，初学者对此往往是混淆不清的，所以我决定拿他第一个开刀。<BR>&nbsp;&nbsp;&nbsp; 首先，我们举一个例子：<BR>&nbsp;&nbsp;&nbsp; void f() { int* p=new int[5]; } <BR>&nbsp;&nbsp;&nbsp; 这条短短的一句话就包含了堆与栈，看到new，我们首先就应该想到，我们分配了一块堆内存，那么指针p呢？他分配的是一块栈内存，所以这句话的意思就是：在栈内存中存放了一个指向一块堆内存的指针p。在程序会先确定在堆中分配内存的大小，然后调用operator new分配内存，然后返回这块内存的首地址，放入栈中，他在VC6下的汇编代码如下：<BR>&nbsp;&nbsp;&nbsp; 00401028&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 14h<BR>&nbsp;&nbsp;&nbsp; 0040102A&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; operator new (00401060)<BR>&nbsp;&nbsp;&nbsp; 0040102F&nbsp;&nbsp; add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,4<BR>&nbsp;&nbsp;&nbsp; 00401032&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ebp-8],eax<BR>&nbsp;&nbsp;&nbsp; 00401035&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,dword ptr [ebp-8]<BR>&nbsp;&nbsp;&nbsp; 00401038&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ebp-4],eax<BR>&nbsp;&nbsp;&nbsp; 这里，我们为了简单并没有释放内存，那么该怎么去释放呢？是delete p么？澳，错了，应该是delete []p，这是为了告诉编译器：我删除的是一个数组，VC6就会根据相应的Cookie信息去进行释放内存的工作。<BR>&nbsp;&nbsp;&nbsp; 好了，我们回到我们的主题：堆和栈究竟有什么区别？ <BR>&nbsp;&nbsp;&nbsp; 主要的区别由以下几点：<BR>&nbsp;&nbsp;&nbsp; 1、管理方式不同；<BR>&nbsp;&nbsp;&nbsp; 2、空间大小不同；<BR>&nbsp;&nbsp;&nbsp; 3、能否产生碎片不同；<BR>&nbsp;&nbsp;&nbsp; 4、生长方向不同；<BR>&nbsp;&nbsp;&nbsp; 5、分配方式不同；<BR>&nbsp;&nbsp;&nbsp; 6、分配效率不同；<BR>&nbsp;&nbsp;&nbsp; 管理方式：对于栈来讲，是由编译器自动管理，无需我们手工控制；对于堆来说，释放工作由程序员控制，容易产生memory leak。<BR>&nbsp;&nbsp;&nbsp; 空间大小：一般来讲在32位系统下，堆内存可以达到4G的空间，从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲，一般都是有一定的空间大小的，例如，在VC6下面，默认的栈空间大小是1M（好像是，记不清楚了）。当然，我们可以修改：&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 打开工程，依次操作菜单如下：Project-&gt;Setting-&gt;Link，在Category 中选中Output，然后在Reserve中设定堆栈的最大值和commit。<BR>注意：reserve最小值为4Byte；commit是保留在虚拟内存的页文件里面，它设置的较大会使栈开辟较大的值，可能增加内存的开销和启动时间。<BR>&nbsp;&nbsp;&nbsp; 碎片问题：对于堆来讲，频繁的new/delete势必会造成内存空间的不连续，从而造成大量的碎片，使程序效率降低。对于栈来讲，则不会存在这个问题，因为栈是先进后出的队列，他们是如此的一一对应，以至于永远都不可能有一个内存块从栈中间弹出，在他弹出之前，在他上面的后进的栈内容已经被弹出，详细的可以参考数据结构，这里我们就不再一一讨论了。<BR>&nbsp;&nbsp;&nbsp; 生长方向：对于堆来讲，生长方向是向上的，也就是向着内存地址增加的方向；对于栈来讲，它的生长方向是向下的，是向着内存地址减小的方向增长。<BR>&nbsp;&nbsp;&nbsp; 分配方式：堆都是动态分配的，没有静态分配的堆。栈有2种分配方式：静态分配和动态分配。静态分配是编译器完成的，比如局部变量的分配。动态分配由alloca函数进行分配，但是栈的动态分配和堆是不同的，他的动态分配是由编译器进行释放，无需我们手工实现。<BR>&nbsp;&nbsp;&nbsp; 分配效率：栈是机器系统提供的数据结构，计算机会在底层对栈提供支持：分配专门的寄存器存放栈的地址，压栈出栈都有专门的指令执行，这就决定了栈的效率比较高。堆则是C/C++函数库提供的，它的机制是很复杂的，例如为了分配一块内存，库函数会按照一定的算法（具体的算法可以参考数据结构/操作系统）在堆内存中搜索可用的足够大小的空间，如果没有足够大小的空间（可能是由于内存碎片太多），就有可能调用系统功能去增加程序数据段的内存空间，这样就有机会分到足够大小的内存，然后进行返回。显然，堆的效率比栈要低得多。<BR>&nbsp;&nbsp;&nbsp; 从这里我们可以看到，堆和栈相比，由于大量new/delete的使用，容易造成大量的内存碎片；由于没有专门的系统支持，效率很低；由于可能引发用户态和核心态的切换，内存的申请，代价变得更加昂贵。所以栈在程序中是应用最广泛的，就算是函数的调用也利用栈去完成，函数调用过程中的参数，返回地址，EBP和局部变量都采用栈的方式存放。所以，我们推荐大家尽量用栈，而不是用堆。<BR>&nbsp;&nbsp;&nbsp; 虽然栈有如此众多的好处，但是由于和堆相比不是那么灵活，有时候分配大量的内存空间，还是用堆好一些。<BR>&nbsp;&nbsp;&nbsp; 无论是堆还是栈，都要防止越界现象的发生（除非你是故意使其越界），因为越界的结果要么是程序崩溃，要么是摧毁程序的堆、栈结构，产生以想不到的结果,就算是在你的程序运行过程中，没有发生上面的问题，你还是要小心，说不定什么时候就崩掉，那时候debug可是相当困难的：）<BR>&nbsp;&nbsp;&nbsp; 对了，还有一件事，如果有人把堆栈合起来说，那它的意思是栈，可不是堆，呵呵，清楚了？<BR><STRONG>static用来控制变量的存储方式和可见性<BR></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 函数内部定义的变量，在程序执行到它的定义处时，编译器为它在栈上分配空间，函数在栈上分配的空间在此函数执行结束时会释放掉，这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时，如何实现？ 最容易想到的方法是定义一个全局的变量，但定义为一个全局变量有许多缺点，最明显的缺点是破坏了此变量的访问范围（使得在此函数中定义的变量，不仅仅受此函数控制）。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部，对外不可见。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static的内部机制：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用，所以静态数据成员不能在任何函数内分配空间和初始化。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这样，它的空间分配有三个可能的地方，一是作为类的外部接口的头文件，那里有类声明；二是类定义的内部实现，那里有类的成员函数定义；三是应用程序的main（）函数前的全局数据声明和定义处。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 静态数据成员要实际地分配空间，故不能在类的声明中定义（只能声明数据成员）。类声明只声明一个类的“尺寸和规格”，并不进行实际的内存分配，所以在类声明中写成定义是错误的。它也不能在头文件中类声明的外部定义，因为那会造成在多个使用该类的源文件中，对其重复定义。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static被引入以告知编译器，将变量存储在程序的静态存储区而非栈上空间，静态<BR>数据成员按定义出现的先后顺序依次初始化，注意静态成员嵌套时，要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static的优势：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以节省内存，因为它是所有对象所公有的，因此，对多个对象来说，静态数据成员只存储一处，供所有对象共用。静态数据成员的值对每个对象都是一样，但它的值是可以更新的。只要对静态数据成员的值更新一次，保证所有对象存取更新后的相同的值，这样可以提高时间效率。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 引用静态数据成员时，采用如下格式：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;类名&gt;::&lt;静态成员名&gt;<BR>&nbsp;&nbsp;&nbsp; 如果静态数据成员的访问权限允许的话(即public的成员)，可在程序中，按上述格式<BR>来引用静态数据成员。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PS:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (1)类的静态成员函数是属于整个类而非类的对象，所以它没有this指针，这就导致<BR>了它仅能访问类的静态数据和静态成员函数。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (2)不能将静态成员函数定义为虚函数。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (3)由于静态成员声明于类中，操作于其外，所以对其取地址操作，就多少有些特殊<BR>，变量地址是指向其数据类型的指针 ，函数地址类型是一个“nonmember函数指针”。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (4)由于静态成员函数没有this指针，所以就差不多等同于nonmember函数，结果就<BR>产生了一个意想不到的好处：成为一个callback函数，使得我们得以将C++和C-based X W<BR>indow系统结合，同时也成功的应用于线程函数身上。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (5)static并没有增加程序的时空开销，相反她还缩短了子类对父类静态成员的访问<BR>时间，节省了子类的内存空间。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (6)静态数据成员在&lt;定义或说明&gt;时前面加关键字static。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (7)静态数据成员是静态存储的，所以必须对它进行初始化。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (8)静态成员初始化与一般数据成员初始化不同:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 初始化在类体外进行，而前面不加static，以免与一般静态变量或对象相混淆；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 初始化时不加该成员的访问权限控制符private，public等；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 初始化时使用作用域运算符来标明它所属类；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所以我们得出静态数据成员初始化的格式：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;数据类型&gt;&lt;类名&gt;::&lt;静态数据成员名&gt;=&lt;值&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (9)为了防止父类的影响，可以在子类定义一个与父类相同的静态变量，以屏蔽父类的影响。这里有一点需要注意：我们说静态成员为父类和子类共享，但我们有重复定义了静态成员，这会不会引起错误呢？不会，我们的编译器采用了一种绝妙的手法：name-mangling 用以生成唯一的标志。<BR></P><img src ="http://www.cppblog.com/richardzeng/aggbug/3928.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardzeng/" target="_blank">Beginning to 编程</a> 2006-03-09 11:20 <a href="http://www.cppblog.com/richardzeng/archive/2006/03/09/3928.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于C++中构造函数的说明 /转孙鑫VC++</title><link>http://www.cppblog.com/richardzeng/archive/2006/03/09/3925.html</link><dc:creator>Beginning to 编程</dc:creator><author>Beginning to 编程</author><pubDate>Thu, 09 Mar 2006 02:59:00 GMT</pubDate><guid>http://www.cppblog.com/richardzeng/archive/2006/03/09/3925.html</guid><wfw:comment>http://www.cppblog.com/richardzeng/comments/3925.html</wfw:comment><comments>http://www.cppblog.com/richardzeng/archive/2006/03/09/3925.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardzeng/comments/commentRss/3925.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardzeng/services/trackbacks/3925.html</trackback:ping><description><![CDATA[<P>在国内的C++图书中，关于构造函数的说明，要么是错误的，要么没有真正说清楚构造函数的作用，因此在我的视频中，对构造函数的讲解，也有一部分错误的叙述。对于C++构造函数一些错误认识的传播，我也相当于起了推波助澜的作用，在此反省一下，并给出正确的叙述。</P>
<P>（感谢西安软件园的王先生为我指出错误，感谢网友backer帮助我找出正确的答案。）</P>
<P>在光盘VC02中，在介绍构造函数时，我说：“构造函数最重要的作用是创建对象本身，对象内存的分配由构造函数来完成的”，这句话是错的，对象内存的分配和构造函数没有关系，对象内存的分配是由编译器来完成的，构造函数的作用是对对象本身做初始化工作，也就是给用户提供初始化类中成员变量的一种方式，在类对象有虚表的情况下，构造函数还对虚表进行初始化。</P>
<P>另外，我说：“C++又规定，如果一个类没有提供任何的构造函数，则C++提供一个默认的构造函数（由C++编译器提供）”，这句话也是错误的，正确的是：</P>
<P>如果一个类中没有定义任何的构造函数，那么编译器只有在以下三种情况，才会提供默认的构造函数：<BR>1、如果类有虚拟成员函数或者虚拟继承父类（即有虚拟基类）时；<BR>2、如果类的基类有构造函数（可以是用户定义的构造函数，或编译器提供的默认构造函数）；<BR>3、在类中的所有非静态的对象数据成员，它们对应的类中有构造函数（可以是用户定义的构造函数，或编译器提供的默认构造函数）。</P><img src ="http://www.cppblog.com/richardzeng/aggbug/3925.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardzeng/" target="_blank">Beginning to 编程</a> 2006-03-09 10:59 <a href="http://www.cppblog.com/richardzeng/archive/2006/03/09/3925.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++的多态性实现机制剖析 /转孙鑫</title><link>http://www.cppblog.com/richardzeng/archive/2006/03/09/3924.html</link><dc:creator>Beginning to 编程</dc:creator><author>Beginning to 编程</author><pubDate>Thu, 09 Mar 2006 02:56:00 GMT</pubDate><guid>http://www.cppblog.com/richardzeng/archive/2006/03/09/3924.html</guid><wfw:comment>http://www.cppblog.com/richardzeng/comments/3924.html</wfw:comment><comments>http://www.cppblog.com/richardzeng/archive/2006/03/09/3924.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/richardzeng/comments/commentRss/3924.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/richardzeng/services/trackbacks/3924.html</trackback:ping><description><![CDATA[<BR>
<P align=center>&nbsp;
<P>
<P align=center>――即VC++视频第三课this指针详细说明<BR>
<P>
<P>
<P align=center>作者：孙鑫 时间：<?xml:namespace prefix = st1 /><st1:chsdate w:st="on" IsROCDate="False" IsLunarDate="False" Day="12" Month="1" Year="2006">2006年1月12日</st1:chsdate>星期四<BR>
<P>
<P>
<P>要更好地理解C++的多态性，我们需要弄清楚函数覆盖的调用机制，因此，首先我们介绍一下函数的覆盖。</P>
<H1>1.&nbsp;&nbsp; 函数的覆盖</H1>
<P>我们先看一个例子：</P>
<DIV>
<P align=center><FONT size=2>例<FONT face=Arial>1- 1</FONT></FONT></P></DIV>
<P>#include &lt;iostream.h&gt;</P>
<P>class animal</P>
<P>{</P>
<P>public:</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void sleep()</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"animal sleep"&lt;&lt;endl;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <B>void breathe()<BR>
<P></B>
<P>
<P><B>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>
<P></B>
<P>
<P><B>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"animal breathe"&lt;&lt;endl;<BR>
<P></B>
<P>
<P><B>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>
<P></B>
<P>
<P>};</P>
<P>class fish:public animal</P>
<P>{</P>
<P>public:</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <B>void breathe()<BR>
<P></B>
<P>
<P><B>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>
<P></B>
<P>
<P><B>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"fish bubble"&lt;&lt;endl;<BR>
<P></B>
<P>
<P><B>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>
<P></B>
<P>
<P>};</P>
<P>void main()</P>
<P>{</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <B>fish fh;<BR>
<P></B>
<P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <B>animal *pAn=&amp;fh;<BR>
<P></B>
<P>
<P><B>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pAn-&gt;breathe();<BR>
<P></B>
<P>
<DIV>
<P>}</P></DIV>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意，在例1-1的程序中没有定义虚函数。考虑一下例1-1的程序执行的结果是什么？</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 答案是输出：animal breathe</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在类fish中重写了breathe()函数，我们可以称为函数的覆盖。在main()函数中首先定义了一个fish对象fh，接着定义了一个指向animal的指针变量pAn，将fh的地址赋给了指针变量pAn，然后利用该变量调用pAn-&gt;breathe()。许多学员往往将这种情况和C++的多态性搞混淆，认为fh实际上是fish类的对象，应该是调用fish类的breathe()，输出“fish bubble”，然后结果却不是这样。下面我们从两个方面来讲述原因。</P>
<P>1、&nbsp;&nbsp;编译的角度</P>
<P>C++编译器在编译的时候，要确定每个对象调用的函数的地址，这称为早期绑定（early binding），当我们将fish类的对象fh的地址赋给pAn时，C++编译器进行了类型转换，此时C++编译器认为变量pAn保存就是animal对象的地址。当在main()函数中执行pAn-&gt;breathe()时，调用的当然就是animal对象的breathe函数。</P>
<P>2、&nbsp;&nbsp;内存模型的角度</P>
<P>我们给出了fish对象内存模型，如下图所示：</P>
<P align=center></P>
<P>
<P><?xml:namespace prefix = v /><v:rect><FONT face="Times New Roman" size=3><IMG onmousewheel="return bbimg(this)" style="WIDTH: 401px; CURSOR: pointer; ZOOM: 80%; HEIGHT: 213px" onclick=javascript:window.open(this.src); height=188 src="http://www.mybole.com.cn/bbs/UploadFile/2006-1/200611213332815.gif" width=359 onload="javascript:if(this.width>screen.width-500)this.style.width=screen.width-500;" border=0></FONT></v:rect></P>
<P>
<P>
<P>
<P>
<P>
<P><BR><BR><BR><BR><BR>
<P><BR clear=all></P>
<P align=center>图1- 1 fish类对象的内存模型</P>
<P>
<P><FONT face="Times New Roman" size=3></FONT></P>
<P><FONT size=3>
<P>我们构造<FONT face="Times New Roman">fish</FONT>类的对象时，首先要调用<FONT face="Times New Roman">animal</FONT>类的构造函数去构造<FONT face="Times New Roman">animal</FONT>类的对象，然后才调用<FONT face="Times New Roman">fish</FONT>类的构造函数完成自身部分的构造，从而拼接出一个完整的<FONT face="Times New Roman">fish</FONT>对象。当我们将<FONT face="Times New Roman">fish</FONT>类的对象转换为<FONT face="Times New Roman">animal</FONT>类型时，该对象就被认为是原对象整个内存模型的上半部分，也就是图<FONT face="Times New Roman">1-1</FONT>中的“<FONT face="Times New Roman">animal</FONT>的对象所占内存”。那么当我们利用类型转换后的对象指针去调用它的方法时，当然也就是调用它所在的内存中的方法。因此，出现图<FONT face="Times New Roman">2.13</FONT>所示的结果，也就顺理成章了。</P>
<H1><FONT face="Times New Roman">2.<FONT size=6>&nbsp;&nbsp; </FONT></FONT>多态性和虚函数</H1>
<P>正如很多学员所想，在例<FONT face="Times New Roman">1-1</FONT>的程序中，我们知道<FONT face="Times New Roman">pAn</FONT>实际指向的是<FONT face="Times New Roman">fish</FONT>类的对象，我们希望输出的结果是鱼的呼吸方法，即调用<FONT face="Times New Roman">fish</FONT>类的<FONT face="Times New Roman">breathe</FONT>方法。这个时候，就该轮到虚函数登场了。</P>
<P>前面输出的结果是因为编译器在编译的时候，就已经确定了对象调用的函数的地址，要解决这个问题就要使用迟绑定（<FONT face="Times New Roman">late binding</FONT>）技术。当编译器使用迟绑定时，就会在运行时再去确定对象的类型以及正确的调用函数。而要让编译器采用迟绑定，就要在基类中声明函数时使用<FONT face="Times New Roman">virtual</FONT>关键字（注意，这是必须的，很多学员就是因为没有使用虚函数而写出很多错误的例子），这样的函数我们称为虚函数。一旦某个函数在基类中声明为<FONT face="Times New Roman">virtual</FONT>，那么在所有的派生类中该函数都是<FONT face="Times New Roman">virtual</FONT>，而不需要再显示的声明为<FONT face="Times New Roman">virtual</FONT>。</P>
<P>下面修改例<FONT face="Times New Roman">1-1</FONT>的代码，将<FONT face="Times New Roman">animal</FONT>类中的<FONT face="Times New Roman">breathe()</FONT>函数声明为<FONT face="Times New Roman">virtual</FONT>，如下：</P>
<DIV>
<P align=center><FONT size=2>例<FONT face=Arial>1- 2</FONT></FONT></P></DIV>
<P><FONT face="Times New Roman">#include &lt;iostream.h&gt;</FONT></P>
<P><FONT face="Times New Roman">class animal</FONT></P>
<P><FONT face="Times New Roman">{</FONT></P>
<P><FONT face="Times New Roman">public:</FONT></P>
<P><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void sleep()</FONT></P>
<P><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</FONT></P>
<P><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"animal sleep"&lt;&lt;endl;</FONT></P>
<P><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</FONT></P>
<P><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <B>virtual </B>void breathe()</FONT></P>
<P><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</FONT></P>
<P><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"animal breathe"&lt;&lt;endl;</FONT></P>
<P><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</FONT></P>
<P><FONT face="Times New Roman">};</FONT></P>
<P><FONT face="Times New Roman">class fish:public animal</FONT></P>
<P><FONT face="Times New Roman">{</FONT></P>
<P><FONT face="Times New Roman">public:</FONT></P>
<P><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void breathe()</FONT></P>
<P><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</FONT></P>
<P><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"fish bubble"&lt;&lt;endl;</FONT></P>
<P><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</FONT></P>
<P><FONT face="Times New Roman">};</FONT></P>
<P><FONT face="Times New Roman">void main()</FONT></P>
<P><FONT face="Times New Roman">{</FONT></P>
<P><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fish fh;</FONT></P>
<P><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; animal *pAn=&amp;fh;</FONT></P>
<P><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pAn-&gt;breathe();</FONT></P>
<DIV>
<P><FONT face="Times New Roman">}</FONT></P></DIV>
<P>大家可以再次运行这个程序，你会发现结果是“<FONT face="Times New Roman">fish bubble</FONT>”，也就是根据对象的类型调用了正确的函数。</P>
<P>那么当我们将<FONT face="Times New Roman">breathe()</FONT>声明为<FONT face="Times New Roman">virtual</FONT>时，在背后发生了什么呢？</P>
<P>编译器在编译的时候，发现animal类中有虚函数，此时编译器会为每个包含虚函数的类创建一个虚表（即vtable），该表是一个一维数组，在这个数组中存放每个虚函数的地址。对于例1-2的程序，animal和fish类都包含了一个虚函数breathe()，因此编译器会为这两个类都建立一个虚表，如下图所示：</FONT><IMG onmousewheel="return bbimg(this)" style="CURSOR: pointer; ZOOM: 90%" onclick=javascript:window.open(this.src); src="http://www.mybole.com.cn/bbs/UploadFile/2006-1/200611213817767.gif" onload="javascript:if(this.width>screen.width-500)this.style.width=screen.width-500;" border=0></P>
<P align=center>图1- 2 animal类和fish类的虚表</P>
<P><FONT size=3><FONT face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT>那么如何定位虚表呢？编译器另外还为每个类提供了一个虚表指针（即<FONT face="Times New Roman">vptr</FONT>），这个指针指向了对象的虚表。在程序运行时，根据对象的类型去初始化<FONT face="Times New Roman">vptr</FONT>，从而让<FONT face="Times New Roman">vptr</FONT>正确的指向所属类的虚表，从而在调用虚函数时，就能够找到正确的函数。对于例<FONT face="Times New Roman">1-2</FONT>的程序，由于<FONT face="Times New Roman">pAn</FONT>实际指向的对象类型是<FONT face="Times New Roman">fish</FONT>，因此<FONT face="Times New Roman">vptr</FONT>指向的<FONT face="Times New Roman">fish</FONT>类的<FONT face="Times New Roman">vtable</FONT>，当调用<FONT face="Times New Roman">pAn-&gt;breathe()</FONT>时，根据虚表中的函数地址找到的就是<FONT face="Times New Roman">fish</FONT>类的<FONT face="Times New Roman">breathe()</FONT>函数。</FONT></P>
<P><FONT size=3>正是由于每个对象调用的虚函数都是通过虚表指针来索引的，也就决定了虚表指针的正确初始化是非常重要的。换句话说，在虚表指针没有正确初始化之前，我们不能够去调用虚函数。那么虚表指针在什么时候，或者说在什么地方初始化呢？</FONT></P>
<P><FONT size=3>答案是在构造函数中进行虚表的创建和虚表指针的初始化。还记得构造函数的调用顺序吗，在构造子类对象时，要先调用父类的构造函数，此时编译器只“看到了”父类，并不知道后面是否后还有继承者，它初始化父类的虚表指针，该虚表指针指向父类的虚表。当执行子类的构造函数时，子类的虚表指针被初始化，指向自身的虚表。对于例<FONT face="Times New Roman">2-2</FONT>的程序来说，当<FONT face="Times New Roman">fish</FONT>类的<FONT face="Times New Roman">fh</FONT>对象构造完毕后，其内部的虚表指针也就被初始化为指向<FONT face="Times New Roman">fish</FONT>类的虚表。在类型转换后，调用<FONT face="Times New Roman">pAn-&gt;breathe()</FONT>，由于<FONT face="Times New Roman">pAn</FONT>实际指向的是<FONT face="Times New Roman">fish</FONT>类的对象，该对象内部的虚表指针指向的是<FONT face="Times New Roman">fish</FONT>类的虚表，因此最终调用的是<FONT face="Times New Roman">fish</FONT>类的<FONT face="Times New Roman">breathe()</FONT>函数。</FONT></P>
<P><FONT size=3>要注意：对于虚函数调用来说，每一个对象内部都有一个虚表指针，该虚表指针被初始化为本类的虚表。所以在程序中，不管你的对象类型如何转换，但该对象内部的虚表指针是固定的，所以呢，才能实现动态的对象函数调用，这就是<FONT face="Times New Roman">C++</FONT>多态性实现的原理。</FONT></P>
<P><FONT size=3>总结（基类有虚函数）：</FONT></P>
<P><FONT face="Times New Roman"><FONT size=3>1、</FONT>&nbsp;&nbsp;</FONT><FONT size=3>每一个类都有虚表。</FONT></P>
<P><FONT face="Times New Roman"><FONT size=3>2、</FONT>&nbsp;&nbsp;</FONT><FONT size=3>虚表可以继承，如果子类没有重写虚函数，那么子类虚表中仍然会有该函数的地址，只不过这个地址指向的是基类的虚函数实现。如果基类<FONT face="Times New Roman">3</FONT>个虚函数，那么基类的虚表中就有三项（虚函数地址），派生类也会有虚表，至少有三项，如果重写了相应的虚函数，那么虚表中的地址就会改变，指向自身的虚函数实现。如果派生类有自己的虚函数，那么虚表中就会添加该项。</FONT></P>
<P><FONT face="Times New Roman"><FONT size=3>3、</FONT>&nbsp;&nbsp;</FONT><FONT size=3>派生类的虚表中虚函数地址的排列顺序和基类的虚表中虚函数地址排列顺序相同。</FONT></P>
<H1><FONT face="Times New Roman">3.&nbsp;&nbsp; VC</FONT>视频第三课<FONT face="Times New Roman">this</FONT>指针说明</H1>
<P><FONT size=3>我在论坛的<FONT face="Times New Roman">VC</FONT>教学视频版面发了帖子，是模拟<FONT face="Times New Roman">MFC</FONT>类库的例子写的，主要是说明在基类的构造函数中保存的<FONT face="Times New Roman">this</FONT>指针是指向子类的，我们在看一下这个例子：</FONT></P>
<DIV>
<P align=center>例1- 3</P></DIV>
<P><FONT face="Times New Roman"><FONT size=3>#include &lt;iostream.h&gt;<BR>
<P></FONT></FONT>
<P>
<P>
<P><FONT face="Times New Roman" size=3></FONT></P>
<P>
<P><FONT face="Times New Roman"><FONT size=3>class base;<BR>
<P></FONT></FONT>
<P>
<P>
<P><FONT face="Times New Roman" size=3></FONT></P>
<P>
<P><FONT face="Times New Roman"><FONT size=3>base * pbase;<BR>
<P></FONT></FONT>
<P>
<P>
<P><FONT face="Times New Roman" size=3></FONT></P>
<P>
<P><FONT face="Times New Roman"><FONT size=3>class base<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>{<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>public:<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; base()<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pbase=this;<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual void fn()<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"base"&lt;&lt;endl;<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>};<BR>
<P></FONT></FONT>
<P>
<P>
<P><FONT face="Times New Roman" size=3></FONT></P>
<P>
<P><FONT face="Times New Roman"><FONT size=3>class derived:public base<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>{<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void fn()<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"derived"&lt;&lt;endl;<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>};<BR>
<P></FONT></FONT>
<P>
<P>
<P><FONT face="Times New Roman" size=3></FONT></P>
<P>
<P><FONT face="Times New Roman"><FONT size=3>derived aa;<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>void main()<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>{<BR>
<P></FONT></FONT>
<P>
<P><FONT face="Times New Roman"><FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pbase-&gt;fn();<BR>
<P></FONT></FONT>
<P>
<DIV>
<P><FONT face="Times New Roman" size=3>}</FONT></P></DIV>
<P><FONT size=3>我在<FONT face="Times New Roman">base</FONT>类的构造函数中将<FONT face="Times New Roman">this</FONT>指针保存到<FONT face="Times New Roman">pbase</FONT>全局变量中。在定义全局对象<FONT face="Times New Roman">aa</FONT>，即调用<FONT face="Times New Roman">derived aa;</FONT>时，要调用基类的构造函数，先构造基类的部分，然后是子类的部分，由这两部分拼接出完整的对象<FONT face="Times New Roman">aa</FONT>。这个<FONT face="Times New Roman">this</FONT>指针指向的当然也就是<FONT face="Times New Roman">aa</FONT>对象，那么我们<FONT face="Times New Roman">main()</FONT>函数中利用<FONT face="Times New Roman">pbase</FONT>调用<FONT face="Times New Roman">fn()</FONT>，因为<FONT face="Times New Roman">pbase</FONT>实际指向的是<FONT face="Times New Roman">aa</FONT>对象，而<FONT face="Times New Roman">aa</FONT>对象内部的虚表指针指向的是自身的虚表，最终调用的当然是<FONT face="Times New Roman">derived</FONT>类中的<FONT face="Times New Roman">fn()</FONT>函数。</FONT></P>
<P><FONT size=3>在这个例子中，由于我的疏忽，在<FONT face="Times New Roman">derived</FONT>类中声明<FONT face="Times New Roman">fn()</FONT>函数时，忘了加<FONT face="Times New Roman">public</FONT>关键字，导致声明为了<FONT face="Times New Roman">private</FONT>（默认为<FONT face="Times New Roman">private</FONT>），但通过前面我们所讲述的虚函数调用机制，我们也就明白了这个地方并不影响它输出正确的结果。不知道这算不算<FONT face="Times New Roman">C++</FONT>的一个<FONT face="Times New Roman">Bug</FONT>，因为虚函数的调用是在运行时确定调用哪一个函数，所以编译器在编译时，并不知道<FONT face="Times New Roman">pbase</FONT>指向的是<FONT face="Times New Roman">aa</FONT>对象，所以导致这个奇怪现象的发生。如果你直接用<FONT face="Times New Roman">aa</FONT>对象去调用，由于对象类型是确定的（注意<FONT face="Times New Roman">aa</FONT>是对象变量，不是指针变量），编译器往往会采用早期绑定，在编译时确定调用的函数，于是就会发现<FONT face="Times New Roman">fn()</FONT>是私有的，不能直接调用。：）</FONT></P>
<P><FONT size=3>许多学员在写这个例子时，直接在基类的构造函数中调用虚函数，前面已经说了，在调用基类的构造函数时，编译器只“看到了”父类，并不知道后面是否后还有继承者，它只是初始化父类的虚表指针，让该虚表指针指向父类的虚表，所以你看到结果当然不正确。只有在子类的构造函数调用完毕后，整个虚表才构建完毕，此时才能真正应用<FONT face="Times New Roman">C++</FONT>的多态性。<B>换句话说，我们不要在构造函数中去调用虚函数，当然如果你只是想调用本类的函数，也无所谓。</B><B> 
<P></B></FONT>
<P>
<H1><FONT face="Times New Roman">4.<FONT size=6>&nbsp;&nbsp; </FONT></FONT>参考资料：</H1>
<P><FONT size=3><FONT face="Times New Roman">1</FONT>、文章《在<FONT face="Times New Roman">VC6.0</FONT>中虚函数的实现方法》，作者：<FONT face="Times New Roman">backer </FONT>，网址：</FONT></P>
<P><FONT face="Times New Roman" size=3>http://www.mybole.com.cn/bbs/dispbbs.asp?boardid=4&amp;id=1012&amp;star=1</FONT></P>
<P><FONT size=3><FONT face="Times New Roman">2</FONT>、书《<FONT face="Times New Roman">C++</FONT>编程思想》<FONT face="Times New Roman"> </FONT>机械工业出版社</FONT></P>
<H1><FONT face="Times New Roman">5.<FONT size=6>&nbsp;&nbsp; </FONT></FONT>后记</H1>
<P><FONT size=3>本想再写详细些，发现时间不够，总是有很多事情，在加上水平也有限，想想还是以后再说吧。不过我相信，这些内容也能够帮助大家很好的理解了。也欢迎网友能够继续补充，大家可以鼓动鼓动<FONT face="Times New Roman">backer</FONT>，让他从汇编的角度再给一个说明，哈哈，别说我说的。</FONT></P><BR><img src ="http://www.cppblog.com/richardzeng/aggbug/3924.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/richardzeng/" target="_blank">Beginning to 编程</a> 2006-03-09 10:56 <a href="http://www.cppblog.com/richardzeng/archive/2006/03/09/3924.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>