委托是一种可在C++对象上调用成员函数,并可以动态绑定到任意对象的成员函数,之后在该对象上调用函数。可以降低类之间的耦合度,在UE4中共支持三种类型的委托。
- 单播委托
- 多播委托
- 动态委托
- 单播
| 声明方式 | 描述 |
|---|---|
| DECLARE_DELEGATE(DelegateName) | 绑定void Function() 类型的函数 |
| DECLARE_DELEGATE_oneParam(DelegateName,Param1Type) | 绑定void Function(Param1)类型的函数 |
| DECLARE_DELEGATE_TowParams(DelegateName,Param1Type,Param2Type) | 绑定void Function(Param1,Param2)类型的函数 |
| DECLARE_DELEGATE_RetVal_TowParams(RetValType,DelegateName,Param1Type,Param2Type) | 绑定RetType Function(Param1,Param2)类型的函数 |
//实例代码 DECLARE_DELEGATE(FDelegateName1NoParam); DECLARE_DELEGATE_TwoParams(FDelegateName2,float,const FString&); DECLARE_DELEGATE_RetVal_TwoParams(int32,FDelegateName3,int,const FString&);
-
多播
多播相较于单播,宏的变化为:DECLARE_MULTICAST_DELEGATE....同时需要注意多播是没有返回值的。
| 声明方式 | 描述 |
|---|---|
| DECLARE_MULTICAST_DELEGATE(DelegateName) | 绑定void Function() 类型的函数 |
| DECLARE_MULTICAST_DELEGATE_TwoParams | 绑定void Function(Param1,Param2)类型的函数 |
// 实例代码 // 多播代理 // 多播代理上绑定的函数的执行顺序是不固定的 // 多播代理无返回值 DECLARE_MULTICAST_DELEGATE(FMultiDelegateNoParam); DECLARE_MULTICAST_DELEGATE_TwoParams(FMultiDelegate2,int32,const FString&)
- 动态单/多播
动态单/多播委托是一个可以进行序列化利用反射暴露给蓝图的代理,需要注意的是,如果需要传入参数,在代理声明的时候,类型后面需要定义变量名(因为需要暴露给蓝图,变量是需要显示调用的)。
| 声明方式 | 描述 |
|---|---|
| DECLARE_DYNAMIC_DELEGATE(DelegateName) | 绑定void Function() 类型的函数 |
| DECLARE_DYNAMIC_DELEGATE_RetVal_oneParam(RetVal,DelegateName,ParamType1,Param1Name) | 绑定RetValFunction(Param1)类型的函数 |
| DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(DelegateName,ParamType1,Param1Name,ParamType2,Param2Name) | 绑定void Function(Param1,Param2)类型的函数 |
// 动态代理 可以暴露给blueprint使用 // 动态单播 DECLARE_DYNAMIC_DELEGATE(FDynamicDelegate1); // 如果有参数 类型后需要定义变量名 因为可以暴露在BluePrints中 所以需要名字 DECLARE_DYNAMIC_DELEGATE_RetVal_TwoParams(int32,FDynamicDelegate2,int32,intVal,const FString&,str); // 动态多播 注意同样没有返回值 DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDynamicDelegate3,int32,val,const FString&,str);2. 绑定委托
- 单播
| API | 描述 |
|---|---|
| BindLambda(FunctorType&& Intype) | 绑定一个Lambda表达式 |
| BindRaw(UserClass* user,MembFunctor* Infunctor) | 绑定一个原生C++成员方法 |
| BindSp(TSharedPtr | 绑定一个共享指针 |
| BindSatic(StaticFunctionPtr* Infunctor) | 绑定一个静态函数 |
| BindSp(TSharedPtr | 绑定一个线程安全的共享指针 |
| BindUFunction(UserClass*user,const FName& InFunctionName) | 绑定一个被UFUNCTION()宏修饰的函数 |
| BindUObject(UserClass*user,MembFunctor* Infunctor) | 绑定一个继承自UObejct类的成员方法 |
示例代码:
void ADelegateActor::SingleDelegateTest()
{
DelegateInstance1.BindUObject(this,&ADelegateActor::DelegateFunction);
DelegateInstance1.BindLambda([this](int32 val,const FString& str)->int32
{
return 0;
});
// 绑定原生C++类
FTestObject objectInstance;
DelegateInstance1.BindRaw(&objectInstance,&FTestObject::delegateFunc);
// 绑定共享指针,一般创建纯C++ 用TSharedPtr用于管理线程
TSharedPtr sharedPtr1 = MakeShareable(new FTestObject);
DelegateInstance1.BindSP(sharedPtr1.ToSharedRef(),&FTestObject::delegateFunc);
// 绑定静态函数
DelegateInstance1.BindStatic(&FTestObject::delegateFunc2);
// 绑定一个安全线程的共享指针
TSharedPtr sharedPtr2 = MakeShareable(new FTestObject);
DelegateInstance1.BindThreadSafeSP(sharedPtr2.ToSharedRef(),&FTestObject::delegateFunc);
// 绑定一个方式,通过Name查找,用到了反射。同时这个函数需要加上UFUNCTION()宏
// UFUNCTION()
// int32 delegateFunc3(int32 val,const FString& str){return 0;}
DelegateInstance1.BindUFunction(this,FName("delegateFunc3"));
}
- 多播
多播绑定函数和单播不同的地方为,它可以绑定多个函数对象,用的函数是Add+BindTargetName,需要注意的是多播没有返回值。
示例代码:
MultiDelegateInstance1.AddUObject(this,&ADelegateActor::MultiDelegateFunc);
MultiDelegateInstance1.AddLambda([](int val,const FString& str){});
.........
- 动态单播/多播
示例代码:
UFUNCTION()
void MultiDelegateFunc(int32 val,const FString& str){}
UFUNCTION()
int32 delegateFunc3(int32 val,const FString& str){return 0;}
// 动态单播
DynamicSigledelegateRef.BindDynamic(this,&ADelegateActor::delegateFunc3);
// 动态多播
// 注意动态多播绑定的方法一定要有UFUNCTION宏,因为需要用到反射的功能
dynamultiDelegateRef.AddDynamic(this,&ADelegateActor::MultiDelegateFunc);
3. 调用委托
- 单播
通过Execute和ExecuteIfBound进行调用,区别是ExecuteIfBound调用时会判断有没有进行了绑定再进行调用,但是返回值是bool。所以用ExecuteIfBound调用绑定的函数对象必须没有返回值。
示例代码:
if(DelegateInstance1.IsBound())
{
DelegateInstance1.Execute(20,TEXT("Unreal"));
}
//DelegateInstance1.ExecuteIfBound(); 返回值为bool
//解绑
DelegateInstance1.Unbind();
-
多播
多播通过BoardCast()可以通知调用所有绑定的函数对象。
示例代码:
MultiDelegateInstance1.Broadcast(10,"Unreal");
-
动态单播/多播
- 在C++中进行调用
// 动态单播 DynamicSigledelegateRef.BindDynamic(this,&ADelegateActor::delegateFunc3); DynamicSigledelegateRef.Execute(10,"Unreal"); // 动态多播 // 注意动态多播绑定的方法一定要有UFUNCTION宏,因为需要用到反射的功能 dynamultiDelegateRef.AddDynamic(this,&ADelegateActor::MultiDelegateFunc); dynamultiDelegateRef.Broadcast(10,"Unreal");
-
在蓝图中进行调用
单播在蓝图中调用需要利用UFUNCTION()宏进行修饰,并将自己作为形参传入函数。
UFUNCTION(BlueprintCallable) void DynamicSingleDelegateTest(FDynamicDelegate2 delegateRef);
在蓝图中调用:
多播在蓝图中调用可以直接使用UPROPERTY()宏中的BlueprintAssignable的属性修饰符修饰,注意:该修饰符只能用于动态多播委托
UPROPERTY(BlueprintAssignable) FDynamicDelegate3 dynamicMultiDelegate;
在蓝图中调用:
4. C++实现UE4委托 借用侯捷老先生的话,知道一个东西,却不明白它的原理,不高明!,所以为了能更好的理解UE4委托,于是模仿UE4创建代理绑定以及调用的方式,自己用C++写了一套很简单的委托。
- 实现单播无返回值无参数委托
class FDelegateDefault{
public:
FDelegateDefault():functor(nullptr){};
template
void BindRaw(ObjectType* InObejct,void(ObjectType::*InFunc)(void)){
functor = std::bind(InFunc,InObejct);
}
void BindLambda(function Infunctor){
functor = std::move(Infunctor);
}
void Execute(){
functor();
}
bool ExecuteIfBound(){
if(IsBound()){
Execute();
return true;
}
return false;
}
bool IsBound(){
return functor?true:false;
}
private:
functionfunctor;
};
class testObject{
public:
void testFunc(){
cout<<"test default delegate"<
- 实现单播多参有返回值的委托
template
class FDelegatebase{
public:
virtual retType Execute(paramTypes...params)=0;
};
template
class FDelegateObject:public FDelegatebase{
public:
FDelegateObject():Functor(nullptr),Object(nullptr){};
FDelegateObject(objectType* InObejct,retType(objectType::*InFunctor)(paramTypes...)):Object(InObejct),Functor(InFunctor){};
virtual retType Execute(paramTypes...params)override{
if(Functor&&Object){
return (Object->*Functor)(params...);
}
}
private:
objectType* Object;
retType(objectType::*Functor)(paramTypes...);
};
template
class FDelegateLambda:public FDelegatebase{
public:
FDelegateLambda():functor(nullptr){};
FDelegateLambda(function InFunctor){
functor = std::move(InFunctor);
}
virtual retType Execute(paramTypes...params)override{
if(functor){
return functor(params...);
}
}
private:
functionfunctor;
};
template
class FDelegateParams{
public:
FDelegateParams():delegatePtr(nullptr){}
~FDelegateParams(){
if(delegatePtr){
delete delegatePtr;
delegatePtr = nullptr;
}
}
template
void BindRaw(objectType* InObejct,retType(objectType::*InFunc)(paramTypes...)){
prepareforBind();
delegatePtr = new FDelegateObject(InObejct,InFunc);
}
void BindLambda(function InFunc){
prepareforBind();
delegatePtr = new FDelegateLambda(InFunc);
}
retType Execute(paramTypes...params){
if(delegatePtr){
return delegatePtr->Execute(params...);
}
}
bool ExecuteIfBound(paramTypes...params){
if(delegatePtr){
Execute(params...);
}
}
bool IsBound(){
return delegatePtr?true:false;
}
protected:
void prepareforBind(){
if(delegatePtr){
delete delegatePtr;
delegatePtr = nullptr;
}
}
private:
FDelegatebase* delegatePtr;
};
class testObject{
public:
const char* testFunc2(int val,string str){
cout<<"val:"<FDelegateName;
int main(){
testObject obj;
DECLARE_DELEGATE_RetVal_Params(Delegate,const char*,int,string);
Delegate.BindRaw(&obj,&testObject::testFunc2);
Delegate.Execute(10,"Unreal");
Delegate.BindLambda([](int val,string str)->const char*{
cout<<"Bind Lambda"<
多播的实现,大同小异。有兴趣的可以看我之前C++委托中实现多播委托的方式。这里不予讨论。C++委托_WLSTLA-CSDN博客



