想要理解回调机制,先要理解函数指针

函数指针

函数指针指向的是函数而非对象,和其他指针一样,函数指针指向某种特定的类型

函数的类型由他的返回类型和参数类型共同决定,与函数名无关,如:

bool lengthCompare(const string &, const string &);

该函数的类型是bool (const string &, const string &),要声明一个可以指向该函数的指针,只需要用指针替换函数名:

//pf指向一个函数,该函数的参数是两个const string的引用,返回值是bool类型
bool (*pf)(const string &, const string &);

pf是指针,右侧是形参列表,表示pf指向的是函数;左侧返回类型是布尔值,因此pf就是一个指向函数的指针,其中函数的参数是两个const string的引用,返回值是bool类型

(*pf) 少了括号,pf就是一个 返回值为bool指针的函数

使用函数指针

当我们把函数名作为一个值使用时,该函数自动转换成指针:

bool lengthCompare(const string &, const string &);
bool (*pf)(const string &, const string &); pf = lengthCompare; //pf指向名为lengthCompare的函数
pf = &lengthCompare; //等价的赋值语句,&是可选的

直接使用指向函数的指针调用该函数:

bool b1 = pr("hello","goodbye");			//调用lengthCompare函数
bool b2 = (*pf)("hello","goodbye"); //等价调用
bool b3 = lengthCompare("hello","goodbye"); //等价调用

函数的类型由他的返回类型和参数类型共同决定,指向不同函数类型的指针间不存在转换规则,但可以为指针赋一个nullptr或者值为0的整形常量表达式,表示指针没有指向任何一个函数:

bool (*pf)(const string &, const string &);
string::size_type sumLength(const string&, const string&);
bool cstringCompare(const char*, const char*);
bool lengthCompare(const string &, const string &); pf = 0; //正确:pf不指向任何函数
pf = sumLength; //错误:返回类型不匹配
pf = cstringCompare; //错误:形参类型不匹配
pf = lengthCompare; //正确:函数和指针的类型精确匹配

重载函数指针

当我们使用重载函数时,上下文必须清晰地界定到底该选用哪个函数,如果定义了指向重载函数的指针:

void ff(int*);
void ff(unsigned int); void (*pf1)(unsigned int) = ff; //pf1指向ff(unsigned int);

编译器通过指针类型决定选用哪个函数,指针类型必须与重载函数其中之一精确匹配

void (*pf2)(int) = ff;	  //错误:没有任何一个ff与该形参列表匹配
double (*pf3)(int*) = ff; //错误:ff和pf3的返回类型不匹配

函数指针形参

和数组类似,不能定一函数类型的形参,但形参可以是指向函数的指针

此时形参看起来像函数类型,实际上被当成指针使用

//第三个形参是函数类型,会自动转换成指向函数的指针
void useBigger(const string &s1, const string &s2,
bool pf(const string &, const string &));//看起来像函数类型,实际上被当成指针使用 //等价的声明:显示地将形参定义成指向函数的指针
void useBigger(const string &s1, const string &s2,
bool (*pf)(const string &, const string &));

我们也可以直接把函数作为实参使用,此时它会自动转换成指针:

//自动将函数lengthCompare转换成指向该函数的指针
useBigger(s1,s2,lengthCompare);

typedef和decltype

简化使用了函数指针的代码

//Func和Func2是函数类型
typedef bool Func(const string&, const string&);
typedef decltype(lengthCompare) Func2; //等价的类型 //FuncP和FuncP2是指向函数的指针
typedef bool (*FuncP)(const string&, const string&);
typedef decltype(lengthCompare) *FuncP2; //等价的类型

我们使用typedef定义自己的类型,Func和Func2是函数类型,而FuncP和FuncP2是指针类型

decltype返回函数类型,此时不会将函数类型自动转换成指针类型,因为decltype的结果是函数类型,所以只有在结果前面加上 * 才能得到指针,可以使用如下形式重新声明useBigger:

// useBigger的等价声明,其中使用了类型别名
void useBigger(const string&, const string&, Func);//自动将Func表示的函数类型转换成指针
void useBigger(const string&, const string&, FuncP2);

返回指向函数的指针

和数组类似,虽然不能返回一个函数,但是能返回指向函数类型的指针,我们也必须把返回类型写成指针形式,因为编译器不会自动地将函数返回类型当成对应的指针类型处理:

using F = int(int*, int);	  //F是函数类型,不是指针
using PF = int(*)(int*, int); //PF是指针类型

我们使用类型别名将F定义成函数类型,将PF定义成指向函数类型的指针

和函数类型的形参不一样,但会类型不会自动地转换成指针,必须显示地将返回类型定为指针

