Hey!👋 ubyte.dev just recently launched. It's dedicated towards Unreal Engine 5.

TWeakPtr and TWeakObjectPtr compatibility with UObject

August 28, 2022
Lucas Tchikhinachvili
How does Unreal's smart pointer library work with UObjects? Let's establish a clear distinction.

UObject

Your might have heard that Unreal's smart pointers (TSharedPtr, TSharedRef, TWeakPtr, TUniquePtr) should not be used 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 should not be foreign. That's the reason this article exists.

Drawing the line

Not compatible with UObject
Compatible with UObject
TSharedPtr, TSharedRef, TWeakPtr, TUniquePtr

TWeakObjectPtr

TWeakObjectPtr doesn't prevent Garbage Collection and always returns NULL when the underlying object is destroyed (regardless of being marked with UPROPERY(), in case you wonder).

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 UObject only.)

The macros that declare a dynamic delegate create a class that inherits from TBaseDynamicDelegate, which inherits from TScriptDelegate. 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:

The binding should not prevent the object from being destroyed!

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 their 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 engine modules make use of TSharedPtr and TWeakPtr for managing non-uobject classes, but those classes cannot participate in unreal separate memory tracking system geared for game code.

Let's not forget mentioning TSharedFromThis, which is the class many classes derive from to directly get an instance as TSharedRef.

It's used extensively by many things which are rarely related to the entire surface-level Player Controller, Player State, Pawn, etc. paradigm. 

Key takeaways

  1. TSharedPtr is 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 should not be UObject.

  2. Garbage collection reliance allows an easy scripting experience, as the allocated memory gets handled automatically and intelligently.

  3. Raw pointers that aren't marked with UPROPERTY() are not exposed to the reflection system, and thus won't explicitly be set to nullptr when the address should no longer be accessed. IsValid() could crash the game; it might be a dangling pointer because it wasn't explicitly set to nullptr.

  4. Suppose you're storing the address of garbage collectable class instances through a pointer. In that case, it's great to mark that field with UPROPERTY(), so the reflection system can null it for you, making nullptr a reliable way to test the validity of an object. IsValid() is a more streamlined approach for game code, as it combines some flag tests such as IsPendingKill (or IsGarbage since UE5).

cross linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram