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 ...
随机推荐
- DataGridView中实现点击单元格Cell动态添加自定义控件
场景 鼠标点击DataGridView的某个单元格时,此单元格添加一个自定义的控件,这里以 添加下拉框为例 效果 注: 博客主页: https://blog.csdn.net/badao_liuman ...
- Asp.net MVC 中的TempData对象的剖析
另一篇文章,也对TempData 做了很详细的介绍,链接地址:https://www.jianshu.com/p/eb7a301bc536 . MVC中的 TempData 可以在Controll ...
- 仅用StoryBoard布局实现按钮的均匀分布
今天在做登录界面时设计的需求是,登录和取消按钮左右对称均匀分布,按钮大小不变如图 屏幕宽度变化时按钮宽度不变,三个间距相等并且随屏幕变化而变化,简单的说就是按钮均匀分布,在网上查了一些资料,弄得比较乱 ...
- 《Python自动化测试九章经》
Python是当前非常流行的一门编程语言,它除了在人工智能.数据处理.Web开发.网络爬虫等领域得到广泛使用之外,他也非常适合软件测试人员使用,但是,对于刚入行的测试小白来说,并不知道学习Python ...
- Maven报错Please ensure you are using JDK 1.4 or above and not a JRE
在部署Jenkins项目时,用Jenkins调用服务器上脚本时,脚本在服务器上执行没有任何问题,但是在Jenkins上调用时就报错,提示mvn应该使用全路径执行,当加入jdk,jre,maven环境变 ...
- 挂载system.img并提取文件
今天提取线刷包的system.img出来,使用Mount命令挂载 $ sudo mount -t ext4 -o loop system.img /mnt mount: 文件系统类型错误.选项错误./ ...
- nginx代理ambassador出现426错误
现在ambassador文档啃得差不多了.进入实战阶段. 一开始,就偶遇426错误. 网络结构大致如下: 浏览器访问nginx, nginx代理到k8s内的ambassador, ambassador ...
- 02-webpack的基本配置-运行webpack
1安装webPack的方式 第一次全局安装 npm i webpack -g 第一次安装了之后以后就不需要在安装了 在项目根录中运行 npm i webpack --save-dev 安装到项目依赖中 ...
- Python __slots__
Python 类的特殊变量:__slots__ 使用 __slots__ 后,类中 __weakref__ 和 __dict__ 消失,同时阻止动态属性绑定 由于 __dict__ 记录着类中所有的属 ...
- <Tree> 298 250 366 199(高频) 98(高频)
298. Binary Tree Longest Consecutive Sequence 先序遍历,根左右.如果该节点的 value == 父节点value + 1, 则长度+1; 否则重置为1. ...