UE5默认C++17,禁用std::unique_ptr管理UObject因GC冲突;C++20仅限非UCLASS模块;范围for需用const引用避免拷贝;蓝图函数参数须用TArray/TMap而非span/View。
Unreal Engine 5 默认使用 C++17(UE 5.3 及以后可选 C++20),但引擎自身大量封装了标准库、禁用部分语言特性,并强制使用其内存/对象生命周期管理机制。直接套用现代 C++ 惯例(如 std::shared_ptr、auto 推导过度、RAII 构造函数抛异常)极易引发崩溃或资源泄漏。
std::unique_ptr 管理 UObject 子类?UE 的垃圾回收(GC)系统依赖 UObject 的反射信息和根引用链,而 std::unique_ptr 是纯栈/堆语义,不参与 GC 注册。一旦你用它持有 UCLASS 实例,GC 会认为该对象不可达并销毁它,而 std::unique_ptr 还在试图管理已释放内存 —— 下次访问必崩。
正确做法是用 UE 原生指针类型:
TObjectPtr(推荐,UE 5.2+ 引入,类型安全、支持 GC、可序列化)UActorComponent*(需手动确保生命周期,例如在 BeginDestroy() 中置空)UActorComponent** 或裸 void* 转换UE 5.3+ 支持 C++20,但仅限非 UCLASS / USTRUCT 的普通模块(如 Runtime/Core 或自建工具模块)。在 Build.cs 中设置需谨慎:
立即学习“C++免费学习笔记(深入)”;
public override void SetupBinaries(
target,
ref List OutBinaries
)
{
base.SetupBinaries(target, ref OutBinaries);
// 仅对非UObject模块启用C++20
if (Target.Type != TargetType.Editor && !IsInUObjectModule())
{
CppSettings.LanguageStandard = CppLanguageStandard.Cpp20;
}
}
关键限制:
concepts、ranges、modules 在当前 UE 构建系统中仍不被支持(会触发 Unknown type name 'concept')std::format 不可用(MSVC 2019 工具链未完全实现,且 UE 自带 FString::Printf 替代)UCLASS 头文件必须保持 C++17 兼容(否则反射生成器 UnrealHeaderTool 解析失败)auto 和范围 for 在容器遍历时要注意什么?UE 容器(TArray、TMap、TSet)重载了 begin()/end(),支持范围 for,但推导类型容易出错:
// ❌ 危险:TArray的 value_type 是 FString,但 operator[] 返回的是 FString& TArray Names = {TEXT("A"), TEXT("B")}; for (auto Name : Names) // 复制每项!大字符串开销高 { DoSomething(Name); } // ✅ 推荐:显式 const 引用避免拷贝 for (const FString& Name : Names) { DoSomething(Name); } // ✅ 或用索引(当需要下标时) for (int32 i = 0; i < Names.Num(); ++i) { DoSomething(Names[i]); }
额外注意:
TMap 的范围 for 迭代的是 TPair,不是 ValueType
Add() 或 RemoveAt() —— UE 容器不保证迭代器失效行为与 STL 一致,可能跳项或崩溃UE 的 UFUNCTION 反射不识别模板、概念或复杂别名。以下写法均无效:
UFUNCTION(BlueprintCallable) void SetData(TArrayView Data); (TArrayView 不被蓝图识别)UFUNCTION(BlueprintCallable) void Process(std
::span Values); (std::span 无反射信息)UFUNCTION(BlueprintCallable) void Configure(const TMap& Config); (引用参数蓝图无法传入)可行方案:
const TArray& 、const TMap& —— UE 会自动转换蓝图数组/字典UPARAM(Ref) 标记的非 const 引用(如 UPARAM(Ref) TArray& OutPoints )TArrayView,但签名仍用 TArray
例如:
UFUNCTION(BlueprintCallable, Category = "Math") void NormalizeVectors(const TArray& InVectors, TArray & OutNormalized); // 内部可写:TArrayView View(InVectors);
最常被忽略的一点:UE 的 USTRUCT 成员变量即使声明为 const,也无法阻止蓝图或序列化修改它 —— 反射系统绕过 const 限定。真正想保护数据,得靠逻辑层校验或私有变量 + 公共 getter。