C++回调,函数指针

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

函数指针#

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

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

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

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

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

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

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

使用函数指针#

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

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

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

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

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

Copy
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; //正确:函数和指针的类型精确匹配

重载函数指针#

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

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

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

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

函数指针形参#

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

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

Copy
//第三个形参是函数类型,会自动转换成指向函数的指针 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 &));

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

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

typedef和decltype#

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

Copy
//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:

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

返回指向函数的指针#

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

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

我们使用类型别名将F定义成函数类型,将PF定义成指向函数类型的指针
和函数类型的形参不一样,但会类型不会自动地转换成指针,必须显示地将返回类型定为指针

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

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

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

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

回调函数#

Callback方式(C的回调函数)#

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

Copy
//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完成时需要触发一个通知外面的事件:

Copy
//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实现,代码如下:

Copy
//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还是很麻烦的,我不会写,鸽了

posted @   KelvinVS  阅读(858)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示
CONTENTS