概述

  • TMap主要由两个类型定义(一个键类型和一个值类型),以关联对的形式存储在映射中。

  • 将数据存储为键值对(TPair<KeyType, ValueType>),只将键用于存储和获取

  • 映射有两种类型:TMapTMultiMap

    • TMap 中的键是唯一的
    • TMultiMap 可存储多个相同的键
  • TMap 也是值类型,支持通常的复制、赋值和析构函数运算,以及它的元素的强所有权。在映射被销毁时,它的元素都会被销毁。键和值也必须为值类型。

  • TMap 是散列容器,这意味着键类型必须支持 GetTypeHash 函数,并提供 运算符== 来比较各个键是否等值

  • TMap 也可使用任选分配器来控制内存分配行为。但不同于 TArray,这些是集合分配器,而不是 FHeapAllocator 和 TInlineAllocator 之类的标准UE4分配器。集合分配器(TSetAllocator类)定义映射应使用的散列桶数量,以及应使用哪个标准UE4分配器来存储散列和元素。

  • KeyFuncs 是最后一个 TMap 模板参数,该参数告知映射如何从元素类型获取键,如何比较两个键是否相等,以及如何对键进行散列计算。这些参数有默认值,它们只会返回对键的引用,使用 运算符== 确定相等性,并调用非成员 GetTypeHash 函数进行散列计算。如果您的键类型支持这些函数,可使用它作为映射键,不需要提供自定义 KeyFuncs。

  • 与 TArray 不同的是,内存中 TMap 元素的相对排序既不可靠也不稳定,对这些元素进行迭代很可能会使它们返回的顺序和它们添加的顺序有所不同。这些元素也不太可能在内存中连续排列。映射的支持数据结构是稀疏数组,这种数组可有效支持元素之间的空位。当元素从映射中被移除时,稀疏数组中就会出现空位。将新的元素添加到数组可填补这些空位。但是,即便 TMap 不会打乱元素来填补空位,指向映射元素的指针仍然可能失效,因为如果存储器被填满,又添加了新的元素,整个存储可能会重新分配。

创建

TMap<int32, FString> FruitMap;  //空TMap,此时尚未分配内存

添加元素

  • Add

    • 元素按插入顺序排列,但不保证这些元素在内存中实际保留此排序
    • 各个键都必定是唯一。如果尝试添加重复键,将替换原来的键值
    • Add 函数可接受不带值的键。调用此重载后的 Add 时,值将被默认构建
    FruitMap.Add(5, TEXT("Banana"));
    FruitMap.Add(2, TEXT("Grapefruit"));
    FruitMap.Add(7, TEXT("Pineapple"));
    // FruitMap == [
    // { Key:5, Value:"Banana" },
    // { Key:2, Value:"Grapefruit" },
    // { Key:7, Value:"Pineapple" } ] FruitMap.Add(2, TEXT("Pear"));
    // FruitMap == [
    // { Key:5, Value:"Banana" },
    // { Key:2, Value:"Pear" },
    // { Key:7, Value:"Pineapple" }
    // ] FruitMap.Add(4);
    // FruitMap == [
    // { Key:5, Value:"Banana" },
    // { Key:2, Value:"Pear" },
    // { Key:7, Value:"Pineapple" },
    // { Key:4, Value:"" }
    // ]
  • Emplace 可以代替 Add,防止插入映射时创建临时文件

    FruitMap.Emplace(3, TEXT("Orange"));
    // FruitMap == [
    // { Key:5, Value:"Banana" },
    // { Key:2, Value:"Pear" },
    // { Key:7, Value:"Pineapple" },
    // { Key:4, Value:"" },
    // { Key:3, Value:"Orange" }
    // ]
  • Append 函数合并映射,将一个映射的所有元素移至另一个映射,源映射的相同键会替代目标映射中的键

    TMap<int32, FString> FruitMap2;
    FruitMap2.Emplace(4, TEXT("Kiwi"));
    FruitMap2.Emplace(9, TEXT("Melon"));
    FruitMap2.Emplace(5, TEXT("Mango"));
    FruitMap.Append(FruitMap2);
    // FruitMap == [
    // { Key:5, Value:"Mango" },
    // { Key:2, Value:"Pear" },
    // { Key:7, Value:"Pineapple" },
    // { Key:4, Value:"Kiwi" },
    // { Key:3, Value:"Orange" },
    // { Key:9, Value:"Melon" }
    // ]
    // FruitMap2 is now empty.

