UE의 스마트 포인터 라이브러리에는 아래 4가지 스마트 포인터가 있습니다.
- TSharedPtr
- TSharedRef
- TWeakPtr
- TUniquePtr
위 스마트 포인터들은 메모리 할당과 추적의 부담을 해소해주도록 설계된 C++11의 스마트 포인터의 커스텀 구현입니다.
위 스마트 포인터들은 언리얼 오브젝트(UObject)에 대해 사용할 수 없습니다.
언리얼 오브젝트(UObject)는 별도의 메모리 추적 시스템(GC)를 사용하기 때문입니다.
(UObject GC에 의헤 TSharedPtr로 관리됩니다. 그래서 TSharedPtr로 UObject를 가르킬 수 없습니다. => 사이클 발생)
TSharedPtr
위에서 설명한것과 같이 TSharedPtr은 UObject를 가르킬 수 없습니다.
UObject를 가르키고 싶다면 TWeakObjectPtr을 사용해야합니다.
즉 Native 객체 사용할 수 있습니다.
TSharedPtr은 객체의 참조를 카운팅하며 참조 카운트가 0이되면 소멸됩니다.
주의하실 점은 C++11의 shared_ptr과 똑같이 동일한 객체를 참조하는 경우 사이클 문제가 발생하여 절대 메모리가 해제 되지 않습니다!
언리얼 엔진의 GC시스템은 UObject를 위한 것입니다. 그래서 UObject는 이미 TSharedPtr로 메모리가 관리되기 때문에
TSharedPtr로 UObject를 가르키려 한다면 UObject해제시 크래시가 발생합니다.
TSharedRef
TSharedPtr과 동일하지만 항상 nullptr이 될 수 없는 오브젝트를 참조해야합니다.
언제든 TSharedPtr이 될 수 있고 참조된 오브젝트가 null이 불가능하다면 TSharedRef를 사용합니다.
TUniquePtr
개념은 unique_ptr과 같습니다.
하나의 포인터로 하나의 메모리를 다룬다는 개념입니다.
여러 unique_ptr이 하나의 메모리를 참조할 수 없고 하나의 포인터가 하나의 메모리를 소유하며 소유권을 공유하지 못하지만 이전은 가능합니다.(Move 가능)
// 생성
TUniquePtr<FString> myString = TUniquePtr<FString>();
TUniquePtr<FMyBox> Box; // 선언
Box = MakeUnique<FMyBox>(10, 20); // 초기화
// 이동
TUniquePtr<FString> youString = MoveTemp(myString);
마찬가지로 UObject를 가르키면 안됩니다!
생각해보면 TUniquePtr이 참조하는 객체(A)에 대해(오브젝트에 대해) TSharedPtr을 생성(B)한다고 가정을 하고
A를 소멸시키면 TUniquePtr은 사라지지만 B객체는 여전히 A를 가르키는 상황이 발생합니다. (댕글링 포인터)
예를 들어, 우리가 유니크 포인터를 통해 어떤 오브젝트를 관리하고 있다고 가정합시다. 그런데 우리가 이 오브젝트에 대한 쉐어드 포인터를 만들었다고 합시다. 이제 두 개의 스마트 포인터가 동일한 오브젝트를 가리키고 있게 되었습니다.
이 상황에서, 유니크 포인터가 먼저 소멸된다면 어떻게 될까요? 유니크 포인터는 소멸 시점에 자동으로 그것이 가리키는 오브젝트를 삭제합니다. 그러나 쉐어드 포인터는 여전히 그 오브젝트를 가리키고 있기 때문에, 이제는 없어진 오브젝트에 접근하려고 시도할 것입니다. 이는 잘못된 메모리 접근으로 이어지며, 이는 프로그램의 크래시를 유발할 수 있습니다.
TWaekPtr
참조 카운팅에 관여하지 않습니다.
참조 대상이 소멸될 경우 자동으로 nullptr이 됩니다.
참조 대상의 유효성을 검사할 수 없습니다.
TSharedPtr의 사이클 문제를 해결하기 위해 사용하며, 메모리를 참조하더라도 참조 카운팅이 증가하지 않고 객체의 라이프 사이클에 관여하지 않습니다.
따라서 TWaekPtr은 참조하는 대상이 소멸되는 경우 자동으로 nullptr이 됩니다.
따라서 참조 대상의 유효성을 IsValid() 함수로 확인후에 사용해야합니다.
// 생성
TSharedPtr<FMyData> MyData = MakeShareable(new FMyData());
TWeakPtr<FMyData> MyDataWeakPtr(MyData)
// 유효성 확인
if(MyDataWeakPtr.IsValid())
{
MyData->a = 10;
}
// TSharedPtr로 변환
TSharedPtr<FMyData> MyDataSharedPtr(MyDataWeakPtr.Pin());
TWeakObjectPtr
TWeakPtr과 동일한데 차이점은 TWeakObjectPtr은 UObject를 다룰 수 있다는 점입니다.
TWeakObjectPtr는TSoftObjectPtr에서도 사용이 되는데 TSoftObjectPtr는 FSoftObjectPath를 감싸고 있는 TWeakObjectPtr이며 경로 , 해당 에셋이 메모리에 존재하는지 확인 가능한 TWeakObjectPtr입니다.
// OK
TWeakObjectPtr<UMyObject> WeakObjectPtr(NewObject<UMyObject>());
// 컴파일 에러
TWeakPtr<UMyObject> WeakPtr(NewObject<UMyObject>());
https://docs.unrealengine.com/5.1/ko/smart-pointers-in-unreal-engine/
https://devjino.tistory.com/254
'UE5' 카테고리의 다른 글
[CPP] static_assert (1) | 2024.02.07 |
---|---|
[UE] Lyra ExperienceManagerComponent::StartExperienceLoad (1) | 2024.01.25 |
[UE] C1189 에러 해결방법 (0) | 2024.01.22 |
[UE] IsA (0) | 2024.01.20 |
[UE] UObject::CreateUObject Delegate 바인딩 언제, 어디서, 왜 쓰는지 (0) | 2024.01.05 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!