⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 帮你理解回调函数_大肚水杯.htm

📁 最全的介绍C语言结构体的使用方法和使用技巧!
💻 HTM
📖 第 1 页 / 共 2 页
字号:
href="http://hi.baidu.com/kaixinlaohe/blog">博客</A><A 
href="http://hi.baidu.com/kaixinlaohe/album">相册</A><SPAN>|</SPAN><A 
href="http://hi.baidu.com/kaixinlaohe/profile">个人档案</A> <SPAN>|</SPAN><A 
href="http://hi.baidu.com/kaixinlaohe/friends">好友</A> </DIV></DIV>
<DIV class=stage>
<DIV class=stagepad>
<DIV style="WIDTH: 100%">
<TABLE class=modth cellSpacing=0 cellPadding=0 width="100%" border=0>
  <TBODY>
  <TR>
    <TD class=modtl width=7>&nbsp;</TD>
    <TD class=modtc noWrap>
      <DIV class=modhead><SPAN class=modtit>查看文章</SPAN></DIV></TD>
    <TD class=modtc noWrap align=right></TD>
    <TD class=modtr width=7>&nbsp;</TD></TR></TBODY></TABLE>
<DIV class=modbox id=m_blog>
<DIV class=tit>帮你理解回调函数</DIV>
<DIV class=date>2007年05月06日 下午 10:06</DIV>
<TABLE style="TABLE-LAYOUT: fixed">
  <TBODY>
  <TR>
    <TD>
      <DIV 
      class=cnt><STRONG>什么是回调函数?<BR><BR></STRONG>  简而言之,回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。<BR><BR>  <STRONG>为什么要使用回调函数?<BR><BR></STRONG>  因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。<BR><BR>  如果想知道回调函数在实际中有什么作用,先假设有这样一种情况,我们要编写一个库,它提供了某些排序算法的实现,如冒泡排序、快速排序、shell排序、shake排序等等,但为使库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,想让库可用于多种数据类型(int、float、string),此时,该怎么办呢?可以使用函数指针,并进行回调。<BR><BR>  回调可用于通知机制,例如,有时要在程序中设置一个计时器,每到一定时间,程序会得到相应的通知,但通知机制的实现者对我们的程序一无所知。而此时,就需有一个特定原型的函数指针,用这个指针来进行回调,来通知我们的程序事件已经发生。实际上,SetTimer() 
      API使用了一个回调函数来通知计时器,而且,万一没有提供回调函数,它还会把一个消息发往程序的消息队列。<BR><BR>  另一个使用回调机制的API函数是EnumWindow(),它枚举屏幕上所有的顶层窗口,为每个窗口调用一个程序提供的函数,并传递窗口的处理程序。如果被调用者返回一个值,就继续进行迭代,否则,退出。EnumWindow()并不关心被调用者在何处,也不关心被调用者用它传递的处理程序做了什么,它只关心返回值,因为基于返回值,它将继续执行或退出。<BR><BR>  不管怎么说,回调函数是继续自C语言的,因而,在C++中,应只在与C代码建立接口,或与已有的回调接口打交道时,才使用回调函数。除了上述情况,在C++中应使用虚拟方法或函数符(functor),而不是回调函数。<BR><BR>  <STRONG>一个简单的回调函数实现<BR><BR></STRONG>  下面创建了一个sort.dll的动态链接库,它导出了一个名为CompareFunction的类型--typedef 
      int (__stdcall *CompareFunction)(const byte*, const 
      byte*),它就是回调函数的类型。另外,它也导出了两个方法:Bubblesort()和Quicksort(),这两个方法原型相同,但实现了不同的排序算法。<BR><BR>
      <P>
      <TABLE borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 
      border=1>
        <TBODY>
        <TR>
          <TD><STRONG>void DLLDIR __stdcall Bubblesort(byte* array,int 
            size,int elem_size,CompareFunction cmpFunc);<BR><BR>void DLLDIR 
            __stdcall Quicksort(byte* array,int size,int 
            elem_size,CompareFunction cmpFunc);</STRONG></TD></TR></TBODY></TABLE></P>
      <P><BR><STRONG>  这两个函数接受以下参数:<BR><BR>  ·byte * 
      array:指向元素数组的指针(任意类型)。<BR><BR>  ·int size:数组中元素的个数。<BR><BR>  ·int 
      elem_size:数组中一个元素的大小,以字节为单位。<BR><BR>  ·CompareFunction 
      cmpFunc:带有上述原型的指向回调函数的指针。<BR><BR>  这两个函数的会对数组进行某种排序,但每次都需决定两个元素哪个排在前面,而函数中有一个回调函数,其地址是作为一个参数传递进来的。对编写者来说,不必介意函数在何处实现,或它怎样被实现的,所需在意的只是两个用于比较的元素的地址,并返回以下的某个值(库的编写者和使用者都必须遵守这个约定):<BR><BR>  ·-1:如果第一个元素较小,那它在已排序好的数组中,应该排在第二个元素前面。<BR><BR>  ·0:如果两个元素相等,那么它们的相对位置并不重要,在已排序好的数组中,谁在前面都无所谓。 
      <BR><BR>  ·1:如果第一个元素较大,那在已排序好的数组中,它应该排第二个元素后面。<BR><BR>  基于以上约定,函数Bubblesort()的实现如下,Quicksort()就稍微复杂一点:<BR><BR></STRONG></P>
      <P>
      <TABLE borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 
      border=1>
        <TBODY>
        <TR>
          <TD><STRONG>void DLLDIR __stdcall Bubblesort(byte* array,int 
            size,int elem_size,CompareFunction cmpFunc)<BR>{<BR> for(int i=0; i 
            &lt; size; i++)<BR> {<BR>  for(int j=0; j &lt; size-1; 
            j++)<BR>  {<BR>   //回调比较函数<BR>   if(1 == 
            (*cmpFunc)(array+j*elem_size,array+(j+1)*elem_size))<BR>   {<BR>    //两个相比较的元素相交换<BR>    byte* 
            temp = new byte[elem_size];<BR>    memcpy(temp, array+j*elem_size, 
            elem_size);<BR>    memcpy(array+j*elem_size,array+(j+1)*elem_size,elem_size);<BR>    memcpy(array+(j+1)*elem_size, 
            temp, elem_size);<BR>    delete [] 
            temp;<BR>   }<BR>  }<BR> }<BR>}</STRONG></TD></TR></TBODY></TABLE></P>
      <P><BR><STRONG>  注意:因为实现中使用了memcpy(),所以函数在使用的数据类型方面,会有所局限。<BR><BR>  对使用者来说,必须有一个回调函数,其地址要传递给Bubblesort()函数。下面有二个简单的示例,一个比较两个整数,而另一个比较两个字符串:<BR><BR></STRONG></P>
      <P>
      <TABLE borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 
      border=1>
        <TBODY>
        <TR>
          <TD><STRONG>int __stdcall CompareInts(const byte* velem1, const 
            byte* velem2)<BR>{<BR> int elem1 = *(int*)velem1;<BR> int elem2 = 
            *(int*)velem2;<BR><BR> if(elem1 &lt; elem2)<BR>  return 
            -1;<BR> if(elem1 &gt; elem2)<BR>  return 1;<BR><BR> return 
            0;<BR>}<BR><BR>int __stdcall CompareStrings(const byte* velem1, 
            const byte* velem2)<BR>{<BR> const char* elem1 = 
            (char*)velem1;<BR> const char* elem2 = (char*)velem2;<BR> return 
            strcmp(elem1, elem2);<BR>}</STRONG></TD></TR></TBODY></TABLE></P>
      <P><BR><STRONG>  下面另有一个程序,用于测试以上所有的代码,它传递了一个有5个元素的数组给Bubblesort()和Quicksort(),同时还传递了一个指向回调函数的指针。<BR><BR></STRONG></P>
      <P>
      <TABLE borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 
      border=1>
        <TBODY>
        <TR>
          <TD><STRONG>int main(int argc, char* argv[])<BR>{<BR> int i;<BR> int 
            array[] = {5432, 4321, 3210, 2109, 1098};<BR><BR> cout &lt;&lt; 
            "Before sorting ints with Bubblesort\n";<BR> for(i=0; i &lt; 5; 
            i++)<BR>  cout &lt;&lt; array[i] &lt;&lt; 
            '\n';<BR><BR> Bubblesort((byte*)array, 5, sizeof(array[0]), 
            &amp;CompareInts);<BR><BR> cout &lt;&lt; "After the 
            sorting\n";<BR> for(i=0; i &lt; 5; i++)<BR>  cout &lt;&lt; array[i] 
            &lt;&lt; '\n';<BR><BR> const char str[5][10] = 
            {"estella","danielle","crissy","bo","angie"};<BR><BR> cout &lt;&lt; 
            "Before sorting strings with Quicksort\n";<BR> for(i=0; i &lt; 5; 
            i++)<BR>  cout &lt;&lt; str[i] &lt;&lt; 
            '\n';<BR><BR> Quicksort((byte*)str, 5, 10, 
            &amp;CompareStrings);<BR><BR> cout &lt;&lt; "After the 
            sorting\n";<BR> for(i=0; i &lt; 5; i++)<BR>  cout &lt;&lt; str[i] 
            &lt;&lt; '\n';<BR><BR> return 
      0;<BR>}</STRONG></TD></TR></TBODY></TABLE></P>
      <P><BR><STRONG>  如果想进行降序排序(大元素在先),就只需修改回调函数的代码,或使用另一个回调函数,这样编程起来灵活性就比较大了。<BR></STRONG></P>
      <P><STRONG>调用约定<BR><BR></STRONG>  上面的代码中,可在函数原型中找到__stdcall,因为它以双下划线打头,所以它是一个特定于编译器的扩展,说到底也就是微软的实现。任何支持开发基于Win32的程序都必须支持这个扩展或其等价物。以__stdcall标识的函数使用了标准调用约定,为什么叫标准约定呢,因为所有的Win32 
      API(除了个别接受可变参数的除外)都使用它。标准调用约定的函数在它们返回到调用者之前,都会从堆栈中移除掉参数,这也是Pascal的标准约定。但在C/C++中,调用约定是调用者负责清理堆栈,而不是被调用函数;为强制函数使用C/C++调用约定,可使用__cdecl。另外,可变参数函数也使用C/C++调用约定。<BR><BR>  Windows操作系统采用了标准调用约定(Pascal约定),因为其可减小代码的体积。这点对早期的Windows来说非常重要,因为那时它运行在只有640KB内存的电脑上。<BR><BR>  如果你不喜欢__stdcall,还可以使用CALLBACK宏,它定义在windef.h中:<BR><BR></P>
      <TABLE borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 
      border=1>
        <TBODY>
        <TR>
          <TD>#define CALLBACK __stdcallor<BR><BR>#define CALLBACK PASCAL 
            //而PASCAL在此被#defined成__stdcall</TD></TR></TBODY></TABLE><BR>  <STRONG>作为回调函数的C++方法</STRONG><BR><BR>  因为平时很可能会使用到C++编写代码,也许会想到把回调函数写成类中的一个方法,但先来看看以下的代码:<BR><BR>
      <TABLE borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 
      border=1>
        <TBODY>
        <TR>
          <TD>class CCallbackTester<BR>{<BR> public:<BR> int CALLBACK 
            CompareInts(const byte* velem1, const byte* 
            velem2);<BR>};<BR><BR>Bubblesort((byte*)array, 5, 
            sizeof(array[0]),<BR>&amp;CCallbackTester::CompareInts);</TD></TR></TBODY></TABLE><BR>  如果使用微软的编译器,将会得到下面这个编译错误:<BR><BR>
      <TABLE borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 
      border=1>
        <TBODY>
        <TR>
          <TD>error C2664: 'Bubblesort' : cannot convert parameter 4 from 'int 
            (__stdcall CCallbackTester::*)(const unsigned char *,const unsigned 
            char *)' to 'int (__stdcall *)(const unsigned char *,const unsigned 
            char *)' There is no context in which this conversion is 
        possible</TD></TR></TBODY></TABLE><BR>  这是因为非静态成员函数有一个额外的参数:this指针,这将迫使你在成员函数前面加上static。当然,还有几种方法可以解决这个问题,但限于篇幅,就不再论述了 
      .</DIV></TD></TR></TBODY></TABLE><BR>
