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

函数指针

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

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

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. 禁止页面被复制和禁止右键,一段样式一段JS就行了,无需复杂设定!

    群里小伙伴经常问怎么禁止页面复制和右键,其实这个问题百度一下是很多资料的,我估计小伙伴都懒,所以这里统一回复下: 找到模板里面的</head>,在上面加如下代码就行了 <style ...

  2. hibernateHQL

    HQL 即Hibernate Query Language的缩写 hql和sql区别/异同 HQL                                                    ...

  3. JAVA笔记 -- this关键字

    this关键字 一. 基本作用 在当前方法内部,获得当前对象的引用.在引用中,调用方法不必使用this.method()这样的形式来说明,因为编译器会自动的添加. 必要情况: 为了将对象本身返回 ja ...

  4. centos 配置sentry+钉钉+邮件通知

    1.sentry官方推荐docker方式安装.使用docker-compose,最好是centos7 2.卸载旧版本 yum remove docker docker-common docker-se ...

  5. Linux下正确修改Docker镜像和容器的默认存储位置,亲测有效

    我们通过 yum 的方式安装完Docker环境后,它默认的存储位置是 /var/lib/docker,默认的 pid 存放位置是 /var/run/docker.pid. 如果仅仅是做测试,我们可能没 ...

  6. Android 网络加载通用Loading

    为了用户体验用好,App在网络请求时通常都会显示个进度显示圈圈,提示用户耐心等待,最脍炙人口的莫过于登录啦. 接下来我们就通过Dialog来实现,然后在BaseActivity,BaseFragmen ...

  7. [转]Eclipse插件开发之基础篇(2) 第一个Eclipse插件

    原文地址:http://www.cnblogs.com/liuzhuo/archive/2010/08/15/eclipse_plugin_1_1_1.html 在Eclipse中使用PDE(Plug ...

  8. Linux统计文件夹、文件数量的命令

    # 查看当前目录下的文件数量(不包含子目录中的文件) ls -l|grep "^-"| wc -l # 查看当前目录下的文件数量(包含子目录中的文件) 注意:R,代表子目录 ls ...

  9. docker介绍和安装(一)

    虚拟化简介 虚拟化(英语:Virtualization)是一种资源管理技术,是将计算机的各种实体资源,如服务器.网络.内存及存储等,予以抽象.转换后呈现出来,打破实体结构间的不可切割的障碍,使用户可以 ...

  10. vue调用高德地图:vue-amap

    前言:之前没有接触过页面调用地图的项目,某次面试,老板要求我用vue-amap调用高德地图,回家以后,我去网上查了一些案例和教程,看似很简单的引入调用,我却整整弄了一宿,还没弄出来!!!百般无奈之下, ...