Why should I replace raw pointers with TObjectPtr?

I am reading the UE5 migration guide (Unreal Engine 5 Migration Guide | Unreal Engine 5.0 Documentation), and I see that TObjectPtr is now recommended over raw pointers. But the only thing the guide says about what this new type does is this:

This system adds dynamic resolution and access tracking in editor builds

What exactly does “dynamic resolution” and “access tracking” mean? How is this beneficial to me? The rest of the migration guide talks about how and where to use them, but doesn’t explain what functionality they provide. I went into the TObjectPtr header file (ObjectPtr.h) to look for more information, but unfortunately I didn’t see any useful comments on what they do.

Maybe I missed a page in the documentation that really went into the benefits of this change? I don’t feel convinced that I should make this change to my project. My project seems to work fine without them, and wrapping my pointers in TObjectPtr will not make my code more readable.

5 Likes

“Access tracking” means that you can actually detect when the object is being used. This can be used for example in deciding which objects to put where in an input stream, to put objects in the order they will be accessed. (Dunno if they do exactly that right now, but that kind of thing is often very helpful!)

Presumably they can also add checks in editor/debug modes where if you were to assign some invalid pointer to the value, you’d get an assert right where it happens, rather than a crash sometime much later when you try to use the bad pointer. (You’d get this from bad casts etc.)

Having worked on reflection systems of various kinds, I’m actually surprised they’ve managed to push raw pointers as far as they have. That would never have been possible without the UnrealHeaderTool, but even with that, it looks to me like an old habit that needs to go away.

7 Likes

Well the most basic and fundamental incentive for doing so is because Epic has decided to adopt this standard for pointers. TObjectPtr is a template made by Epic which means it gives Unreal Engine easy access to all variables that use that template. This in turn means that whatever new feature/code/system they design will probably assume that you are using TObjectPtr’s and hence adopting this new standard is important for ensuring your code is compatible with future releases and everything is funcitoning as intended.

1 Like

@Navhkrin

Well the most basic and fundamental incentive for doing so is because Epic has decided to adopt this standard for pointers

Sure, that’s very apparent. However, consider these three snippets from the migration guide:

TObjectPtr … an optional replacement for raw object pointers in editor builds

Although it is optional, we recommend using TObjectPtr<T> over T*

TObjectPtr … may improve your experience when developing in editor builds

So, the gist of what I’ve found out is its not mandatory (this is apparent since my build still runs without adding them), and it ‘may’ help interacting with the editor (I’m interested in this). I just would really like to know some more details to motivate me to make this change.

2 Likes

In the first paragraph in the the “Mandatory Updates” section:

Some of these changes are mandatory, while others are optional but recommended in UE5EA, and will become mandatory in future versions of UE5.

5 Likes

Good catch! If I must do it, then I must do it lol

2 Likes

I wonder if this means that the editor will crash less when reloading C++ libraries, or force-deleting assets? That’d be nice.

1 Like

TObjectPtr also ensures the pointer is initialized.

Found an issue caused by a non-null initial value in a marketplace plugin the other day, in fact.

(I realize this thread is old, but it’s one of the top google links for ‘TObjectPtr ue5’, so…)

(Note that UObject’s are memset zero-initialized anyways, but the same isn’t true for UStructs.)

10 Likes

Honestly that is the main reason to use it. Creating new member pointer variables you can quickly forget initializing it with a nullptr.

Sadly Visual Studio based builds don’t really track this… I saw so many issues between debug and release builds during the past years.

5 Likes

TObjectPtr can represent a reference as a handle. That means it opens some options for lazy loading (but editor only, packaged game remains unchanged). When this TObjectPtr/FObjectPtr is seen it does not necessarily need to be resolved at the same time. It can be kept around as a handle and resolved later, when really needed.

Look for LazyOnDemand and TODO: OBJPTR in the engine source. It seems to be tied to TObjectPtr.

In 5.0, USoundWaves have meta=(LoadBehavior = “LazyOnDemand”)
I saw it used also for data assets and other classes in ue5-main branch.

I don’t think it changes initialization in any way. UE5 has a different, more verbose check for uninitialized pointers but it is not related to TObjectPtr. It is used even if you use standard pointers.

It seems using TObjectPtr can lead to better editor time experience like faster asset loads.

TODOs reveal also some potential future work:

// @TODO: OBJPTR: Investigate TObjectPtr support for UFunction parameters.

// @TODO: OBJPTR: we want to permit lazy background loading in the future
// else if (*LoadBehaviorMeta == "LazyBackground")
// {
// 	LoadBehavior = EImportLoadBehavior::LazyBackground;
// }

// @TODO: OBJPTR: Need to find other options for solving this issue of placeholder classes during blueprint compile without forcing all imports to resolve always

// @TODO: OBJPTR: We should have a test that ensures that lazy loading of an object with an external package is handled correctly.

... and many more
3 Likes

So this discussion is the first thing that pops up for me when I do a google search.

I wanted to provide some context for anyone trying to understand this.

Why should you use TObjectPtr?
Simply stated, Null pointer errors will be easier to debug when you are compiling non-shipping builds.

What are the downsides?
A few more instructions to execute because of pointer indirection in editor builds.
Other than that, there is no downside.
TObjectPtr is converted into raw a raw pointer for shipping builds, so there is no performance penalty.

To sum up, using TObjectPtr will alert you to the fact you’re using an uninitialized raw pointer more easily, and there is NO performance penalty in shipping builds.

20 Likes

Should we also be using TObjectPtr for more than just pointer declaration? Should we be using it everwhere, for example, return values. Replace this:
AActor* GetWeapon() const;
With this:
TObjectPtr<AActor> GetWeapon() const;

1 Like

No. While I still don’t think Epic’s made it clear why one should use TObjectPtr instead of raw pointers, the one thing I think is clear from them is that it’s only useful for members. There’s no real reason to use them in function signatures or as local function variables.

An Epic docs page

Or Tom Looman is pretty good too (you’ll have to scroll down just a little though).

7 Likes