代理:

  • 代理可以帮助我们解决一对一或是一对多的任务分配工作。主要可以帮助我们解决通知问题。我们可以通过代理完成调用某一个对象的一个函数,而不直接持有该对象的任何指针。
  • 代理就是为你跑腿送信的,你可以不用关心给送信的目标人具体是谁,只要按照约定好的信件格式进行送信即可
  • 更简单理解,想去调用某个函数,但并不是直接去调用,而是通过另一个入口去调用(代理)

分类:

  • 单播代理 只能进行通知一个人
  • 多播代理 可以进行多人通知
  • 动态代理 可以被序列化(这体现在于蓝图进行交互,C++中可以将通知事件进行蓝图广播)

单播代理:

通过宏进行构建,单播代理只能绑定一个通知对象,无法进行多个对象通知、

单播代理分为有返回值与无返回值两种

代理可使用声明宏

函数签名

声明宏

void Function()

DECLARE_DELEGATE(DelegateName)

void Function(Param1)

DECLARE_DELEGATE_OneParam(DelegateName, Param1Type)

void Function(Param1, Param2)

DECLARE_DELEGATE_TwoParams(DelegateName, Param1Type, Param2Type)

void Function(Param1, Param2, ...)

DECLARE_DELEGATE_<Num>Params(DelegateName, Param1Type, Param2Type, ...)

<RetValType> Function()

DECLARE_DELEGATE_RetVal(RetValType, DelegateName)

<RetValType> Function(Param1)

DECLARE_DELEGATE_RetVal_OneParam(RetValType, DelegateName, Param1Type)

<RetValType> Function(Param1, Param2)

DECLARE_DELEGATE_RetVal_TwoParams(RetValType, DelegateName, Param1Type, Param2Type)

<RetValType> Function(Param1, Param2, ...)

DECLARE_DELEGATE_RetVal_<Num>Params(RetValType, DelegateName, Param1Type, Param2Type, ...)

常用绑定函数:

  • BindUObject 绑定UObject类型对象成员函数的代理
  • BindSP 绑定基于共享引用的成员函数代理
  • BindRaw 绑定原始自定义对象成员函数的代理,操作调用需要注意执行需要检查
  • IsBound BindStatic 绑定全局函数成为代理
  • UnBind 解除绑定代理关系

注意:绑定中传递的对象类型必须和函数指针所属类的类型相同否则绑定会报错

调用执行:

  • 为了保证调用的安全性,执行Execute函数之前需要检查是否存在有效绑定使用函数、
  • IsBound Execute 调用代理通知,不安全,需要注意
  • ExecuteIfBound 调用代理通知,安全,但是有返回类型的回调函数无法使用此函数执行回调
  • IsBound 检查当前是否存在有效代理绑定

构建步骤:

  • 通过宏进行声明代理对象类型(根据回调函数选择不同的宏)
  • 使用代理类型进行构建代理对象
  • 绑定回调对象,和操作函数
  • 执行代理对象回调
// Actor1.h
// 头文件下
DECLARE_DELEGATE(DelegateOne)
DECLARE_DELEGATE_RetVal_OneParam(int32 ,DelegateTwo, int32)
// 变量声明
class AActor2* ac2; DelegateOne DegOne;
DelegateTwo DegTwo;
// Actor1.cpp
// 这里将代码写在了BeginPlay中,方便测试
ac2 = GetWorld()->SpawnActor<AActor2>(AActor2::StaticClass()); // 绑定无参无返回值单播代理
DegOne.BindUObject(ac2, &AActor2::CallBackNone);
DegOne.ExecuteIfBound();
// 绑定有参有返回值单播代理
DegTwo.BindUObject(ac2, &AActor2::CallBackRes);
int32 num = 0;
num = DegTwo.Execute(100);
UKismetSystemLibrary::PrintString(this, FString::Printf(TEXT("%d"),num)); /////////////////////////////////////////////////////// // Actor2.h
//声明两个被用来绑定的的函数
void CallBackNone();
int32 CallBackRes(int32 num);
// Actor2.cpp
void AActor2::CallBackNone()
{
UKismetSystemLibrary::PrintString(this, TEXT("无返回值无参数函数调用!"));
} int32 AActor2::CallBackRes(int32 num)
{
UKismetSystemLibrary::PrintString(this, TEXT("有返回值有参数函数调用!"));
return num;
}

测试结果:

 多播代理:

无法构建具有返回值的多播代理——多播代理无返回值

DECLARE_MULTICAST_DELEGATE[_Const, _RetVal, _etc.] (DelegateName)

多播代理绑定函数 

函数

说明

"Add()"

将函数委托添加到该多播委托的调用列表中。

"AddStatic()"

添加原始C++指针全局函数委托。

"AddRaw()"

添加原始C++指针委托。原始指针不使用任何类型的引用,因此如果从委托下面删除了对象,则调用此函数可能不安全。调用Execute()时请小心!

"AddSP()"

添加基于共享指针的(快速、非线程安全)成员函数委托。共享指针委托保留对对象的弱引用。

"AddUObject()"

添加基于UObject的成员函数委托。UObject委托保留对对象的弱引用。

"Remove()"

从该多播委托的调用列表中删除函数(性能为O(N))。请注意,委托的顺序可能不会被保留!

"RemoveAll()"

从该多播委托的调用列表中删除绑定到指定UserObject的所有函数。请注意,委托的顺序可能不会被保留!

广博: 调用函数Broadcast,但是调用不保证执行顺序的正确性

构建步骤:

  • 使用宏构建代理类型
  • 使用代理类型构建多播代理对象
  • 添加绑定代理
  • 执行调用

多播代理执行使用的是 Broadcast() 进行执行函数

动态代理:

  • 允许被序列化的数据结构,这将使得代理可以被数据化提供给蓝图进行使用,达到在CPP中调用代理广播,事件通知到蓝图中。
  • 动态代理和普通代理基本相同,分为单向和多向,动态代理无法使用带有返回值的函数进行构建(动态单播除外,并且单播无法在蓝图中绑定无法使用宏BlueprintAssignable修饰)
  • UE中的大部分通知事件均使用动态代理(方便蓝图操作),如碰撞通知

动态单播代理:

  • DECLARE_DYNAMIC_DELEGATE[_Const, _RetVal, etc.]( DelegateName )

动态多播代理:

  • DECLARE_DYNAMIC_MULTICAST_DELEGATE[_Const, _RetVal, etc.]( DelegateName )

操作函数:

  • BindDynamic( UserObject, FuncName ) 在动态代理上调用BindDynamic()的辅助宏。
  • AddDynamic( UserObject, FuncName ) 在动态多播代理上调用AddDynamic()的辅助宏。
  • RemoveDynamic( UserObject, FuncName ) 在动态多播代理上调用RemoveDynamic()的辅助宏。

与单播多播区别:

  • 动态代理构建类型名称需要用 F 开头(动态代理实现机制构建了类)
  • 动态代理对象类型可以使用UPROPERTY标记,其他代理均无法使用(不加编译可过,调用出错)
  • 动态代理绑定对象的函数需要使用UFUNCTION进行描述(因为需要跟随代理被序列化)

构建:

// Actor1.h
DECLARE_DYNAMIC_DELEGATE(FDelegateTree); // 注意分号
// 变量定义
class AActor2* ac2;
FDelegateTree DegTree;
// Actor1.cpp
ac2 = GetWorld()->SpawnActor<AActor2>(AActor2::StaticClass()); DegTree.BindDynamic(ac2, &AActor2::CallBackNone);
if (DegTree.IsBound()) {
DegTree.ExecuteIfBound();
} //////////////////////////////////////////////////////////
// Actor2.h
UFUNCTION()
void CallBackNone();
// Actor2.cpp
void AActor2::CallBackNone()
{
UKismetSystemLibrary::PrintString(this, TEXT("无返回值无参数函数调用!"));
}

测试结果:

 动态代理用于蓝图:

在构建动态代理提供蓝图使用时,需要在代理上增加标记宏UPROPERTY(BlueprintAssignable)

UEC++ 代理/委托的更多相关文章

  1. UEC 利用代理/委托写一个生命组件

    首先基于ActorComponent创建一个组件 HealthComponent,将需要的变量与函数创建 #include "CoreMinimal.h" #include &qu ...

  2. 代理委托和block

    delegate 委托是协议的一种,顾名思义,就是委托他人帮自己去做事.委托是给一个对象提供机会对另一个对象中的变化做出反应或者影响另一个对象的行为.其基本思想是:两个对象协同解决问题,并且打算在广泛 ...

  3. 【C#】事件(Event)和代理/委托(Delegate)

    代理(Delegate)的例子 delegate void MyDelegate(string str,int index); // 声明代理 class Test { public static v ...

  4. JavaScript中的事件代理/委托

    事件委托在JS高级程序设计中的定义为"利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件" 如何理解上面的这句话呢,在网上,大牛们一般都使用收快递这个例子来解释的, ...

  5. C# 代理/委托 Delegate

    本文转载自努力,努力,努力 1. 委托的定义:委托是函数的封装,它代表一"类"函数.他们都符合一定的签名:拥有相同的参数列表,返回值类型.同时,委托也可以看成是对函数的抽象,是函数 ...

  6. iOS delegate, 代理/委托与协议.

    之前知知道iOS协议怎么写, 以为真的跟特么java接口一样, 后来发现完全不是. 首先, 说说应用场景, 就是当你要用一个程序类, 或者说逻辑类, 去控制一个storyboard里面的label, ...

  7. js中事件代理(委托)

    var oul = document.getElementById(‘uli’); oul.onclick = function(e) { e = e || window.event; var tar ...

  8. [置顶] Objective-C ,ios,iphone开发基础:protocol 协议(委托,代理)的声明

    协议是为了弥补Objective-c中类只能单继承的缺陷,在Objective-c2.0之前当一个类遵循一个协议的时候,必须在类中实现协议的所有方法,在Objective-c2.0之后协议中的方法就有 ...

  9. OC 代理 协议 委托 数据源的概念

    (网摘) OBJC 中的 protocol 相当于 java 里的接口,delagate 就是接口的实现类(C中的回调类似 ): 数据源就是对象遵循了存储数据的协议,可以存储使用数据 协议表示了方法可 ...

随机推荐

  1. NC202498 货物种类

    NC202498 货物种类 题目 题目描述 某电商平台有 \(n\) 个仓库,编号从 \(1\) 到 \(n\) . 当购进某种货物的时候,商家会把货物分散的放在编号相邻的几个仓库中. 我们暂时不考虑 ...

  2. 神经网络可视化《Grad-CAM:Visual Explanations from Deep Networks via Gradient-based Localization》

    神经网络已经在很多场景下表现出了很好的识别能力,但是缺乏解释性一直所为人诟病.<Grad-CAM:Visual Explanations from Deep Networks via Gradi ...

  3. 构建 API 的7个建议【翻译】

    迄今为止,越来越多的企业依靠API来为客户提供服务,以确保竞争的优势和业务可见性.出现这个情况的原因是微服务和无服务器架构正变得越来越普遍,API作为其中的关键节点,继承和承载了更多业务. 在这个前提 ...

  4. 工作流引擎在vivo营销自动化中的应用实践 | 引擎篇03

    作者:vivo 互联网服务器团队- Cheng Wangrong 本文是<vivo营销自动化技术解密>的第4篇文章,分析了在营销自动化业务引入工作流技术的背景和工作流引擎的介绍,同时介绍了 ...

  5. DeiT:注意力也能蒸馏

    DeiT:注意力也能蒸馏 <Training data-efficient image transformers & distillation through attention> ...

  6. Azure Devops(十四) 使用Azure的私有Nuget仓库

    哈喽大家好,最近因为工作的原因没有时间写文章,断更了俩月,今天我们开始继续研究Azure上的功能. 今天我们开始研究一下Azure的制品仓库,在之前的流水线的相关文章中,我们都使用到了制品仓库用来保存 ...

  7. C# 基础知识-特性

    C基础 - 特性 一.特性 1>特性本质就是一个类,直接或者间接的继承了Attribute 2>特性就是在不破话类封装的前提下,加点额外的信息或者行为 特性添加后,编译会在元素内部产生IL ...

  8. 虚言妙诀终虚见,面试躬行是致知,Python技术面试策略与技巧实战记录

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_183 2021年,对于正在找工作的朋友来说,笼罩在新冠肺炎疫情之下,今年的就业季显得更加具有挑战性,更有意思的是,每当这个时候,各 ...

  9. 【原创】Magisk+Shamiko过APP ROOT检测

    本文所有教程及源码.软件仅为技术研究.不涉及计算机信息系统功能的删除.修改.增加.干扰,更不会影响计算机信息系统的正常运行.不得将代码用于非法用途,如侵立删! Magisk+Shamiko过APP R ...

  10. Floyd算法详解

    Floyd本质上使用了DP思想,我们定义\(d[k][x][y]\)为允许经过前k个节点时,节点x与节点y之间的最短路径长度,显然初始值应该为\(d[k][x][y] = +\infin (k, x, ...