[UE5] 언리얼의 스마트 포인터

SmartPointer
avatar
2025.02.20
·
7 min read

TSharedPtr

reference counting 을 사용하여 객체의 수명을 관리한다. 여러 객체가 같은 인스턴스를 공유하며, 마지막 참조가 사라질 때 자동으로 메모리를 해제한다.

선언 및 초기화

null 을 허용하므로 데이터 오브젝트 사용 여부에 관계없이 초기화할 수 있다.

// 빈 SharedPtr 생성
TSharedPtr<FMyObjectType> EmptyPointer;

// 새 오브젝트에 SharedPtr 생성
TSharedPtr<FMyObjectType> NewPointer(new FMyObjectType());

// SharedRef 에서 SharedPtr 생성
TSharedRef<FMyObjectType> NewReference(new FMyObjectType());
TSharedPtr<FMyObjectType> PointerFromReference = NewReference;

// Thread Safe SharedPtr 생성
TSharedPtr<FMyObjectType, ESPMode::ThreadSafe> NewThreadsafePointer =
       MakeShared<FMyObjectType, ESPMode::ThreadSafe>(MyArgs);

// Reset 함수를 사용하거나 nullptr를 할당하며 SharedPtr을 재설정
PointerOne.Reset();
PointerTwo = nullptr;

// MoveTemp 함수를 사용해 다른 SharedPtr로 이동할 수 있다
// PointerOne의 콘텐츠를 PointerTwo로 이동 (PointerOne == nullptr)
PointerTwo = MoveTemp(PointerOne); 

SharedPtr 과 SharedRef 간 변환

SharedRef는 암시적으로 SharedPtr로 변환되며, 새 SharedPtr이 유효한 오브젝트를 참조한다는 것을 보장한다.

TSharedPtr<FMyObjectType> MySharedPointer = MySharedReference;

SharedPtr이 null이 아닌 오브젝트를 참조하면 SharedPtr 함수인 ToSharedRef 를 사용해 SharedRef를 생성할 수 있다. null 인 오브젝트를 참조하는 SharedPtr에서 SharedRef를 생성하려고 하면 프로그램이 assert 를 발생시킨다.

if( MySharedPointer.IsValid() )
{
    MySharedReference = MySharedPointer.ToSharedRef();
}

비교

SharedPtr 이 동일한 오브젝트를 참조하면 같은 것으로 판단한다.

TSharedPtr<FTreeNode> NodeA, NodeB;
if(NodeA == NodeB) { ... }

IsValidbool 연산자는 SharedPtr 가 유효한 오브젝트를 참조하는지 여부를 확인한다. 또한 Get 을 호출하여 유효한 오브젝트 포인터를 반환하는지 확인할 수도 있다.

역참조 및 접근

일반 C++ 포인터와 동일한 방식으로 역참조하고, 메서드를 호출하며, 멤버에 접근할 수 있다. 모든 C++ 포인터와 마찬가지로 역참조하기 전에는 IsValid 함수를 호출하거나 오버로드된 bool 연산자를 사용하여 null 검사를 수행해야 한다.

TSharedRef

참조하는 오브젝트를 소유한다는 면에서 TSharedPtr 와 같은 역할을 하지만, TSharedRef 는 반드시 유효한 객체를 가리켜야 한다. TSharedRef 는 언제나 TSharedPtr 로 변환될 수 있다.

TSharedRef 은 다른 오브젝트를 참조하도록 재설정하거나, null 오브젝트를 할당하거나, 빈 오브젝트를 생성할 수 없다. 항상 유효한 오브젝트가 포함되며, IsValid 메서드도 없다.

선언 및 초기화

null 을 허용하지 않으므로 초기화에는 오브젝트가 필요하다.

TSharedRef<FMyObjectType> NewReference = MakeShared<FMyObjectType>();

TWeakPtr

TSharedPtr 로 관리되는 객체에 대한 약한 참조를 제공한다. 참조 카운트에 영향을 주지 않으면서, 객체의 존재 여부를 확인할 수 있다. 따라서 참조하는 오브젝트의 소멸을 방지하지 못한다. Pin 함수를 통해 TSharedPtr 로 변환 후 안전하게 객체에 접근해야 한다.

선언, 초기화, 할당

// 새 데이터 오브젝트를 할당하고 이에 대한 강한 참조를 만든다
TSharedRef<FMyObjectType> ObjectOwner = MakeShared<FMyObjectType>();
// 새 데이터 오브젝트에 TWeakPtr 생성
TWeakPtr<FMyObjectType> ObjectObserver(ObjectOwner);

ObjectOwnerReset 하면, ObjectObserver 가 범위 내에 있는지 여부에 관계 없이 오브젝트가 파괴된다.

// ObjectOwner가 해당 오브젝트의 유일한 오너라면, 해당 오브젝트가 삭제된다.
ObjectOwner.Reset();

// null 오브젝트를 참조하는 ObjectOwner로 인해 null이 되며, false로 평가된다.
if (ObjectObserver.Pin()){ ... }
// 유효한 오브젝트를 참조하는지에 관계 없이 복사 가능
TWeakPtr<FMyObjectType> AnotherObjectObserver = ObjectObserver;

// 작업이 끝나면 Reset 가능
ObjectObserver = nullptr;
AnotherObjectObserver.Reset();

TSharedPtr로 변환

Pin 함수는 TWeakPtr 의 오브젝트에 대한 TSharedPtr 을 생성한다.

if (TSharedPtr<FMyObjectType> LockedObserver = ObjectObserver.Pin())
{
    // TSharedPtr은 이 범위 내에서만 유효
    LockedObserver->SomeFunction();
}

역참조 및 액세스

Pin 함수를 사용하여 TSharedPtr 로 승격한 후, Get 함수를 통해 액세스해야 한다.

TUniquePtr

유일한 소유권을 가진 스마트 포인터. 오직 하나의 TUniquePtr 만 객체를 소유하며, 소유권 이전만 가능하여 복사를 막으며, 범위를 벗어나면 자동으로 객체를 파괴하여 메모리 누수를 방지한다.

TUniquePtr 가 참조하는 오브젝트에 SharedPtr 또는 SharedRef 를 생성하면 위험하다. 다른 스마트 포인터들이 오브젝트를 여전히 참조하고 있음에도 불구하고, 유니크 포인터가 소멸되면 해당 오브젝트가 함께 소멸되기 때문이다. 마찬가지로, SharedPtr 또는 SharedRef 가 가리키는 오브젝트에 UniquePtr 를 생성하지 않는 것을 권장한다.

주의할 점

UObject 는 언리얼의 GC에 의해 관리되는 객체이므로 일반 C++ 스마트 포인터를 UObject 에 직접 사용할 수 없다. UObject 를 참조할 때는 TWeakObjectPtr 을 사용할 수 있다. UObject 에 특화된 약한 참조로, 객체가 이미 파괴되었는지 안전하게 확인할 수 있도록 돕는다.







- 컬렉션 아티클