迭代

  • 范围 - for

    for (auto& Elem :FruitMap)
    {
    FPlatformMisc::LocalPrint( *FString::Printf(TEXT("(%d, \"%s\")\n"), Elem.Key, *Elem.Value) );
    }
    // Output:
    // (5, "Mango")
    // (2, "Pear")
    // (7, "Pineapple")
    // (4, "Kiwi")
    // (3, "Orange")
    // (9, "Melon")
  • 迭代器

    • CreateIterator 返回拥有读写访问权限的迭代器,
    • CreateConstIterator 返回拥有只读访问权限的迭代器
    for (auto It = FruitMap.CreateConstIterator(); It; ++It)
    {
    FPlatformMisc::LocalPrint(
    *FString::Printf( TEXT("(%d, \"%s\")\n"),
    It.Key(), // same as It->Key
    *It.Value() // same as *It->Value
    ) );
    }

查询

  • Num 函数查询映射中保存的元素数量

    int32 Count = FruitMap.Num(); // Count == 6
  • Contains 函数查询是否包含特定键

    bool bHas7 = FruitMap.Contains(7); // bHas7 == true
    bool bHas8 = FruitMap.Contains(8); // bHas8 == false
  • [] 运算符将键用作索引查找相应值

    • 使用非常量映射执行该操作将返回非常量引用,使用常量映射将返回常量引用。
    • 在使用 运算符[] 前,应检查映射中是否存在该键。如果映射中键不存在,将触发断言。
    FString Val7 = FruitMap[7]; // Val7 == "Pineapple"
    FString Val8 = FruitMap[8]; // Assert!
  • Find 函数进行键查找

    • 如果映射包含该键,Find 将返回指向元素数值的指针;如果映射不包含该键,则返回null。
    • 在常量映射上调用 Find,返回的指针也将为常量。
    FString* Ptr7 = FruitMap.Find(7); // *Ptr7 == "Pineapple"
    FString* Ptr8 = FruitMap.Find(8); // Ptr8 == nullptr
  • FindOrAdd 将返回对与给定键关联的值的引用。

    • 如果映射中不存在该键,FindOrAdd 将返回新创建的元素(使用给定键和默认构建值),该元素也会被添加到映射。
    • FindOrAdd 可修改映射,因此仅适用于非常量映射。
    FString& Ref7 = FruitMap.FindOrAdd(7);
    // Ref7 == "Pineapple"
    // FruitMap == [
    // { Key:5, Value:"Mango" },
    // { Key:2, Value:"Pear" },
    // { Key:7, Value:"Pineapple" },
    // { Key:4, Value:"Kiwi" },
    // { Key:3, Value:"Orange" },
    // { Key:9, Value:"Melon" }
    // ] FString& Ref8 = FruitMap.FindOrAdd(8);
    // Ref8 == ""
    // FruitMap == [
    // { Key:5, Value:"Mango" },
    // { Key:2, Value:"Pear" },
    // { Key:7, Value:"Pineapple" },
    // { Key:4, Value:"Kiwi" },
    // { Key:3, Value:"Orange" },
    // { Key:9, Value:"Melon" },
    // { Key:8, Value:"" }
    // ]
    //如已发生重新分配,此处的 Ref7 引用可能会因 FruitMap.FindOrAdd(8) 的调用而无效化。
  • FindRef 会返回与给定键关联的值副本

    • 若映射中未找到给定键,则返回默认构建值。
    • FindRef 不会创建新元素,因此既可用于常量映射,也可用于非常量映射。
    FString Val7 = FruitMap.FindRef(7);
    FString Val6 = FruitMap.FindRef(6);
    // Val7 == "Pineapple"
    // Val6 == ""
    // FruitMap == [
    // { Key:5, Value:"Mango" },
    // { Key:2, Value:"Pear" },
    // { Key:7, Value:"Pineapple" },
    // { Key:4, Value:"Kiwi" },
    // { Key:3, Value:"Orange" },
    // { Key:9, Value:"Melon" },
    // { Key:8, Value:"" }
    // ]
  • FindKey 函数执行逆向查找

    • 返回指向与所提供值配对的第一个键的指针。搜索映射中不存在的值将返回空键。
    • 按值查找比按键查找慢(线性时间)。这是因为映射按键排序,而非按值排序。
    • 如果映射有多个具有相同值的键,FindKey 可返回其中任一键。
    const int32* KeyMangoPtr   = FruitMap.FindKey(TEXT("Mango"));   // *KeyMangoPtr   == 5
    const int32* KeyKumquatPtr = FruitMap.FindKey(TEXT("Kumquat")); // KeyKumquatPtr == nullptr
  • GenerateKeyArrayGenerateValueArray 分别使用所有键和值的副本来填充 TArray。

    • 在这两种情况下,都会在填充前清空所传递的数组,因此产生的元素数量始终等于映射中的元素数量
    TArray<int32>   FruitKeys;
    TArray<FString> FruitValues;
    FruitKeys.Add(999);
    FruitKeys.Add(123);
    FruitMap.GenerateKeyArray (FruitKeys);
    FruitMap.GenerateValueArray(FruitValues);
    // FruitKeys == [ 5,2,7,4,3,9,8 ]
    // FruitValues == [ "Mango","Pear","Pineapple","Kiwi","Orange","Melon","" ]