PF f1(int);	 //正确:PF是指向函数的指针,f1返回指向函数的指针
F f1(int); //错误:F是函数类型,f1不能返回一个函数
F *f1(int); //正确:显示地制定返回类型是指向函数的指针

我们也能用如下形式直接声明f1:

int (*f1(int)(int, int));

按照由内向外地顺序阅读:f1有形参列表,所以f1是个函数;f1前有*,所以f1返回一个指针;进一步观察发现,指针的类型本身也包含形参列表,因此指针指向函数,该函数返回的类型是int

回调函数

Callback方式(C的回调函数)

Callback的本质是设置一个函数指针进去,然后在需要需要触发某个事件时调用该方法,比如Windows的窗口消息处理函数就是这种类型

//callbackTest.c
//1.定义函数onHeight(回调函数)
//@onHeight 函数名
//@height 参数
//@contex 上下文
void onHeight(double height, void* contex)
{
sprint("current height is %lf",height);
} //2.定义onHeight函数的原型
//@CallbackFun 指向函数的指针类型
//@height 回调参数,当有多个参数时,可以定义一个结构体
//@contex 回调上下文,在C中一般传入nullptr,在C++中可传入对象指针
typedef void (*CallbackFun)(double height, void* contex); //3.定义注册回调函数
//@registHeightCallback 注册函数名
//@callback 回调函数原型
//@contex 回调上下文
void registHeightCallback(CallbackFun callback, void* contex)
{
double h=100;
callback(h,nullptr);
} //4.main函数
void main()
{
//注册onHeight函数,即通过registHeightCallback的参数将onHeight函数指针
//传入给registHeightCallback函数,在registHeightCallback函数中调用
//callback就相当于调用onHeight函数。
registHeightCallback(onHeight,nullptr);
}

我们就可以用来实现一个小需求:Download完成时需要触发一个通知外面的事件:

//Callback方式

typedef void (__stdcall *DownloadCallback)(const char *pURL,bool OK);

void DownLoadFile(const char *pURL,DownloadCallback callback)
{
std::cout<<"downloading..."<<pURL<<""<<std::endl;
callback(pURL,true);
} void __stdcall onDownloadFinished(const char* pURL,bool bOK)
{
std::cout<<"onDownloadFinished..."<<pURL<<" status:"<<bOK<<std::endl;
} void main()
{
DownLoadFile("http://wwww.baidu.com",onDownloadFinished);
system("pause");
}

Sink方式

Sink的本质是按对方的需求实现一个C++接口,然后把实现的接口设置给对方,对方需要触发事件时调用该接口, COM中连接点就是基于这种方式,上面下载文件的需求,如果用Sink实现,代码如下:

//sink方式

class IDownloadSink
{
public:
virtual void OnDownloadFinished(const char *pURL,bool bOK) = 0;
}; class CMyDownloader
{
public:
CMyDownloader (IDownloadSink *pSink)
:m_pSink(pSink)
{ } void DownloadFile(const char* pURL)
{
std::cout<<"downloading..."<<pURL<<""<<std::endl;
if(m_pSink!=NULL)
{
m_pSink->OnDownloadFinished(pURL,true);
}
} private:
IDownloadSink *m_pSink;
}; class CMyFile:public IDownloadSink
{
public:
void download()
{
CMyDownloader downloader(this);
downloader.DownloadFile("www.baidu.com");
} virtual void OnDownloadFinished(const char *pURL,bool bOK)
{
std::cout<<"onDownloadFinished..."<<pURL<<" status:"<<bOK<<std::endl;
}
}; void main()
{
CMyFile *file = new CMyFile();
file->download();
system("pause");
}

Delegate方式

Delegate的本质是设置成员函数指针给对方,然后让对方在需要触发事件时调用,C#中用Delegate的方式实现Event,C++中因为语言本身的关系,要实现Delegate还是很麻烦的,我不会写,鸽了

