Unreal Engine pointers
I come from a background of 10 years of C# programming. One of the more challenging topics to “comprehend” when moving to C++ was definitely the concept of pointers. They seem almost scary to begin with, especially since I had heard so many “horror” stories about pointers. To be honest - it’s not actually that bad once you get the hang of it.
However, there’s a difference between plain C++ pointers, and Unreal Engine pointers. The core concept is the same, but we’re not using the smart pointers from the C++ standard library, but rather Unreal Engine’s own “wrappers” of smart pointers.
Let’s quickly go through what pointers in C++ actually are, why we have them, what they’re used for - and why we love them!
TL;DR
| Type | Use case |
|---|---|
TObjectPtr | Use for UObject derived types |
TSoftObjectPtr | Use for UObject derived types that you want to load asynchronously |
TWeakObjectPtr | Use for UObject derived types that you want to reference without owning |
TUniquePtr | Use for non-UObject types when you want exclusive ownership |
TSharedPtr | Use for non-UObject types when you want shared ownership |
TWeakPtr | Use for non-UObject types when you want to reference without owning |
Raw pointers (*) | Use for temporary references, but avoid for owning references |
C++ pointers recap
Let’s imagine we have a simple variable of type int. Let’s call it Answer and instantiate is with the value of 42 (hopefully you get the reference).
int Answer = 42;
We can convert this into a pointer by using the & operator, which gives us the memory address.
int Answer = 42;
int* pAnswer = &Answer;
We just made a pointer to an int variable, assigned to the memory address for Answer.
What does that mean? Basically, this means that our pointer pAsnwer is storing the location in memory where the value 42 is stored.
Usually the memory address is presented as a hexadecimal number, like 0x6dfed4 as an example.
Pointers are great for performance and efficiency.
Smart pointers
In C++, we also have the concept of smart pointers. Smart pointers are a godsend for memory management, as allocation and deallocation of memory are handled automatically whenever the pointer goes out of scope. This is a huge help for us in preventing memory leaks!
If you want to learn more, do a google search for RAII - or “Resource Acquisition Is Initialization”.
C++ has different kinds of smart pointers, such as std::unique_ptr, std::shared_ptr, and std::weak_ptr.
Each type has their very own use cases and benefits.
unique_ptr is a smart pointer that stores only one pointer at any given time, as is very useful when we have a single owner of a resource.
It’s not possible to copy a unique_ptr, but we can “transfer ownership” of the given resource, by using std::move.
int Answer = 42;
std::unique_ptr<int> p1 = std::make_unique<int>(Answer);
std::unique_ptr<int> p2 = p1; // ⚠️ error: cannot copy unique_ptr
std::unique_ptr<int> p2 = std::move(p1); // transfer ownership
shared_ptr is a smart pointer that allows multiple pointers to share ownership of the given resource.
int Answer = 42;
std::shared_ptr<int> p1 = std::make_shared<int>(Answer);
std::shared_ptr<int> p2 = p1; // ✅ shared ownership
weak_ptr is a smart pointer that holds a so-called “non-owning” reference to the given resource.
It’s used in conjunction with sahred_ptr to prevent circular references.
int Answer = 42;
std::shared_ptr<int> p1 = std::make_shared<int>(Answer);
std::weak_ptr<int> p2 = p1; // ✅ weak ownership
Now let’s put this into the context of Unreal Engine.
Unreal Engine pointers
You might think to yourself; “Great, got it! Let’s use the dandy smart pointers in Unreal Engine!”, and in all fairness, you’re right. There’s just a small difference - Unreal Engine has its own set of smart pointers.
TUniquePtr=std::unique_ptrTSharedPtr=std::shared_ptrTWeakPtr=std::weak_ptr
Why? Because Unreal Engine has its own memory management system, and these Unreal Engine specific pointers are designed to work seamlessly with the memory system.
WARNING: Do not use
TUniquePtr,TSharedPtr, prTWeakPtrforUObjectderived types. Unreal Engine has its own garbage collection system ofUObjectderived types, and using smart pointers can lead to unexpected behavior.
The concept is exactly the same, but the syntax is a bit different.
int32 Answer = 42;
TUniquePtr<int32> p1 = MakeUnique<int32>(Answer);
TUniquePtr<int32> p2 = MoveTemp(p1); // transfer ownership
TSharedPtr<int32> p1 = MakeShared<int32>(Answer);
TSharedPtr<int32> p2 = p1; // shared ownership
TSharedPtr<int32> p1 = MakeShared<int32>(Answer);
TWeakPtr<int32> p2 = p1; // weak ownership
We still have the raw pointers available to us, and they’re still very useful in Unreal Engine. You’ll often use a raw pointer if you’re just observing or referencing an object temporarily, and you don’t want to claim ownership.
void UMyClass::MyFunction()
{
// raw pointer to an actor
AActor* MyActor = GetWorld()->SpawnActor<AActor>(...);
// do something with the actor, but let the world manage its lifetime.
}
UObject pointers
Besides smart pointers, Unreal Engine has their own special types of pointers to UObject derived classes.
TObjectPtrTSoftObjectPtrTWeakObjectPtr.
TObjectPtr kind of acts like a raw pointer, but it has some built-in features that give Unreal Engine more
control and tooling options. It’s bascially Unreal Engine’s “modern” replacement for raw C++ pointers.
Historically, UObject* was used in Unreal Engine, but TObjectPtr is the new recommended way to refernce UObject.
UCLASS()
public AMyActor : public AActor
{
GENERATED_BODY()
// old
UPROPERTY()
UStaticMeshComponent* MeshComponent;
// new
UPROPERTY()
TObjectPtr<UStaticMeshComponent> MeshComponent;
}
TWeakObjectPtr is conceptually the same as (but then again not that same as) std::weak_ptr, but just like the TObjectPtr, it’s designed to work with Unreal Engine’s
memory management. Where std::weak_ptr is used in conjunction with std::shared_ptr, TWeakObjectPtr is used in conjunction with UObject
derived classes.
Where std::weak_ptr requires you to lock the pointer before using it using the lock() function, TWeakObjectPtr has the Get() function,
that returns a raw pointer to the object if it’s still valid, or potentially a nullptr if the object has been destroyed.
Another big difference is std::weak_ptr only works with objects owned by std::shared_ptr - TWeakObjectPtr works with UObject.
// "raw" c++
std::weak_ptr<MyClass> MyClassPtr;
if(std::shared_ptr<MyClass> MyClass = MyClassPtr.lock())
{
// do something with MyClass
}
else
{
// MyClass has been destroyed, handle accordingly
}
// Unreal Engine UObjects
TWeakObjectPtr<AActor> MyActorPtr = ...;
if(AActor* MyActor = MyActorPtr.Get())
{
// do something with MyActor
}
else
{
// MyActor has been destroyed, handle accordingly
}
Hopefully this gives you a better understanding of pointers in the context of Unreal Engine.
I know I struggled with it to begin with, as there’s a difference in “raw C++” pointers, and the pointer “wrappers” you use in Unreal Engine.
They’re conceptually the same, but they do differ in use cases and syntax, and also have the concept of UObject to consider in the mix.
Happy dev’ing!