How does Unreal's smart pointer library work with UObjects? Let's establish a clear distinction.
Your might have heard the story about how using Unreal's smart pointers (
TUniquePtr) causes unexpected results on
UObjects, because the
UObject memory management system is geared for game code, which is much more garbage collection oriented.
A clear distinction between what is exposed to the uobject memory management system and what not must not be foreign. That's the reason this article exists.
Drawing the line
|Not compatible with UObject||
|Compatible with UObject||
TWeakObjectPtr doesn't prevent GC and becomes NULL when the underlying object is destroyed. It's a great way to add detail on how you're using it. Most likely as an observer, while not 'telling the engine it should not be GC'ed because your
UPROPERTY() marked field is storing the address.'
A great example of where
TWeakObjectPtr shines is its usage by the series of
DECLARE_DYNAMIC_ delegate variants. (It actually uses the non-generic
FWeakObjectPtr, which implies
The macros that declare a dynamic delegate create a class that inherits from
TBaseDynamicDelegate, which inherits from
TScriptDelegate stores the object bound to the delegate as
FWeakObjectPtr, which makes a lot of sense if you think about how you would logically expect the ownership to be handled.
More ways the engine uses TWeakPtr or TSharedPtr
The primary way
UGameplayTagManager stores and retrieves gameplay tags is by storing them as nodes in a tree-like fashion, parallel to how gameplay tag strings maintain hierarchy with dot delimiters.
Those nodes are stored as
TSharedPtr<FGameplayTagNode>, which reminds me of how
std::shared_ptr is commonly used for creating tree-like relationships.
After all, lots of back-end modules make use of
TWeakPtr for doing all kinds of non-gameplay related things. You'd also see
TWeakPtr being used on
FGCObjects once in a while.
Let's not forget mentioning
TSharedFromThis, which is the class many classes derive from to directly get an instance as
It's used extensively by many things. Again, not really related to tangible gameplay mechanics, or a part of the entire surface-level Player Controller, Player State, Pawn, etc. paradigm.
TSharedPtris excellent for managing the lifetime of tree-like nodes. These nodes can't simultaneously participate in Unreal's separate memory tracking system geared for game-code, thus would preferably not be
UObjects or classes derived from
TWeakPtrfor classes derived from
FGCObjectworks well, while using
TSharedPtrcreates tricky situations.
- Garbage collection reliance is the tradeoff made for attaining an easy scripting experience, as the allocated memory gets handled automatically and intelligently.
- Pointers that aren't marked with
UPROPERTY()are not exposed to the reflection system, thus won't explicitly be set to
nullptrwhen the address is no longer valid.
IsValid()could crash the game; it might be a dangling pointer because it wasn't explicitly set to
- If you're storing the address of garbage collectable class instances through a pointer, it's great to mark that field with
UPROPERTY(), so the reflection system can null it for you, making comparisons to
nullptra reliable way to test the existance in memory.
nullptrchecks are somewhat relatively speaking low-level. Use global
bool IsValid(const UObject* Test)if you want to combine a slightly higher level
IsPendingKillflag check, which is a more streamlined approach for game code.)