C++回调,函数指针的更多相关文章

  1. Delphi 函数指针(三大好处:灵活,委托的本质,回调机制),还可把函数指针当参数传入

    首先学习: 指向非对象(一般的)函数/过程的函数指针 Pascal 中的过程类型与C语言中的函数指针相似,为了统一说法,以下称函数指针.函数指针的声明只需要参数列表:如果是函数,再加个返回值.例如声明 ...

  2. 嵌入式&iOS:回调函数(C)与block(OC)回调对比

    学了OC的block,再写C的回调函数有点别扭,对比下区别,回忆记录下. C的回调函数: callBack.h 1).定义一个回调函数的参数数量.类型. typedef void (*CallBack ...

  3. VC++ 回调函数及使用方法(转)

    转载:http://blog.csdn.net/vsooda/article/details/7435801 转载:http://blog.csdn.net/lincyang/article/deta ...

  4. SetTimer 与 回调函数

    在控制台应用程序中,SetTimer的函数原型为: UINT_PTR SetTimer( HWND hWnd, // handle to window UINT_PTR nIDEvent, // ti ...

  5. 理解和使用 JavaScript 中的回调函数

    理解和使用 JavaScript 中的回调函数 标签: 回调函数指针js 2014-11-25 01:20 11506人阅读 评论(4) 收藏 举报  分类: JavaScript(4)    目录( ...

  6. C#将C++动态库的回调函数封装成事件

    关于C#调用C++动态库的文章很多,调用动态库中回调函数的方法也不在少数.但大多数调用回调函数的方法依然保留了C++的语法特点. 比如有一段C++的回调函数代码,为了表达它的意思,我把注释也粘贴了进来 ...

  7. Delphi对象变成Windows控件的前世今生(关键是设置句柄和回调函数)goodx

    ----------------------------------------------------------------------第一步,准备工作:预定义一个全局Win控件变量,以及一个精简 ...

  8. 回调函数的应用误区4(c/s OK版本回调小程序)

    VC++深入详解里面说得也挺好:回调函数的实现机制: 1)定义一个回调函数 2)“函数实现者”(回调函数所在的模块)在初始化的时候,将回调函数的函数指针注册给“调用者”. 3)当特定的事件或条件发生的 ...

  9. c++回调函数

    dcc组件支持回调函数接口,当连接/断开连接对端时,调用传入的函数指针. A库和B库想做到不耦合,但是A库需要用到B库的某些函数,A库提供回调函数接口,在初始化的时候指定回调函数,降低耦合程度,每一个 ...

  10. C中的回调函数

    C语言中应用回调函数的地方非常多,如Nginx中: struct ngx_command_s { ngx_str_t name; ngx_uint_t type; char *(*set)(ngx_c ...

随机推荐

  1. python基础(12):函数(二)

    1. 函数参数 之前我们说过了传参,如果我们需要给⼀个函数传参,⽽参数⼜是不确定的,或者我给⼀个函数传很多参数,我的形参就要写很多,很⿇烦,怎么办呢,我们可以考虑使⽤动态参数. 形参的第三种: 动态参 ...

  2. struts图片上传

    文件上传:三种上传方案1.上传到tomcat服务器 上传图片的存放位置与tomcat服务器的耦合度太高2.上传到指定文件目录,添加服务器与真实目录的映射关系,从而解耦上传文件与tomcat的关系文件服 ...

  3. python3之猜数字游戏

    猜数字小游戏 # coding:utf-8 import random #利用random生成一个1-10的随机数 luckeyNum = random.randint(1,10) #限定猜的次数 l ...

  4. java基础第十六篇之多线程

    1:线程的概念 进程(任务):一个正在运行的程序 进程的调度:CPU来决定什么时候该运行哪个进程 (时间片轮流法) 线程在一个应用程序中,同时,有多个不同的执行路径,是进程中的实际运作单位. 好处是提 ...

  5. 爬取动态html网页,requests+execjs

    请求地址:https://g.hongshu.com/content/99269/15382723.html 网页内容为动态执行js所得 1.直接浏览器模拟 不用考虑页面的业务逻辑什么的,直接得到结果 ...

  6. 【hexo+github搭建myblog】bash: npm: command not found 问题,疑似解决!关键词:NPM全局安装路径

    情况:打算用hexo+github搭建个人博客 1. hexo搭建,参考博文如下,非常感谢: Hexo+Github博客搭建完全教程 hexo从零开始到搭建完整 问题: 在最基本的安装步骤 (参考链接 ...

  7. layui常用的验证

    var LayVerifyExtend = { notnullNonnegativeInteger: function (value, item) { //value:表单的值.item:表单的DOM ...

  8. RV32FDQ/RV64RDQ指令集(1)

    Risc-V架构定义了可选的单精度浮点指令(F扩展指令集)和双精度浮点指令(D扩展指令集),以及四精度浮点指令集(Q扩展指令集).Risc-V架构规定:处理器可以选择只实现F扩展指令子集而不支持D扩展 ...

  9. iOS中的GCD线程

    一.什么是GCD      全称是Grand Central Dispatch ,纯C语言编写,提供非常多强大的函数,是目前苹果官网推荐的多线程开发方法,NSOperation 便是基于GCD的封装 ...

  10. emacs require和provide

    Emacs的默认配置文件是.emacs,Emacs启动时会读取并执行.emacs中的LISP代码,用户使用.emacs达到自己的Emacs初始化配置目的. 不过单独使用.emacs有一个问题,因为时间 ...