지난번에는 C++의 스마트 포인터를 알아보았는데 이번에는 언리얼의 스마트 포인터에 대해 알아보자!
2024.07.01 - [C++] - #include Smart Pointer
언리얼에서 지원하는 포인터는 Template으로 정의되어 있으며 총 4가지의 포인터를 지원한다.
TSharedPtr, TSharedRef, TWeakPtr, TUniquePtr
주의할 점은 언리얼의 포인터는 UObject가 아닌 개체에만 사용할 수 있다.
그 이유는 UObject 개체들은 UObject Handling System으로 관리되어 Garbage Collecting이 되기 때문!!
Shared Pointer (TSharedPtr) : 참조하는 개체를 소유하고 Shared Pointer나 Shared Reference에서
참조하지 않으면 제거한다. nullptr가 가능하다.
Shared Reference (TSharedRef) : 참조하는 개체를 소유하지만 nullptr가 불가능하다.
그래서 Shared reference는 항상 유요한 포인터를 가지고 있는 걸 보장한다.
Weak Pointer (TWeakPtr) : 참조하는 개체를 소유하지 않기에 생명 주기에 영향을 주지 않는다.
갑자기 null이 되어버릴 수도 있다.
Unique Pointer (TUniquePtr) : 참조하는 개체를 유일하고 명시적으로 소유한다. 소유권 공유가
불가능하지만 소유권 이전은 가능하다. *유니크 포인터는 Scope ( {} 중괄호 영역 )을 벗어나면
자동 소멸되므로 Unique Pointer가 참조하는 개체를 Shared Pointer나 Shared Reference에서
참조하도록 하지 않도록 해야 한다. Unique Pointer에서는 다른 곳에서 참조하는 걸 확인하지 않기 때문이다!
*Unique Pointer가 참조하는 개체를 Shared pointer로 참조하여
오류가 발생하는 예제를 만들고 싶었는데 크래쉬 발생했다.. ㅋ
로그를 보면 Unique Pointer2 test에서 Shared Pointer가 비어있다고 하고
Unique Pointer가 참조하는 개체가 파괴될 때 두 번 파괴되는 오류가 발생한다.
그 이유는 첫 번째 함수에서 만든 SharedPtr는 TestUniquePointer1() 함수 내에서만 존재하기 때문에
범위를 벗어나면 소멸된다. 그래서 유니크포인터를 참조한 쉐어드 포인터는 이미 소멸된 개체를
다시 소멸시키려고 해서 오류가 발생한다.
크래쉬가 발생하는 상황을 만들지 않고 스마트 포인터의 예제를 만들어보았다.
// Called when the game starts or when spawned
void ASmartPointer::BeginPlay()
{
Super::BeginPlay();
TestUniquePointer1();
TestSharedPointer1();
}
void ASmartPointer::TestUniquePointer1()
{
// Unique pointer
TUniquePtr<FDataClass> UniquePointer = MakeUnique<FDataClass>(TEXT("Unique"));
UE_LOG(LogTemp, Display, TEXT("=== Unique Pointer1 Test ==="));
if(UniquePointer)
{
UE_LOG(LogTemp, Warning, TEXT("Unique Pointer Value: %s"), *UniquePointer->Value);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Unique Pointer is empty!"));
}
UE_LOG(LogTemp, Display, TEXT("=== Unique Pointer1 Test ==="));
}
void ASmartPointer::TestSharedPointer1()
{
// Shared pointer
TSharedPtr<FDataClass> SharedPointer(new FDataClass(TEXT("Shared")));
if(SharedPointer.IsValid())
{
UE_LOG(LogTemp, Warning, TEXT("Shared Pointer Value: %s"), *SharedPointer->Value);
TestSharedPointer2(SharedPointer);
UE_LOG(LogTemp, Warning, TEXT("TestSharedPointer2 end. Shared Pointer Used : %d"), SharedPointer.GetSharedReferenceCount());
TestSharedRef(SharedPointer);
UE_LOG(LogTemp, Warning, TEXT("TestSharedRef end. Shared Pointer Used : %d"), SharedPointer.GetSharedReferenceCount());
TestWeakPointer(SharedPointer);
UE_LOG(LogTemp, Warning, TEXT("TestWeakPointer End. Shared Pointer Used : %d"), SharedPointer.GetSharedReferenceCount());
}
UE_LOG(LogTemp, Warning, TEXT("Shared Pointer Used : %d"), SharedPointer.GetSharedReferenceCount());
}
void ASmartPointer::TestSharedPointer2(TSharedPtr<FDataClass> SharedPtr)
{
// Shared pointer
TSharedPtr<FDataClass> SharedPointer = SharedPtr;
if(SharedPointer)
{
UE_LOG(LogTemp, Display, TEXT("=== Shared Pointer2 Test ==="));
UE_LOG(LogTemp, Warning, TEXT("Shared Pointer2 Value: %s"), *SharedPointer->Value);
UE_LOG(LogTemp, Warning, TEXT("Shared Pointer2 Used : %d"), SharedPointer.GetSharedReferenceCount());
UE_LOG(LogTemp, Display, TEXT("=== Shared Pointer2 Test ==="));
}
}
void ASmartPointer::TestSharedRef(TSharedPtr<FDataClass> SharedPtr)
{
// Shared references
TSharedRef<FDataClass> SharedReference = SharedPtr.ToSharedRef();
UE_LOG(LogTemp, Display, TEXT("=== Shared Referece Test ==="));
UE_LOG(LogTemp, Warning, TEXT("Shared Reference Value: %s"), *SharedReference->Value);
UE_LOG(LogTemp, Warning, TEXT("Shared Reference Used : %d"), SharedReference.GetSharedReferenceCount());
UE_LOG(LogTemp, Display, TEXT("=== Shared Referece Test ==="));
}
void ASmartPointer::TestWeakPointer(TSharedPtr<FDataClass> SharedPtr)
{
// Weak pointer
TWeakPtr<FDataClass> WeakPointer(SharedPtr);
if(WeakPointer.IsValid())
{
UE_LOG(LogTemp, Display, TEXT("=== Weak Pointer Test ==="));
UE_LOG(LogTemp, Warning, TEXT("Weak Pointer Value: %s"), *WeakPointer.Pin()->Value);
UE_LOG(LogTemp, Warning, TEXT("Weak Pointer Used : %d"), SharedPtr.GetSharedReferenceCount());
UE_LOG(LogTemp, Display, TEXT("=== Weak Pointer Test ==="));
}
}
요렇게 출력이 된다!
Shared Pointer Test때에는 3개의 참조가 있고 Weak Pointer Test때에는 2개의 참조가 있다.
Weak Pointer는 자신이 가지고 있지 않고 접근만 가능하기 때문이다.
Shared Pointer의 개수가 Test 함수 안에서는 3개의 참조이고 나오면 하나의 참조라고 나오는데..
흠.. 처음에 생각했을 때는 2개가 되어야 하지 않나 했는데 3개가 맞았다.
(Weak Pointer의 참조 개수도 마찬가지로 1개가 아니라 2개가 맞다.)
처음에 선언한 SharedPointer 참조 1개
요렇게 함수를 호출했을 때 함수 내부에서 선언하고 할당한 SharedPointer 1개
그리고 함수를 호출했을 때 argument로 전달한 SharedPtr 1개 해서 총 3개의 참조가 생기는 것이다.
음.. 아직은 스마트 포인터를 어디서 사용하면 좋을지 떠오르지 않지만
프로젝트를 만들 때 한번 써보도록 해야겠다
참고 링크 :
'Unreal Engine > UE5' 카테고리의 다른 글
UE5 - 반사 시스템 (Reflection System) :: 리플렉션 시스템 ? 🌈 (0) | 2024.07.26 |
---|---|
UE5 - 문자열 손질 (String Handling) :: FName vs FString vs FText🔡 (2) | 2024.07.16 |
UE5 - What is Outer, Onwer ? GetOuter() & GetOwner() (2) | 2024.04.20 |