C++回调,函数指针
想要理解回调机制,先要理解函数指针
函数指针
函数指针指向的是函数而非对象,和其他指针一样,函数指针指向某种特定的类型
函数的类型由他的返回类型和参数类型共同决定,与函数名无关,如:
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++回调,函数指针的更多相关文章
- Delphi 函数指针(三大好处:灵活,委托的本质,回调机制),还可把函数指针当参数传入
首先学习: 指向非对象(一般的)函数/过程的函数指针 Pascal 中的过程类型与C语言中的函数指针相似,为了统一说法,以下称函数指针.函数指针的声明只需要参数列表:如果是函数,再加个返回值.例如声明 ...
- 嵌入式&iOS:回调函数(C)与block(OC)回调对比
学了OC的block,再写C的回调函数有点别扭,对比下区别,回忆记录下. C的回调函数: callBack.h 1).定义一个回调函数的参数数量.类型. typedef void (*CallBack ...
- VC++ 回调函数及使用方法(转)
转载:http://blog.csdn.net/vsooda/article/details/7435801 转载:http://blog.csdn.net/lincyang/article/deta ...
- SetTimer 与 回调函数
在控制台应用程序中,SetTimer的函数原型为: UINT_PTR SetTimer( HWND hWnd, // handle to window UINT_PTR nIDEvent, // ti ...
- 理解和使用 JavaScript 中的回调函数
理解和使用 JavaScript 中的回调函数 标签: 回调函数指针js 2014-11-25 01:20 11506人阅读 评论(4) 收藏 举报 分类: JavaScript(4) 目录( ...
- C#将C++动态库的回调函数封装成事件
关于C#调用C++动态库的文章很多,调用动态库中回调函数的方法也不在少数.但大多数调用回调函数的方法依然保留了C++的语法特点. 比如有一段C++的回调函数代码,为了表达它的意思,我把注释也粘贴了进来 ...
- Delphi对象变成Windows控件的前世今生(关键是设置句柄和回调函数)goodx
----------------------------------------------------------------------第一步,准备工作:预定义一个全局Win控件变量,以及一个精简 ...
- 回调函数的应用误区4(c/s OK版本回调小程序)
VC++深入详解里面说得也挺好:回调函数的实现机制: 1)定义一个回调函数 2)“函数实现者”(回调函数所在的模块)在初始化的时候,将回调函数的函数指针注册给“调用者”. 3)当特定的事件或条件发生的 ...
- c++回调函数
dcc组件支持回调函数接口,当连接/断开连接对端时,调用传入的函数指针. A库和B库想做到不耦合,但是A库需要用到B库的某些函数,A库提供回调函数接口,在初始化的时候指定回调函数,降低耦合程度,每一个 ...
- C中的回调函数
C语言中应用回调函数的地方非常多,如Nginx中: struct ngx_command_s { ngx_str_t name; ngx_uint_t type; char *(*set)(ngx_c ...
随机推荐
- 禁止页面被复制和禁止右键,一段样式一段JS就行了,无需复杂设定!
群里小伙伴经常问怎么禁止页面复制和右键,其实这个问题百度一下是很多资料的,我估计小伙伴都懒,所以这里统一回复下: 找到模板里面的</head>,在上面加如下代码就行了 <style ...
- hibernateHQL
HQL 即Hibernate Query Language的缩写 hql和sql区别/异同 HQL ...
- JAVA笔记 -- this关键字
this关键字 一. 基本作用 在当前方法内部,获得当前对象的引用.在引用中,调用方法不必使用this.method()这样的形式来说明,因为编译器会自动的添加. 必要情况: 为了将对象本身返回 ja ...
- centos 配置sentry+钉钉+邮件通知
1.sentry官方推荐docker方式安装.使用docker-compose,最好是centos7 2.卸载旧版本 yum remove docker docker-common docker-se ...
- Linux下正确修改Docker镜像和容器的默认存储位置,亲测有效
我们通过 yum 的方式安装完Docker环境后,它默认的存储位置是 /var/lib/docker,默认的 pid 存放位置是 /var/run/docker.pid. 如果仅仅是做测试,我们可能没 ...
- Android 网络加载通用Loading
为了用户体验用好,App在网络请求时通常都会显示个进度显示圈圈,提示用户耐心等待,最脍炙人口的莫过于登录啦. 接下来我们就通过Dialog来实现,然后在BaseActivity,BaseFragmen ...
- [转]Eclipse插件开发之基础篇(2) 第一个Eclipse插件
原文地址:http://www.cnblogs.com/liuzhuo/archive/2010/08/15/eclipse_plugin_1_1_1.html 在Eclipse中使用PDE(Plug ...
- Linux统计文件夹、文件数量的命令
# 查看当前目录下的文件数量(不包含子目录中的文件) ls -l|grep "^-"| wc -l # 查看当前目录下的文件数量(包含子目录中的文件) 注意:R,代表子目录 ls ...
- docker介绍和安装(一)
虚拟化简介 虚拟化(英语:Virtualization)是一种资源管理技术,是将计算机的各种实体资源,如服务器.网络.内存及存储等,予以抽象.转换后呈现出来,打破实体结构间的不可切割的障碍,使用户可以 ...
- vue调用高德地图:vue-amap
前言:之前没有接触过页面调用地图的项目,某次面试,老板要求我用vue-amap调用高德地图,回家以后,我去网上查了一些案例和教程,看似很简单的引入调用,我却整整弄了一宿,还没弄出来!!!百般无奈之下, ...