三、函数指针、回调函数 与 UE 委托
1. 函数指针
函数的二进制代码存放在内存四区(栈区、堆区、数据段、代码段)中的代码段,函数的地址是它在内存中的起始地址。如果把函数的地址作为参数传递给函数,就可以在函数中灵活的调用其它函数。
使用函数指针的三个步骤:
- 声明函数指针;
- 指向函数地址;
- 通过函数指针调用函数。
1.1 声明函数指针
声明普通指针时,必须提供指针的类型。
同样,声明函数指针时,也必须提供函数类型,即 返回值 和 参数列表 (函数名和形参名不是)。

相应的函数指针声明如下:

1.2 指向函数地址
int no = 001;
string msg = "8933";
// 函数指针声明 返回值 参数列表
int (*Pfunc) (int, string);
// 指向函数地址
Pfunc = CreateSession;
1.3 通过函数指针调用函数
#include<iostream>
using namespace std;
bool CreateResult(int id, string info)
{
cout << "id : " << id << ", " << info << endl;
return true;
}
// 返回值 函数名 参数列表 (形参类型 形参名称, ...)
int CreateSession(int no, string str)
{
cout << "no : " << no << "创建" << str << "完成" << endl;
int id = no;
string info = "创建成功";
// 函数指针声明 返回值 参数列表
bool (*Pfunc) (int, string);
// 指向函数地址
Pfunc = CreateResult;
// 调用函数
return Pfunc(id, info);
}
int main()
{
int no = 001;
string msg = "8933";
// 函数指针声明 返回值 参数列表
int (*Pfunc) (int, string);
// 指向函数地址
Pfunc = CreateSession;
// 调用函数
cout << Pfunc(no, msg) << endl;
return 0;
}

2. 回调函数
函数指针的应用,回调函数。
函数逻辑大体相同,但某处需要特定,
则可以使用函数指针做参数,等具体执行的时候传入特定的逻辑:回调函数。
#include<iostream>
using namespace std;
// 返回值 函数名 参数列表 (形参类型 形参名称, ...)
void CreateSession(int no, string str, void(*CreatePtr)(int no, string str))
{
cout << "no : " << no << "创建" << str << "准备" << endl;
// ~ 特定方式创建 ~
CreatePtr(no, str);
// ~ 特定方式创建 ~
cout << "no : " << no << "创建" << str << "完成" << endl;
}
// L 特定创建
void CreateByL(int no, string str)
{
cout << "L 特定创建";
cout << "no : " << no << "创建" << str << endl;
}
// M 特定创建
void CreateByM(int no, string str)
{
cout << "M 特定创建";
cout << "no : " << no << "创建" << str << endl;
}
int main()
{
int no = 001;
string msg = "8933";
CreateSession(no, msg, CreateByL);
CreateSession(no, msg, CreateByM);
return 0;
}
3. UE 委托
委托的基本类型:单播委托、多播委托、动态委托。
委托的使用:声明、绑定、调用。
3.1 单播委托
单播委托指只能绑定一个函数指针的委托。
可绑定 无返回值 或 有返回值 的函数。
//无返回值函数委托声明
DECLARE_DELEGATE(DelegateName); //无参
DECLARE_DELEGATE_OneParam(DelegateName, Param1Type); //1个参数
DECLARE_DELEGATE_XXXParams(DelegateName, Param1Type, ...); //多参
//有返回值函数委托声明
DECLARE_DELEGATE_RetVal(RetValType, DelegateName); //有返回值无参
DECLARE_DELEGATE_RetVal_OneParam(RetValType, DelegateName, Param1Type); //有返回值,1个参数
DECLARE_DELEGATE_RetVal_XXXParams(RetValType, DelegateName, Param1Type, ...); //有返回值,多参
单播声明
// 声明单播委托 DECLARE_DELEGATE(委托变量名)
DECLARE_DELEGATE(FTestDelegate);
class XXX_API AMyTestActor : public AActor
{
GENERATED_BODY()
public:
AExecuteDelegateActor();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
public:
// 当变量使用
// 单播委托不能声明BlueprintAssignable标识符。动态多播可以声明BlueprintAssignable
FTestDelegate TestDelegate;
}
单播绑定
| 函数 | 描述 |
|---|---|
| BindSP | 绑定SharedPtr指向对象的函数,即纯C++类的函数。因为纯C++类一般会使用TSharedPtr用于管理内存 |
| BindThreadSafeSP | 绑定线程安全的共享成员函数委托 |
| BindRaw | 纯C++类变量,可以用其绑定委托 |
| BindUFunction | 如果成员函数有UFUNCTION宏表示,可用此绑定委托 |
| BindUObject | 绑定继承UObject类的成员函数委托 |
| BindStatic | 绑定静态(全局)函数委托,BindStatic(&MyClass::StaticFunc); |
| BindLambda | 绑定一个Lambda函数 |
| BindWeakLambda | 绑定弱引用Lambda函数 |
| UnBind | 取消绑定委托 |
//绑定纯C++类函数到委托
TSharedRef<FMyClass> MyClassObj(new FMyClass());
TestDelegate.BindSP(MyClassObj, &FMyClass::XXXFunc);
//绑定UObject子类函数到委托
TestDelegate.BindUObject(this, &UMyUObjectClass::XXXFunc); //当前类内绑定
UMyUObjectClass* MyUObjectClassObj; //其它类内绑定
TestDelegate.BindUObject(MyUObjectClassObj, &UMyUObjectClass::XXXFunc);
单播调用
| 函数 | 说明 |
|---|---|
| IsBound | 检查是否绑定一个委托 |
| Execute | 不检查绑定而执行委托 |
| ExecuteIfBound | 当绑定一个委托时,执行调用 |
3.2 多播委托
多播委托可以绑定多个函数,但不能有返回值。
多播声明
//多播委托
DECLARE_MULTICAST_DELEGATE(DelegateName);
DECLARE_MULTICAST_DELEGATE_ONEPARAM(DelegateName, Param1Type);
DECLARE_MULTICAST_DELEGATE_XXXPARAMS(DelegateName, Param1Type,...);
//动态多播委托
DECLARE_DYNAMIC_MULTICAST_DELEGATE(DelegateName);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ONEPARAM(DelegateName, Param1Type, Param1Name);
多播绑定