<DIV class=opt><A title=查看该分类中所有文章 
href="http://hi.baidu.com/kaixinlaohe/blog/category/vc++">类别:vc++</A> | <A 
title=将此文章添加到百度搜藏 
onclick="window.open('http://cang.baidu.com/do/add?it='+encodeURIComponent('帮你理解回调函数'+'_百度空间')+'&amp;iu='+encodeURIComponent(location.href)+'&amp;fr=sp#nw=1','_s','scrollbars=no,width=600,height=450,right=75,top=20,status=no,resizable=yes'); return false;" 
href="http://cang.baidu.com/do/add" target=_blank>添加到搜藏</A> | 浏览(<SPAN 
id=result></SPAN>) </DIV>
<DIV class=line></DIV>
<DIV id=in_reader>
<DIV class=tit>最近读者:</DIV>
<TABLE width="100%">
  <TBODY>
  <TR>
    <TD align=middle width="10%"><IMG height=55 
      src="帮你理解回调函数_大肚水杯.files/portraitn.jpg" width=55 border=0><BR>&nbsp;</TD>
    <TD align=left width="12%">
      <SCRIPT>document.write("<a href='http://passport.baidu.com/?login&tpl=sp&tpl_reg=sp&u="+myref+"' target='_self'>登录</a>后,您就出现在这里。");</SCRIPT>
    </TD>
    <TD class=user vAlign=bottom align=middle width="10%"><A 
      href="http://hi.baidu.com/sunyubo458" target=_blank><IMG 
      src="帮你理解回调函数_大肚水杯.files/1e1473756e7975626f343538f002.jpg" 
      border=0><BR>sunyubo458 </A></TD>
    <TD class=user vAlign=bottom align=middle width="10%"><A 
      href="http://hi.baidu.com/hu_amao" target=_blank><IMG 
      src="帮你理解回调函数_大肚水杯.files/b89268755f616d616f7802.jpg" border=0><BR>hu_amao 
      </A></TD>
    <TD class=user vAlign=bottom align=middle width="10%"><A 
      href="http://hi.baidu.com/xiaoh274" target=_blank><IMG 
      src="帮你理解回调函数_大肚水杯.files/b81e7869616f68323734bd00.jpg" 
      border=0><BR>xiaoh274 </A></TD>
    <TD class=user vAlign=bottom align=middle width="10%"><A 
      href="http://hi.baidu.com/jiangguiqing" target=_blank><IMG 
      src="帮你理解回调函数_大肚水杯.files/d4716a69616e675f6775695f71696e67f102.jpg" 
      border=0><BR>jiang_gui_qing </A></TD>
    <TD class=user vAlign=bottom align=middle width="10%"><A 
      href="http://hi.baidu.com/yanzimywife" target=_blank><IMG 
      src="帮你理解回调函数_大肚水杯.files/da6279616e7a696d79776966658900.jpg" 
      border=0><BR>yanzimywife </A></TD>
    <TD width="100%"></TD></TR></TBODY></TABLE></DIV>