移除

  • Remove 函数提供要移除元素的键。

    • 返回值是被移除元素的数量。
    • 如果映射不包含与键匹配的元素,则返回值可为零。
    • 移除元素将在数据结构(在Visual Studio的观察窗口中可视化映射时可看到)中留下空位
    FruitMap.Remove(8);
    // FruitMap == [
    // { Key:5, Value:"Mango" },
    // { Key:2, Value:"Pear" },
    // { Key:7, Value:"Pineapple" },
    // { Key:4, Value:"Kiwi" },
    // { Key:3, Value:"Orange" },
    // { Key:9, Value:"Melon" }
    // ]
  • FindAndRemoveChecked 函数可用于从映射移除元素并返回其值。

    • 名称中的checked部分意味着将检查键是否存在,如果不存在,则出现断言:
    FString Removed7 = FruitMap.FindAndRemoveChecked(7);
    // Removed7 == "Pineapple"
    // FruitMap == [
    // { Key:5, Value:"Mango" },
    // { Key:2, Value:"Pear" },
    // { Key:4, Value:"Kiwi" },
    // { Key:3, Value:"Orange" },
    // { Key:9, Value:"Melon" }
    // ] FString Removed8 = FruitMap.FindAndRemoveChecked(8);
    // Assert!
  • RemoveAndCopyValue 函数的作用与 Remove 相似,不同点是会将已移除元素的值复制到引用参数。如果映射中不存在指定的键,则输出参数将保持不变,函数将返回 false。

    FString Removed;
    bool bFound2 = FruitMap.RemoveAndCopyValue(2, Removed);
    // bFound2 == true
    // Removed == "Pear"
    // FruitMap == [
    // { Key:5, Value:"Mango" },
    // { Key:4, Value:"Kiwi" },
    // { Key:3, Value:"Orange" },
    // { Key:9, Value:"Melon" } ] bool bFound8 = FruitMap.RemoveAndCopyValue(8, Removed);
    // bFound8 == false
    // Removed == "Pear", i.e. unchanged
    // FruitMap == [
    // { Key:5, Value:"Mango" },
    // { Key:4, Value:"Kiwi" },
    // { Key:3, Value:"Orange" },
    // { Key:9, Value:"Melon" } ]
  • EmptyReset 函数可将映射中的所有元素移除。

    • Empty 可采用参数指示映射中保留的slack量
    • Reset 则是尽可能多地留出slack量。
    TMap<int32, FString> FruitMapCopy = FruitMap;
    // FruitMapCopy == [
    // { Key:5, Value:"Mango" },
    // { Key:4, Value:"Kiwi" },
    // { Key:3, Value:"Orange" },
    // { Key:9, Value:"Melon" }
    // ] FruitMapCopy.Empty(); // We could also have called Reset() here.
    // FruitMapCopy == []

