How to properly use static in classes in UE?

Hey, so I’m still in the process of learning C++ and recently learned to clean up a lot of my memory allocation by using static in order to create a shared variable for all instances of my objects.

Currently all my NPCs and enemies have their own variable holding the the player pawn. However, I obviously only need one universal reference, since the player is the player pawn and it never changes in my game. (and it’s single player)
So I tried making my variable static, and declaring at the top of the .cpp file.
static AWSPlayer *Player;
AWSPlayer* AWSNPC::Player;

The first entity to spawn gets and sets the player variable:

void AWSNPC::BeginPlay()
{
	Super::BeginPlay();
	if (!Player)
		Player = Cast<AWSPlayer>(UGameplayStatics::GetPlayerPawn(GetWorld(), 0));

This works perfectly fine the first time I run the game. All entities use the variable as expected.

However, it appears that after closing the game in editor or changing maps, my player doesn’t appear to get emptied, and it refers to my previous value, causing a crash as that version of the player no longer exists. How do I make sure it always sets the Player variable, but only once?

Why are you using static? why are you worrying about memory allocation in UE4 with UObjets? And especially actors.

What you are doing is wrong.

Take a read here: Unreal Object Handling | Unreal Engine Documentation.

But your Player pointer should be NON static, and have UPROPERTY above it so the garbage collection can set it null if the player ever gets destroyed.

Don’t fight the engine by doing stuff that is really not the intended way.

Ah, I’ve been learning general C++, I’m passingly aware of garbage collection but not enough to realize the engine is already handling these kinds of things.

Another piece of advice would be start getting used to the Smart Pointer Library, it will get you out of trouble ahead, so get used on how to use it and also it is a very good idea to check some engine source code for plugins, it will be a great benefit for you.

1 Like

Thanks! I was able to get something going, but I’m probably doing it wrong.

It currently works, but crashes on exit (it doesn’t technically matter since the process has ended anyway, but I would like to get it right).

TSharedPtr<AWSPlayer> Player;

	if (!Player)
		Player = MakeShareable(Cast<AWSPlayer>(UGameplayStatics::GetPlayerPawn(GetWorld(), 0)));
Unhandled Exception: EXCEPTION_ACCESS_VIOLATION 0x0000074d1aeaa800

UnrealEditor_Winstick_Win64_DebugGame!SharedPointerInternals::TReferenceControllerWithDeleter<AWSPlayer,SharedPointerInternals::DefaultDeleter<AWSPlayer> >::DestroyObject() [Z:\Unreal\UE_5.0EA\Engine\Source\Runtime\Core\Public\Templates\SharedPointerInternals.h:116]
UnrealEditor_Winstick_Win64_DebugGame!TSharedPtr<AWSPlayer,0>::~TSharedPtr<AWSPlayer,0>()
UnrealEditor_Winstick_Win64_DebugGame!AWSNPC::~AWSNPC()
UnrealEditor_Winstick_Win64_DebugGame!AWSNPC::`vector deleting destructor'()

UObjects are managed objects, so by the time the smart pointer tries to destroy the object, the garbage collector has already destroyed it.
If you’re using UObjects you need to use the built-in ways to create/destroy them, that’s NewObject<>() and CreateDefaultSubobject<>().
For non-UObject types, you can use new/delete, smart pointers etc. and that’s fine.

1 Like

Oh, the object I’m trying to create a shared pointer to is my Player’s pawn. I’m not trying to make a new UObject. Is maybe what I need a weak pointer instead since it already exists? I wasn’t able to figure out how to assign it though.

Basically I’m just trying to figure out how to make universal pointers to unique instances, instead of allocating memory to every single object that needs a reference to them.

I think you need to check through engine source code and realize that several classes are subclasses of UObject, which includes Pawn. When you need a reference to an object (which is alive) you are only having a pointer and that’s it, you are not allocating memory more than the enough to handle a memory address.

What you can do is just hold the refence inside any object that needs it. The info passed by Bolobolo is accurate.

A pointer is 8 bytes of memory. Using 130.000 unique pointers adds one MB of additional memory to your game. My advice: Do youself a favor and abandon that task. The gains don’t justify the amount of work you’re putting in and have already put in.

2 Likes

Cheers, I guess I’ve been diving too deep into general C++ instead of keeping the perspective of working within an established engine.

You can use TWeakObjectPtr in your header like this
TWeakObjectPtr<APawn> SomeActor;

Assign it like this: IsValid() should return false if the actor was destroyed.

if(!SomeActor.IsValid()) 
{
	SomeActor = UGameplayStatics::GetPlayerPawn(GetWorld(), 0);
}

You can dereference it like this:
APawn* ActorPtr = SomeActor.Get();

or using the “->”
SomeActor->GetActorLocation();

1 Like

Why would you use TWeakObjectPtr in an actor class? Sure in a struct or some class which is not reflected. But there is 0 reason to use it in an Actor class. A UPROPERTY() raw pointer to an actor inside a reflected class is more than enough.

Just adding complexity to a non issue.

Why not, smart pointers are very powerful and are used throughout the engine, take for example FHitResult you get from doing a line trace, it has a bunch of TWeakObjectPtr variables, including the Actor hit by the trace is also a TWeakObjectPtr.

It has additional cost overhead, and it is not needed in this case.

Weakpointers only prevent an object from being reference counted. Actors are different, and they get explicitly destroyed by the world and the object is marked pending kill. Raw pointers will not keep this object alive. If you are holding a pointer to an Actor inside another another actor/actorcomponent, then weak pointer is not needed.

The property must be UPROPERTY so the reflection system and GC can see that property. But the GC will turn that property nullptr when the actor is destroyed. Before the actor is actually fully destroyed, it will be marked pending kill, which is why you should always use the IsValid global for checking object pointers, as this also checks PendingKill.

I just feel like TWeakObjectPtr in this case is not required.

In this case it might not be but still, it’s good to know that other options exist.
TWeakObjectPtr IsValid() function can also return false if the actor is pending kill.