<DIV class=line></DIV>
<SCRIPT language=JavaScript>
allkey=allkey+"fe010724b315d133c9955901_388f78cf7dcc143cf8dc61db_";
</SCRIPT>

<DIV id=in_comment><A name=comment></A>
<DIV class=tit>网友评论:</DIV>
<SCRIPT>
function writecmt(type,id,cmtname,cmturl,cmttime){
	var html1="";
	if(type==1){
		if(cmturl==""){
			html1="<a name='"+id+"'>"+cmtname+"</a> - <span class='date'>"+cmttime+"</span> ";
		}else{
			html1="<a name='"+id+"' href='"+cmturl+"' target='_blank' title='"+cmturl+"'>"+cmtname+"</a> - <span class='date'>"+cmttime+"</span> ";
		}
	}else{
		if(cmtname=="匿名网友"){
			if(cmturl==""){
				html1="<a name='"+id+"'>"+cmtname+"</a> - <span class='date'>"+cmttime+"</span> ";
			}else{
				html1="<a name='"+id+"' href='"+cmturl+"' target='_blank' title='"+cmturl+"'>"+cmtname+"</a> - <span class='date'>"+cmttime+"</span>";
			}
		}else{
			if(cmturl==""){
				html1="<div class='f14' style='display:inline'>网友:<a name='"+id+"'>"+cmtname+"</a> - <span class=\"date\">"+cmttime+"</span></div>";
			}else{
				html1="<div class='f14' style='display:inline'>网友:<a name='"+id+"' href='"+cmturl+"' target='_blank' title='"+cmturl+"'>"+cmtname+"</a> - <span class=\"date\">"+cmttime+"</span></div>";
			}
		}
	}
	document.write(html1);
}