排序

TMap 可以进行排序。排序后,迭代映射会以排序的顺序显示元素,但下次修改映射时,排序可能会发生变化。排序是不稳定的,因此等值元素在MultiMap中可能以任何顺序出现。

  • 使用 KeySortValueSort 函数可分别按键和值进行排序。两个函数均使用二元谓词来进行排序:

    FruitMap.KeySort([](int32 A, int32 B) {
    return A > B; // sort keys in reverse
    });
    // FruitMap == [
    // { Key:9, Value:"Melon" },
    // { Key:5, Value:"Mango" },
    // { Key:4, Value:"Kiwi" },
    // { Key:3, Value:"Orange" }
    // ] FruitMap.ValueSort([](const FString& A, const FString& B) {
    return A.Len() < B.Len(); // sort strings by length
    });
    // FruitMap == [
    // { Key:4, Value:"Kiwi" },
    // { Key:5, Value:"Mango" },
    // { Key:9, Value:"Melon" },
    // { Key:3, Value:"Orange" }
    // ]

运算符

  • 和 TArray 一样,TMap 是常规值类型,可通过标准复制构造函数或赋值运算符进行复制。因为映射严格拥有其元素,复制映射的操作是深层的,所以新的映射将拥有其自己的元素副本。

    TMap<int32, FString> NewMap = FruitMap;
    NewMap[5] = "Apple";
    NewMap.Remove(3);
    // FruitMap == [
    // { Key:4, Value:"Kiwi" },
    // { Key:5, Value:"Mango" },
    // { Key:9, Value:"Melon" },
    // { Key:3, Value:"Orange" }
    // ]
    // NewMap == [
    // { Key:4, Value:"Kiwi" },
    // { Key:5, Value:"Apple" },
    // { Key:9, Value:"Melon" }
    // ]
  • MoveTemp 函数可调用移动语义。在移动后,源映射必定为空

    FruitMap = MoveTemp(NewMap);
    // FruitMap == [
    // { Key:4, Value:"Kiwi" },
    // { Key:5, Value:"Apple" },
    // { Key:9, Value:"Melon" }
    // ]
    // NewMap == []

Slack

简介

  • Slack是不包含元素的已分配内存。调用 Reserve 可分配内存,无需添加元素;
  • 通过非零slack参数调用 Reset 或 Empty 可移除元素,无需将其使用的内存取消分配。
  • Slack优化了将新元素添加到映射的过程,因为可以使用预先分配的内存,而不必分配新内存。
  • 它在移除元素时也十分实用,因为系统不需要将内存取消分配。在清空希望用相同或更少的元素立即重新填充的映射时,此方法尤其有效。
  • TMap 不像 TArray 中的 Max 函数那样可以检查预分配元素的数量。

函数

  • Reset

    FruitMap.Reset();
    // FruitMap == [<invalid>, <invalid>, <invalid>]
    // 内存不释放
  • Reserve 函数预先分配映射

    FruitMap.Reserve(10);
    for (int32 i = 0; i < 10; ++i)
    {
    FruitMap.Add(i, FString::Printf(TEXT("Fruit%d"), i));
    }
    // FruitMap == [
    // { Key:9, Value:"Fruit9" },
    // { Key:8, Value:"Fruit8" },
    // ...
    // { Key:1, Value:"Fruit1" },
    // { Key:0, Value:"Fruit0" }
    // ]
  • 使用 CollapseShrink 函数可移除 TMap 中的全部slack。

    • Shrink 将从容器的末端移除所有slack,但这会在中间或开始处留下空白元素。
    for (int32 i = 0; i < 10; i += 2)
    {
    FruitMap.Remove(i);
    }
    // FruitMap == [
    // { Key:9, Value:"Fruit9" },
    // <invalid>,
    // { Key:7, Value:"Fruit7" },
    // <invalid>,
    // { Key:5, Value:"Fruit5" },
    // <invalid>,
    // { Key:3, Value:"Fruit3" },
    // <invalid>,
    // { Key:1, Value:"Fruit1" },
    // <invalid>
    // ]
    FruitMap.Shrink();
    // FruitMap == [
    // { Key:9, Value:"Fruit9" },
    // <invalid>,
    // { Key:7, Value:"Fruit7" },
    // <invalid>,
    // { Key:5, Value:"Fruit5" },
    // <invalid>,
    // { Key:3, Value:"Fruit3" },
    // <invalid>,
    // { Key:1, Value:"Fruit1" }
    // ]
    //代码中,Shrink 只删除了一个无效元素,因为末端只有一个空元素
  • Compact 函数将空白空间组合在一起,为调用 Shrink 做好准备

    FruitMap.Compact();
    // FruitMap == [
    // { Key:9, Value:"Fruit9" },
    // { Key:7, Value:"Fruit7" },
    // { Key:5, Value:"Fruit5" },
    // { Key:3, Value:"Fruit3" },
    // { Key:1, Value:"Fruit1" },
    // <invalid>,
    // <invalid>,
    // <invalid>,
    // <invalid>
    // ]
    FruitMap.Shrink();
    // FruitMap == [
    // { Key:9, Value:"Fruit9" },
    // { Key:7, Value:"Fruit7" },
    // { Key:5, Value:"Fruit5" },
    // { Key:3, Value:"Fruit3" },
    // { Key:1, Value:"Fruit1" }
    // ]

KeyFuncs

只要类型具有 运算符== 和非成员 GetTypeHash 重载,就可用作 TMap 的KeyType,不需要任何更改。但是,可能需要将类型用作键,而不重载这些函数。在这些情况下,可对 KeyFuncs 进行自定义。为键类型创建 KeyFuncs,必须定义两个typedef和三个静态函数,如下所示:

  • KeyInitType —— 用于传递键的类型。
  • ElementInitType —— 用于传递元素的类型。
  • KeyInitType GetSetKey(ElementInitType Element)——返回元素的键。
  • bool Matches(KeyInitType A, KeyInitType B) —— 如果 A 和 B 等值将返回 true,否则返回 false
  • uint32 GetKeyHash(KeyInitType Key) —— 返回 Key 的散列值。

KeyInitType 和 ElementInitType 是键类型和值类型的常规传递约定的typedef。它们通常为浅显类型的一个值,和非浅显类型的一个常量引用。请记住,映射的元素类型是 TPair

struct FMyStruct
{
// String which identifies our key
FString UniqueID; // Some state which doesn't affect struct identity
float SomeFloat; explicit FMyStruct(float InFloat)
:UniqueID (FGuid::NewGuid().ToString())
, SomeFloat(InFloat)
{
}
};
template <typename ValueType>
struct TMyStructMapKeyFuncs :
BaseKeyFuncs<
TPair<FMyStruct, ValueType>,
FString
>
{
private:
typedef BaseKeyFuncs<
TPair<FMyStruct, ValueType>,
FString
> Super; public:
typedef typename Super::ElementInitType ElementInitType;
typedef typename Super::KeyInitType KeyInitType; static KeyInitType GetSetKey(ElementInitType Element)
{
return Element.Key.UniqueID;
} static bool Matches(KeyInitType A, KeyInitType B)
{
return A.Compare(B, ESearchCase::CaseSensitive) == 0;
} static uint32 GetKeyHash(KeyInitType Key)
{
return FCrc::StrCrc32(*Key);
}
};

FMyStruct 具有唯一标识符,以及一些与身份无关的其他数据。GetTypeHash 和 运算符== 不适用于此,因为 运算符== 为实现通用目的不应忽略任何类型的数据,但同时又需要如此才能与 GetTypeHash 的行为保持一致,后者只关注 UniqueID 字段。以下步骤有助于为 FMyStruct 创建自定义 KeyFuncs

  1. 首先,继承 BaseKeyFuncs,因为它可以帮助定义某些类型,包括 KeyInitType 和 ElementInitType

    BaseKeyFuncs 使用两个模板参数:映射的元素类型和键类型。和所有映射一样,元素类型是 TPair,使用 FMyStruct 作为其 KeyTypeTMyStructMapKeyFuncs 的模板参数作为其 ValueType。将备用 KeyFuncs 用作模板,可为每个映射指定 ValueType,因此每次要在 FMyStruct 上创建键控 TMap 时不必定义新的 KeyFuncs。第二个 BaseKeyFuncs 参数是键类型,不要与元素存储的键区(TPair 的 KeyType)混淆。因为此映射应使用 UniqueID(来自 FMyStruct)作为键,所以此处使用 FString

  2. 然后,定义三个必需的 KeyFuncs 静态函数。第一个是 GetSetKey,该函数返回给定元素类型的键。由于元素类型是 TPair,而键是 UniqueID,所以该函数可直接返回 UniqueID

    第二个静态函数是 Matches,该函数接受两个元素的键(由 GetSetKey 获取),然后比较它们是否相等。在 FString 中,标准的等效测试(运算符==)不区分大小写;要替换为区分大小写的搜索,请用相应的大小写对比选项使用 Compare 函数。

  3. 最后,GetKeyHash 静态函数接受提取的键并返回其散列值。由于 Matches 函数区分大小写,GetKeyHash 也必须区分大小写。区分大小写的 FCrc 函数将计算键字符串的散列值。

  4. 现在结构已满足 TMap 要求的行为,可创建它的实例。

TMap<
FMyStruct,
int32,
FDefaultSetAllocator,
TMyStructMapKeyFuncs<int32>
> MyMapToInt32; // Add some elements
MyMapToInt32.Add(FMyStruct(3.14f), 5);
MyMapToInt32.Add(FMyStruct(1.23f), 2); // MyMapToInt32 == [
// {
// Key:{
// UniqueID:"D06AABBA466CAA4EB62D2F97936274E4",
// SomeFloat:3.14f
// },
// Value:5
// },
// {
// Key:{
// UniqueID:"0661218447650259FD4E33AD6C9C5DCB",
// SomeFloat:1.23f
// },
// Value:5
// }
// ]

在自行设置KeyFuncs时,要注意 TMap 假设两个项目使用 Matches 比较的结果相等,则它们会从 GetKeyHash 返回相同的值。此外,如果对现有映射元素的键进行的修改将会改变来自这两个函数中任一个的结果,那么系统会将这种修改视作未定义的行为,因为这会使映射的内部散列失效。这些规则也适用于使用默认 KeyFuncs 时 运算符== 和 GetKeyHash 的重载。

其他

CountBytes 和 GetAllocatedSize 函数用于估计内部数组的当前内存使用情况。CountBytes 接受 Farchive 参数,而 GetAllocatedSize 则不会。这些函数常用于统计报告。

Dump 函数接受 FOutputDevice,并写出关于映射内容的实现信息。此函数常用于调试。

参考

TMap

【UE4 C++ 基础知识】<6> 容器——TMap的更多相关文章

  1. 【UE4 C++ 基础知识】<11>资源的同步加载与异步加载

    同步加载 同步加载会造成进程阻塞. FObjectFinder / FClassFinder 在构造函数加载 ConstructorHelpers::FObjectFinder Constructor ...

  2. 《两地书》--Kubernetes(K8s)基础知识(docker容器技术)

    大家都知道历史上有段佳话叫“司马相如和卓文君”.“皑如山上雪,皎若云间月”.卓文君这么美,却也抵不过多情女儿薄情郎. 司马相如因一首<子虚赋>得汉武帝赏识,飞黄腾达之后便要与卓文君“故来相 ...

  3. Kubernetes(K8s)基础知识(docker容器技术)

    今天谈谈K8s基础知识关键词: 一个目标:容器操作:两地三中心:四层服务发现:五种Pod共享资源:六个CNI常用插件:七层负载均衡:八种隔离维度:九个网络模型原则:十类IP地址:百级产品线:千级物理机 ...

  4. 【UE4 C++ 基础知识】<12> 多线程——FRunnable

    概述 UE4里,提供的多线程的方法: 继承 FRunnable 接口创建单个线程 创建 AsyncTask 调用线程池里面空闲的线程 通过 TaskGraph 系统来异步完成一些自定义任务 支持原生的 ...

  5. 【UE4 C++ 基础知识】<3> 基本数据类型、字符串处理及转换

    基本数据类型 TCHAR TCHAR就是UE4通过对char和wchar_t的封装 char ANSI编码 wchar_t 宽字符的Unicode编码 使用 TEXT() 宏包裹作为字面值 TCHAR ...

  6. 【UE4 C++ 基础知识】<5> 容器——TArray

    概述 TArray 是UE4中最常用的容器类.其速度快.内存消耗小.安全性高. 其设计时未考虑扩展问题,因此建议在实际操作中勿使用 新建(new) 和 删除(delete) 创建或销毁 TArray ...

  7. 【UE4 C++ 基础知识】<7> 容器——TSet

    概述 TSet是一种快速容器类,(通常)用于在排序不重要的情况下存储唯一元素. TSet 类似于 TMap 和 TMultiMap,但有一个重要区别:TSet 是通过对元素求值的可覆盖函数,使用数据值 ...

  8. JAVA基础知识:容器

    JDK所提供的容器都在java.util包里面,下面开始讨论的都是JDK1.4版本的,只讲述基本知识,不涉及泛型 容器API的类图结构如下图所示 Set:元素无顺序且不可重复      List:元素 ...

  9. 【UE4 C++ 基础知识】<8> Delegate 委托

    概念 定义 UE4中的delegate(委托)常用于解耦不同对象之间的关联:委托的触发者不与监听者有直接关联,两者通过委托对象间接地建立联系. 监听者通过将响应函数绑定到委托上,使得委托触发时立即收到 ...

随机推荐

  1. zabbix 历史数据处理

    问题描述 zabbix server 平稳运行有一段时间了,但是最近问题却来了,今天早上收到zabbixserver磁盘空间不足的告警.通过查看之后发现是大部分数据是zabbix 库的的数据 在进一步 ...

  2. 新来的前端小姐姐问:Vue路由history模式刷新页面出现404问题

    摘要:vue-router 默认 hash 模式 -- 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载. 本文分享自华为云社区<学习Vue Rou ...

  3. noip模拟36

    \(\color{white}{\mathbb{荷花映日,莲叶遮天,名之以:残荷}}\) 今天再次翻车掉出前十 开题看错 \(t1\) 以为操作2的值固定发现是个简单题,然后 \(t2\) 开始大力 ...

  4. 20210713考试-2021noip13

    这位巨佬的博客还是比我好多了 T1 工业题 考场: 暴力挺香的,不想正解了. 题解: $f(i,j)$ 只会得到 $f(i-1,j)$ 和 $f(i,j-1)$ 的贡献.每向右一步乘 $a$ ,向下一 ...

  5. Flask(3)- Flask 中的 HTTP 方法

    查看 app.route() 源代码 def route(self, rule: str, **options: t.Any) -> t.Callable: """ ...

  6. windows 安装pip 及更换pip国内源

    一.官网下载压缩包并解压. 官网:https://pypi.org/project/pip/#files 文件:选择.tar.gz版本 image 解压后,进入解压文件目录,在当前路径下打开cmd窗口 ...

  7. Vue Abp vNext获取当前登录用户

    系统默认提供了获取当前用户的api方法 https://localhost:44364/api/identity/my-profile 手工实现方法:abp后台获取当前用户需要在AppService应 ...

  8. Activiti 学习(三)—— Activiti 流程启动并完成

    Activiti 流程启动 流程定义部署后,就可以通过工作流管理业务流程了,也就是说前文部署的出差申请流程可以使用了.针对该流程,启动一个流程表示发起一个新的出差申请单,这就相当于 java 类与 j ...

  9. Nginx-初见

    目录 产品出现瓶颈? 什么是Nginx? Nginx作用 正向代理 反向代理 负载均衡策略(Nignx) 轮询 加权轮询 IP hash 动静分离 参考链接 产品出现瓶颈? 项目刚刚上线的时候,并发量 ...

  10. 编译执行 VS 解释执行

    一般编译程序从对源程序执行途径的角度不同,可分为解释执行和编译执行. 所谓解释执行是借助于解释程序完成,即按源程序语句运行时的动态结构,直接逐句地边分析边翻译并执行.像自然语言翻译中的口译,随时进行翻 ...