IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 虚幻C++入门个人笔记(3) -> 正文阅读

[游戏开发]虚幻C++入门个人笔记(3)

接口

接口的词义广泛,用来陈述功能、选项,与其他程序结构进行沟通的方式。接口抽象出了交互结构,提供了两个未知逻辑交互的便捷性。对于编程中,如何更好地设计低耦合程序起到了至关重要的作用。设计者可以在互不关心的情况下,进行友好的程序设计,并且通过接口来完成设计的整合交互

虚幻引擎中加入了接口设计,从一定程度上“去掉了”多继承。接口可以帮助我们解决在不同类型的类之间却有相同行为的特性。接口设计增加了代码编写的便捷性(处理不同类中的相同行为)

例如在设计射击类游戏时,我们需要子弹与场景中的物体进行交互,场景中的桌椅板凳,角色,怪物(都是独立的对象)都希望受到子弹的攻击伤害,那么子弹在打到目标后要逐一排查,审查目标是否属于上述对象,这很麻烦。但我们可以通过接口,增加上述目标具有受伤的能力。当子弹打到目标时,我们只需要检查目标是否继承受伤的接口,如果有则调用接口函数即可

构建接口类

接口类可以直接在虚幻编辑器中选择继承然后完成构建? ? ? ? ——Unreal接口

编写接口函数

接口如果想在蓝图中使用可以在UCLASS中加入Blueprintable

如果接口在蓝图中被继承,则:

1.如果函数没有返回类型,贼在蓝图中当作事件Event使用

2.如果函数存在返回类型,则在蓝图中当作函数使用(需要在函数的overlap中寻找)

3.函数说明如果使用BlueprintNativeEvent则必须在继承类中定义函数(同名加后缀_Implementation),如果使用BlueprintImplementableEvent修饰则可以选择性重写

接口中要加标记宏UFUNCTION(BlueprintNativeEvent)

实现接口

如果在C++中希望获得接口能力,则需要继承接口。需要注意的是,必须继承I开头的接口名称,并且继承修饰位public。不要尝试重写接口中的函数。如果希望实现接口可以编写同名函数,并用后缀“_Implementation”进行标记。所有的接口函数都必须有定义

注意的地方

1.逻辑写在接口的头文件的I类的public里,不要写错了地方

2.不要加私有域和受保护域

3.不要在U类里写代码

继承操作

1.在你需要继承的接口的Actor的头文件里的class的后面加",public I接口名称",引入头文件

2.virtual 接口中的函数_Implementation() override; //声明并定义

调用操作

调用函数,持有继承接口对象指针,第一步先转换到I类指针,调用Execute_接口函数名,参数第一位需要传递原对象指针,后面直接按照原函数参数填入即可

接口例子:

1.创建接口函数

//接口头文件中

public:

?? ?UFUNCTION(BlueprintNativeEvent,BlueprintCallable)
?? ?void NotifyHurt(int32 Value);

?? ?UFUNCTION(BlueprintNativeEvent,BlueprintCallable)
?? ?int32 NotifyHurt_Two();

2.继承接口类

MyActor头文件中,继承添加上接口

3.创建接口函数定义

//MyActor头文件中

public:
?? ?
?? ?virtual void NotifyHurt_Implementation(int32 Value) override;? ? ? ? //加定义
?? ?
?? ?virtual int32 NotifyHurt_Two_Implementation() override;? ? ? ? //加定义,return0

4.调用接口函数

AMyActor* My?= GetWorld()->SpawnActor<AMyActor>(AMyActor::StaticClass());
?? ?if (My)
?? ?{
?? ??? ?IHurtInterface* Interf = Cast<IHurtInterface>(My);
?? ??? ?if (Interf)
?? ??? ?{
?? ??? ??? ?Interf->Execute_NotifyHurt(My, Num);
?? ??? ?}
?? ?}

接口总结

1.接口函数需要定义在I开头的类中,不要修改访问域public关键字,声明需要使用宏标记BlueprintNativeEvent或BlueprintImplementableEvent(专门用来在蓝图中使用)

2.如果需要继承接口,继承I类,继承关系public

3.接口中的函数禁止重载

4.在继承类中实现接口函数,并添加后缀_Implementation,需要注意,函数前加入虚函数关键字virtual,函数结尾加override,并在cpp文件中实现逻辑

5.所有接口函数均必须要进行定义

6.调用函数,持有继承接口对象指针,第一步先转到I类指针,调用Execute_接口函数名,参数第一位需要传递原对象指针,后面直接按照原函数参数填入即可

7.检查某一个类是否实现了对应接口可以使用如下语法进行检查

obj->GetClass()->ImplementsInterface(U类型::StaticClass());

act->GetClass()->ImplementsInterface(UMyInterface::StaticClass());

act是对象指针

接口优点

1.具备多态特性,接口衍生类支持李氏转换原则

2.接口可以使得整个继承系统更加的干净单一

3.接口可以规范类的具体行为,继承自接口的类必须实现接口中的行为

4.接口可以隔离开发中的开发耦合,我们只需要针对接口去编码,无需关心具体行为

5.接口继承可以使得继承关系中出现真正的操作父类

接口缺点

1.丢失了C++中的广泛继承特性

2.接口拘束了类型的属性拓展,无法进行更详细的内容定义

3.继承关系中容易让人混淆,接口本身不具备真正的继承特性

虚幻中的智能指针

指针

虚幻为了解决C++中指针导致的内存泄漏问题,加入了智能指针(共享指针,共享引用,弱指针),当我们使用动态方式构建对象时,再也不需要担心内存释放的问题,指针的释放规则由引擎制定,包括释放时机

自定义类

智能指针不能应用到U类上,我们在虚幻中创建C++类中继承自虚幻的都是U类,选择无则是自定义类

在构建自定义类时需要以F开头

#pragma once

#include "CoreMinimal.h"
#include <UObject/GCObject.h>

/**
?*?
?*/
class ULESSONJK_API FNClass : public FGCObject
{
public:
?? ?FNClass();
?? ?~FNClass();

protected:
?? ?class USoundBase* Sound;

?? ?virtual void AddReferencedObjects(FReferenceCollector& Collector)override;

}

定义

void FNClass::AddReferencedObjects(FReferenceCollector& Collector)
{
?? ?Collector.AddReferencedObject(Sound);

}

智能指针

虚幻中存在一套非常强大的动态内存管理机制,而这套机制中根本在于智能指针(非侵入式),并且UE的智能指针速度相比STL更快,速度和普通C++指针速度一样

智能指针本质目的是将内存工作进行托管。当两个智能指针指向同一个空间,一个设置为空,另一个不会跟随为空,智能指针设置为空并不是释放内存空间,只是在减少空间引用。

注意:智能指针只能适用于自定义类,U类禁止使用

共享指针和共享引用优点

优点描述
简洁的语法可以像操作常规的C++指针那样来复制、解引用及比较共享指针

防止内存泄漏

当没有共享引用时资源自动销毁
弱引用弱指针允许安全地检查一个对象是否已经被销毁
线程安全包含了可以通过多个线程安全地进行访问的“线程安全”版本
普遍性几乎可以创建到任何类型的对象的共享指针
运行时安全共享引用永远不会为null,且总是可以解引用
不会产生引用循环使用弱引用来断开引用循环
表明用途可以轻松地区分对象 拥有者和观察者
性能共享指针的性能消耗最小,所有操作所占时间都是固定的
强大的功能支持针‘const’、前置声明的不完全类型、类型转换等
内存所占内存大小是C++指针在64位系统中所占内存的二倍(外加了一个共享的16字节的引用控制器)

构建智能指针时为什么要添加一个像弱指针一样的东西?

弱指针能够解决循环引用嵌套导致内存泄漏的问题

三个指针

共享指针描述
共享指针(TSharedPtr)引用计数的非侵入式的权威智能指针
共享引用(TSharedRef)不能设置为null值的、引用计数的、非侵入式权威智能指针
弱指针(TWeakPtr)引用计数的、非侵入式弱指针引用

共享指针

最常用的智能指针,用来托管内存

共享指针是虚幻中最常用的智能指针,在操作上可以帮助我们构建托管内存指针。共享指针本身是非侵入式的,这使得指针的使用与操作和普通指针一致。共享指针支持主动指向空,并且共享指针是线程安全的。节省内存,性能高效

注意:构建自定义类时,需要使用F开头

基本操作语法

声明和初始化

TSharedPtr<FNClass> pN;? ? ? ? //声明一个未被赋值的共享指针

TSharedPtr<FNClass> pN(new FNClass());? ? ? ? //构建一个赋值了的共享指针

TSharedPtr<FNClass> pN=MakeShareable(new FNClass());? ? ? ? //借助MakeShareable构建一个共享指针

//MakeShareable函数是用来构建共享指针的快捷方式

不要将一个指针变量单独作为两次共享指针对象的创建,会出错

例如

FNClass* p1=new FNClass();

TSharedPtr<FNClass> pN(p1);

TSharedPtr<FNClass> pN2(p1);? ? ? ? //会出错

TSharedPtr<FNClass> pN2=pN;? ? ? ? //没问题

解引用和操作

pN->CallFun();? ? ? ? //CallFun是成员函数

pN.Get()->CallFun();

(*pN).CallFun();

使用前要if(pN.IsValid())

释放

pN.Reset();

pN=nullptr;? ? ? ? //标记我不使用这个指针了,并不是真正的释放了,释放由系统自动

获取引用计数器

pN.GetSharedReferenceCount();? ? ? ? //获得当前地址被引用个数

共享引用

共享引用和共享指针一样,都是为了完成内存托管的问题,特点是不能主动释放为空

共享引用禁止为空,表明了共享引用创建后必须给予有效初始化,可以使得代码更加简洁安全,保证了对象访问的安全性。无法主动释放共享内存,可以跟随对象释放减少引用计数器

共享引用的安全性体现在,如果使用共享引用构建的对象,无法将对象空间设置为空。如果想释放内存,可以借助指向其他共享引用来减少引用计数,来释放空间

共享引用本质,无法主动减少引用计数器,只能通过被动方法,例如生命周期终结,共享引用易主

操作语法

声明和初始化

TSharedRef<FNClass> pN;? ? ? ? //错误 执行将导致崩溃,共享引用无法为空

TSharedRef<FNClass> pN(new FNClass());? ? ? ? //正确

解引用操作

TSharedRef<FNClass> pN(new FNClass());? ? ? ? //正确

pN->CallFun();

(*pN).CallFun();

const FNClass& pN1=pN.Get();? ? ? ? //返回const引用,禁止将对象主动释放

和共享指针转换

//共享引用支持隐式转换为共享指针,由于共享引用是安全的,所以转换是隐式转换

TSharedPtr<FNClass> pSN=pN;

//从共享指针转换到共享引用是不安全的,所以需要调用TS函数

TSharedRef<FNClass> pM=pSN.ToSharedRef();

弱指针

智能指针好用但解决不了引用循环的问题,弱指针就是被拿来解决这个问题的

弱指针不会阻止对象的销毁,如果引用对象被销毁,则弱指针也将自动清空。一般弱指针的操作意图是保存了一个到达目标对象的指针,但不会控制该对象的生命周期,弱指针不会增加引用计数,可以用来断开引用循环问题

引用循环问题:当有两个类的时候,A类持有B类智能指针pb,B类持有A类智能指针pa,

A* a;

B* b;

a->pb=b;

b->pa=a;

//当引用计数器为0时智能指针释放

无论是谁销毁了对象,只要其对象被销毁,弱指针都将自动清空,这使得能够安全缓存指向可变对象的指针。这也意味着,弱指针可能会意外清空,并且,可以使用弱指针断开引用循环

当不再存在对对象的共享引用时,弱指针的对象将被销毁

弱指针有助于表明意图。当在某一个类中看到一个弱指针时,就应当明白该类仅缓存指向对象的指针,它并不会控制它的生命周期

操作语法

声明和初始化

TWeakPtr<FNClass> pWN;? ? ? ? //声明一个空的弱指针

TSharedPtr<FNClass> pN;

TWeakPtr<FNClass> pWN1(pN);? ? ? ? //借助共享指针构建

TSharedRef<FNClass> pRN;

TWeakPtr<FNClass> pWN2(pRN);? ? ? ? //借助共享引用构建

解引用操作

//弱指针无法直接调用(弱指针调用会出现对象为空,因为弱指针不会阻止对象释放)

//弱指针如果需要操作,需要转换为共享指针

TSharedPtr<FNClass> pN(pWN.Pin());

if(pN.IsValid())? ? ? ? //检查是否转换成功

{

pN->Func();????????//使用共享指针的操作方式

}

//Pin函数会组指对象被销毁

检查是否有效

//检查弱指针指向的对象空间是否存在

if(pWN.IsValid())

{}

释放操作

//主动释放,但是并不会影响引用计数

pWN=nullptr;

智能指针总结

一块内存如果存在有效引用(可直接到达内存的操作方式),则我们可以认为当前内存是有效且合理的。但当一块内存不存在引用,则我们可以视为此块内存为被弃用无效的,则可以回收重复利用,这就是内存垃圾回收机制的基本原理

智能指针强调的是当前内存的使用者存在多少,当不存在时进行回收

注意:智能指针构建的均是栈对象数据类型

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-09-24 21:25:15  更:2022-09-24 21:26:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/17 3:36:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码