</SCRIPT>

<DIV id=page></DIV></DIV>
<DIV id=in_send>
<FORM id=popFormSubmit name=form1 onsubmit="return checkcmtform()" 
action=/kaixinlaohe/commit method=post><INPUT type=hidden value=8 name=ct> 
<INPUT type=hidden value=1 name=cm> <INPUT type=hidden 
value=388f78cf7dcc143cf8dc61db name=spBlogID>
<SCRIPT language=JavaScript>
	document.write("<input type='hidden' name='spRefURL' value='"+window.location.href+"'>");
</SCRIPT>
 
<DIV class=tit>发表评论:</DIV>
<TABLE cellSpacing=5 cellPadding=0 width=620 border=0>
  <TBODY>
  <TR>
    <TD class=f14>姓 名:</TD>
    <TD><INPUT id=spBlogCmtor style="WIDTH: 220px" onfocus=hidErr(1); 
      tabIndex=1 maxLength=49 onchange="checkname('spBlogCmtor')" 
      name=spBlogCmtor>
      <SCRIPT>
document.write(" &nbsp;&nbsp; <a href='http://passport.baidu.com/?reg&tpl=sp&return_method=get&skip_ok=1&u=http://hi.baidu.com/sys/reg/' target='_blank'>注册</a>");
document.write(" | <a href='http://passport.baidu.com/?login&tpl=sp&tpl_reg=sp&u="+myref+"'>登录</a>");

