結論
TArrayの要素が更新された場合、TArrayのアドレスの再配置が行われるが参照で持っているアドレスは更新されない。
不正アクセスした際の挙動がわかりにくくなるため、スマートポインタで持っておくとよい。
どういうことか
UYourActor.h
USTRUCT()
struct FMyStruct
{
GENERATED_BODY()
FString MemberStr = TEXT("Member");
};
UCLASS()
class MYPROJECT_API UMyObject : public UObject
{
GENERATED_BODY()
private:
TArray<FMyStruct> MyStructs;
public:
void RemoveLastElement()
{
MyStructs.Remove(MyStructs.Last());
}
};
こういうクラスがあるとする。
FMyStruct& LastElementRef = MyStructs.Last();
RemoveLast();
// TArrayを変更したので要素のアドレスが変わった状態で参照にアクセス(不正アクセス)
UE_LOG(Log,Log, TEXT("%s"), *LastElementRef.MemberStr);
上記のように直接参照を受け取ると不正アクセスした時の壊れ方がややこしくなる。
TArrayの要素に変更があった場合でもアドレスが更新されず、参照が残る。
エディタではうまく動いていたがパッケージで突然変な動作になったり、エディタ実行でブレークポイントを使ってステップ実行すると正常動作するがブレークポイントを使わないと正常動作しない、といった事が起こる。
正常に動いているように見える場合もFMyStruct
を継承した構造体のデータにだけアクセスできなかったりする。
// 参照に対するポインタを指すポインタを作成する
TSharedPtr<FMyStruct*> LastElementPtr = MakeShared<FMyStruct*>(&MySructs.Last());
// 参照に対するポインタを指すポインタの実体 = 参照を取得
FMyStruct& LastElementRef = **LastElementPtr.Get();
RemoveLast();
// TArrayを変更したので要素のアドレスが変わった状態で参照にアクセス(不正アクセス)
UE_LOG(Log,Log, TEXT("%s"), *LastElementRef.MemberStr);
上記のようにスマートポインタを経由するとアドレスの更新が反映される。
比較的わかりやすく壊れてくれることが多い。