多播调用
Broadcast调用后,会执行所有绑定的委托,但是委托的执行顺序尚未定义。
| 函数 | 说明 |
|---|---|
| Broadcast() | 执行所有绑定的委托 |
3.3 动态委托
动态委托有多播,也有单播。
可以序列化,其函数可以按命名查找,但执行速度比常规委托慢。
动态声明
//动态单播委托
DECLARE_DYNAMIC_DELEGATE(DelegateName);
DECLARE_DYNAMIC_DELEGATE_ONEPARAM(DelegateName, Param1Type);
DECLARE_DYNAMIC_DELEGATE_XXXPARAMS(DelegateName, Param1Type,...);
//动态多播委托,可以暴露给蓝图
DECLARE_DYNAMIC_MULTICAST_DELEGATE(DelegateName);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ONEPARAM(DelegateName, Param1Type, Param1Name);
暴露给蓝图
动态单播委托变量(属性)可以作为函数的参数,暴露给蓝图,在蓝图中动态绑定一个委托。
DECLARE_DYNAMIC_DELEGATE(FMyDynamicDelegate);
public:
//不能声明为蓝图实现函数,cpp文件必须有实现
UFUNCTION(BlueprintCallable)
void DynDelTestFunc(FMyDynamicDelegate MyDynamicDelegate)
{ //这里为方便说明,直接在声明处定义
//调用出入的动态委托
MyDynamicDelegate.ExecuteIfBound();
}
public:
FMyDynamicDelegate MyDynamicDelegate;
接着蓝图中调绑定委托:

动态多播委托也可以暴露给蓝图使用。
如果暴露给蓝图,需要给委托变量的 UPROPERTY 宏添加 BlueprintAssignable 标识符。
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ONEPARAM(FDelegateTest, class AActor*, MyActor);
UPROPERTY(BlueprintAssignable)
FDelegateTest TestDelegate;
动态绑定

动态调用

3.3 应用场景
- 单播委托: 当只需在C++ 中绑定和调用,且只有一个函数需要绑定委托时,可以使用单播委托。
- 多播委托: 绑定多个函数,但不能有返回值,其它功能与单播委托一样。
- 动态委托: 动态绑定,可以序列化,也就是说可在蓝图中使用。
- 动态多播委托: 蓝图中的事件调度器(EventDispatcher),可以用其在C++和蓝图中绑定和调用委托。

浙公网安备 33010602011771号