</SCRIPT>
       
      <DIV id=nmerror style="DISPLAY: none">*姓名最长为50字节</DIV></TD></TR>
  <TR id=1_err style="DISPLAY: none">
    <TD>&nbsp;</TD>
    <TD>
      <DIV class=error id=1_err_con></DIV></TD></TR>
  <TR>
    <TD class=f14>网址或邮箱:</TD>
    <TD><INPUT id=spBlogCmtURL style="WIDTH: 360px" onfocus=hidErr(2); 
      tabIndex=2 maxLength=128 onchange="checkeandu('spBlogCmtURL')" 
      name=spBlogCmtURL> (选填)</TD>
    <SCRIPT>
G("spBlogCmtor").value="";
G("spBlogCmtURL").value="";
</SCRIPT>
  </TR>
  <TR id=2_err style="DISPLAY: none">
    <TD>&nbsp;</TD>
    <TD>
      <DIV class=error id=2_err_con></DIV></TD></TR>
  <TR>
    <TD class=f14 vAlign=top>内 容:</TD>
    <TD><TEXTAREA id=spBlogCmtText style="WIDTH: 520px; HEIGHT: 155px" onfocus=hidErr(3); tabIndex=3 name=spBlogCmtText></TEXTAREA>
      <SCRIPT>
G("spBlogCmtor").value=G("spBlogCmtor").defaultValue;
G("spBlogCmtText").value="";
</SCRIPT>
       </TD></TR>
  <TR id=3_err style="DISPLAY: none">
    <TD>&nbsp;</TD>
    <TD>
      <DIV class=error id=3_err_con></DIV></TD></TR>
  <TR id=vercode>
    <TD class=f14 vAlign=top>验证码:</TD>
    <TD vAlign=top><INPUT type=hidden 
      value=21720140DC3B8B76FDFC4A6F66CE10491A456EF5C9096D6B1C6953B63FB6FCFA15506AF54E8B32C1E465FC696DAE038494F9B5CC22030779D2B3A883E2C6513B 
      name=spVcode> <INPUT id=spVerifyKey tabIndex=4 maxLength=4 size=6 
      name=spVerifyKey autocomplete="off">请输入下图中的四位验证码,字母不区分大小写。<BR>
      <SCRIPT language=JavaScript>

			var imgsrc="http://post.baidu.com/cgi-bin/genimg?21720140DC3B8B76FDFC4A6F66CE10491A456EF5C9096D6B1C6953B63FB6FCFA15506AF54E8B32C1E465FC696DAE038494F9B5CC22030779D2B3A883E2C6513B";
			document.write("<img id='verifypic' src='"+imgsrc+"' width='120' height='40'>");

			function newverifypic(){
				document.getElementById("verifypic").src = imgsrc +"&t="+ Math.random();
			}
		</SCRIPT>
      <A title=看不清左边的字符 href="javascript:newverifypic();">看不清?</A> </TD></TR>
  <TR>
    <TD class=f14 vAlign=top>&nbsp;</TD>
    <TD class=f14 vAlign=top><INPUT id=btn_ok tabIndex=5 type=submit value=发表评论 name=btn_ok></TD></TR></TBODY></TABLE></FORM></DIV><BR></DIV>
<TABLE height=8 cellSpacing=0 cellPadding=0 width="100%" border=0>
  <TBODY>
  <TR>
    <TD class=modbl width=7>&nbsp;</TD>
    <TD class=modbc>&nbsp;</TD>
    <TD class=modbr width=7>&nbsp;</TD></TR></TBODY></TABLE></DIV></DIV></DIV></DIV>
<SCRIPT language=javascript>
<!--
var hstr="/kaixinlaohe/brwstat?key1=1";
document.write("<script src='"+hstr+"&key2="+allkey+"'><\/script>");
//-->
</SCRIPT>
<BR>
<CENTER>
<DIV id=ft>&copy;2007 Baidu</DIV></CENTER>
<SCRIPT>
if(document.getElementById("m_blog"))
{
	var imgarray = document.getElementById("m_blog").getElementsByTagName('img');
	var imgw = document.getElementById("m_blog").offsetWidth;
	imgw =imgw-40;
	for(var i=0; i<imgarray.length; i++){
	if(imgarray[i].className=="blogimg" && imgarray[i].width>=imgw) imgarray[i].width=imgw;
	}
}
</SCRIPT>
</CENTER><IMG style="DISPLAY: none" src=""> </BODY></HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -