• [UE4]动态数组:TArray容器


    为什么使用UE4提供的容器类?

    如果你用过C++的STL库,你就知道STL提供了各种各样的容器/数据结构,使得你对处理很多数据的时候非常快捷高效。UE4同样也提供了类似的库,库里面的类型是以T开头的,使用UE4提供的容器库可以更好地实现跨平台。所以在UE4进行开发的时候我们很少去使用STL容器,更多时候是使用UE4提供的容器。

    一、TArray<T>是什么

    如果你学过C++的模板,你就知道TArray<T>是一个模板类型,T是由我们指定的任意类型。比如你想创建一个数组元素都是int类型的动态数组,那么这个数组类型就是TArray<int>,类似的,你可以创建TArray<double>、TArray<FString>等等,T甚至是可以是你自己创建的C++类。

    TArray<T>中所有数据元素都是T类型的,因此不能混合各种不同类型的元素进去这个容器。TArray没有设计成被继承的,所以不应该去继承它。并且new/delete一个TArray是危险的行为。当TArray的生命周期结束时(超出作用域),容器里面的所有元素也会被销毁。当你从另一个TArray创建一个新的TArray会复制所有数据元素到新的变量,而不会共享这些元素的内存。

    二、创建动态数组

    为了创建一个动态数组,你可以这样写:

    TArray<int32> IntArray;
    注意,int32是32位整形,而int会根据机器不同而字节不同,所以为了跨平台建议使用int32。

    这时候,因为我们没有数据填充到该数组里面,所以还没有内存被分配。

    三、填充动态数组

    1) TArray::Init

    在UE4的官方文档可以看到该函数的声明:

    void Init(const ElementType & Element, int32 Number)

    该函数用于设置数组为Number个,并且每个元素值的为Element,例如:

    IntArray.Init(10, 5);// IntArray == [10,10,10,10,10]

    2) TArray::Add

    先来看该函数的声明:

    int32 Add( const ElementType & Item)

    该函数用于添加新元素到动态数组末尾。

    3) TArray::Emplace

    先来看该函数的声明:

    template<typename... ArgsType>int32 Emplace( ArgsType &&... Args)

    该函数也是用于添加新元素到动态数组末尾。示例:

    TArray<FString> StrArr;StrArr.Add(TEXT("Hello"));StrArr.Emplace(TEXT("World"));// StrArr == ["Hello","World"]

    虽然Add和Emplace都是添加新元素到动态数组末尾,不过它们的内部实现是不同的:

    • Add会复制元素到数组容器里面
    • Emplace使用你给的参数来构造一个新的元素类型的实例


    在上面的示例中,Add创建了一个FString临时变量,然后传递给数组,在数组内部再次调用拷贝构造函数来复制这个FString临时变量。但是Emplace使用了C++11的右值引用技术,它不会构造临时变量,而是在数组内部使用这个字符串常量来创建FString变量。因此,由于Emplace少了一次拷贝构造函数操作,所以Emplace会比FString高效。但是由于Add比Emplace更具可读性,如果是普通的C++内置类型(不会有拷贝构造函数的巨大开销),建议使用Add。

    4) TArray::Push

    TArray::Push提供了两个一样功能的重载函数可以分别代替Add和Emplace。所以无论何时使用Push比Add和Emplace更加方便了:

    void Push( const ElementType & Item )void Push( ElementType && Item )

    四、迭代动态数组

    通常迭代都至少有两种方式,一种是使用索引,一种是使用迭代器。

    1) 使用索引迭代动态数组

    为了练习我们的数组,我们可以在靠近NPC的函数中打印该数组。我们将ANPC::Prox_Implementation修改成如下代码:

    void ANPC::Prox_Implementation(
        AActor* otherActor,
        UPrimitiveComponent* otherComp,
        int32 otherBodyIndex,
        bool bFromSweep,
        const FHitResult & sweepResult
    ) {
        //通过强制转换成AAVatar是否成功来判断是否玩家角色
        if (Cast<AAvatar>(otherActor) == nullptr)
        {
            return;
        }
        //获得第一人称控制器
        APlayerController* PController = GetWorld()->GetFirstPlayerController();
        if (PController)
        {
            //获得HUD界面
            AMyHUD* hud = Cast<AMyHUD>(PController->GetHUD());
            hud->AddMessage(Message(NpcMessage, 5.f, FColor::White));
            //测试数组
            TArray<int> array;
            array.Push(1);
            array.Push(3);
            array.Push(7);
            for (int index = 0; index < array.Num(); index++)
            {
                //GEngine是全局引擎变量,我们使用它的AddOnScreenDebugMessage函数来在游戏屏幕上打印调试信息。
                //该函数第一个参数是调试输出的位置,填写-1表示总在原来的调试信息上方。
                //第二个参数是字体大小,第三个参数是字体颜色,第四个参数是要打印的字符串,这里用FromInt函数将Int转换FString。
                GEngine->AddOnScreenDebugMessage(-1, 40, FColor::White, FString::FromInt(array[index]));
            }
        }
    }

    注意,每次调试打印的信息都在原来的调试信息上面,所以数组里面的内容依次是1、3、7,而不是7、3、1。

    2) 使用迭代器循环数组

    void ANPC::Prox_Implementation(
        AActor* otherActor,
        UPrimitiveComponent* otherComp,
        int32 otherBodyIndex,
        bool bFromSweep,
        const FHitResult & sweepResult
    ) {
        //通过强制转换成AAVatar是否成功来判断是否玩家角色
        if (Cast<AAvatar>(otherActor) == nullptr) {
            return;
        }
        //获得第一人称控制器
        APlayerController* PController = GetWorld()->GetFirstPlayerController();
        if (PController)
        {
            //获得HUD界面
            AMyHUD* hud = Cast<AMyHUD>(PController->GetHUD());
            hud->AddMessage(Message(NpcMessage, 5.f, FColor::White));
            //测试数组
            TArray<int> array;
            array.Push(1);
            array.Push(3);
            array.Push(7);
            //在使用容器的时候,为了容器的操作一致性,通常都会像下面这样使用迭代器来循环
            //所谓的迭代器其实类似于一个指针,当对指针进行++时,就指向后面的元素。
            //当超出容器范围的时候,迭代器为空,跳出循环
            for (TArray<int>::TIterator it = array.CreateIterator(); it; ++it)
            {
                //GEngine是全局引擎变量,我们使用它的AddOnScreenDebugMessage函数来在游戏屏幕上打印调试信息。
                //该函数第一个参数是调试输出的位置,填写-1就不会覆盖以前的调试信息。
                //第二个参数是字体大小,第三个参数是字体颜色,第四个参数是要打印的字符串,这里用FromInt函数将Int转换FString。
                GEngine->AddOnScreenDebugMessage(-1, 40, FColor::White, FString::FromInt(*it));
            }
        }
    }

    在实际开发中,因为使用迭代器进行迭代更简洁美观,我们通常都会这样进行迭代容器。

    找到一个元素是否在 TArray
    寻找UE4容器是容易的。它通常使用find成员函数完成。使用前面创建的数组,我们可以通过键入以下代码行来找到值10的索引:
    int index = array.Find( 10 ); // would be index 3 in image above

  • 相关阅读:
    二分查找算法
    Python基础二(基础数据类型)
    Python基础一
    mysql 数据库
    Scrapy (网络爬虫框架)入门
    列表推导式的使用
    Scrapy(爬虫框架)中,Spider类中parse()方法的工作机制
    vim 基础命令大全
    windows cmd 命令大全
    类与类的关系一
  • 原文地址:https://www.cnblogs.com/timy/p/8641671.html
Copyright © 2020-2023  润新知