BeginPlay can't call class pointer's member referencing TArray

An instance of class Dummy1 is in the level when it starts. It has a TArray of int32 which the constructor (or BeginPlay; the issue persists either way) initializes with 1 value.

In Dummy1’s BeginPlay(), it spawns an instance of class Dummy2, with a pointer back to the Dummy1 that spawned it.

The Dummy2 instance then attempts to use it’s pointer to the Dummy1 instance in BeginPlay() to call a member function of Dummy1 that references the TArray. UE4 Crashes.

Relevant code in Dummy1.cpp:

ADummy1::ADummy1()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	numbers = TArray<int32>();
	numbers.Add(1);
}

// Called when the game starts or when spawned
void ADummy1::BeginPlay()
{
	Super::BeginPlay();

	ADummy2* newDummy2 = GetWorld()->SpawnActor<ADummy2>(GetActorLocation(), FRotator(0, 0, 0));
	if (newDummy2 != nullptr) {
		newDummy2->myDummy1 = this;
	}
	
}

void ADummy1::MyFunc() 
{
	UE_LOG(MyLog, Warning, TEXT("Array Value: %d"), numbers.Num());
}

Relevant code in Dummy2.cpp:

void ADummy2::BeginPlay()
{
	Super::BeginPlay();
	myDummy1->MyFunc();
}

Things I’ve tried (still crashes):

  1. Initialize TArray in Dummy1’s BeginPlay() function
  2. Use a pointer to a TArray (epic fail)
  3. Have Dummy2’s BeginPlay() call another function that accesses the TArray
  4. Make Dummy2’s BeginPlay() public
  5. Make the TArray a UPROPERTY() in Dummy1 (it already is - EditAnywhere)

Things that have worked (but don’t do what I want):

  1. Calling Dummy1’s function that accesses the TArray (MyFunc) from Dummy2’s Tick()
  2. Calling Dummy1’s function that accesses the TArray (MyFunc) from Dummy1’s BeginPlay()
  3. Removing the TArray reference from MyFunc and calling MyFunc from Dummy2’s BeginPlay() (Function call successful, no crash)

I tried to attach the complete files, but the forum wouldn’t let me. I’m an Unreal noob (and brushing up on C++), so sorry if I’m being dumb. But it’s been 2 days…

I should add the crash error that I get is:
“The thread tried to read from or write to a virtual address for which it does not have the appropriate access.”

During ADummy1::BeginPlay() you are spawning an instance of Dummy2 that will go out of scope/GCed right after the function ends. Depending on the order of items being constructed in the world it might lead to strange behaviour.
If I may ask, what are you actually trying to do? There might be a better pattern for it.

Thanks for your help!

First, I can verify that Dummy2 is not getting garbage collected because I can see it created in the World Outliner if I comment out the TArray reference in MyFunc. Furthermore, UE_LOG successfully logs other things (e.g. ‘Hello World’) from MyFunc when it’s called the same way.

To answer your question, I used the C++ Puzzle template to build a grid of blocks. I want to assign each of the blocks a random numeric value, and am trying to use the TArray in my PuzzleBlockGrid to store the array of values that can be assigned.

Hello - I got your bug report. Not seeing a crash on my system (Dummy2 is created and references the Dummy1 actor) - I’d try deleting saved/intermediate folders and recompiling project.

Feel free to link google drive project here for others to test!

Thanks . I gave that a try, and it still crashes. Should i try an engine re-install?

Test Project Here: https://drive.google.com/open?id=12N_onc-o9QT-NP0ZqahRxl1bI6Eilmmb

I don’t think an engine reinstall should be necessary - regenerating visual studio project files (right click on .uproject) might be something else to try.

Still no luck :frowning: I also upgraded to 4.19.1, and both suggestions simultaneously, with no success. Any other tips?

SpawnActor executes the new actor’s BeginPlay before returning.

This causes:

 void ADummy2::BeginPlay()
 {
     Super::BeginPlay();
     myDummy1->MyFunc();
 }

to run before:

if (newDummy2 != nullptr) {
    newDummy2->myDummy1 = this;
}

As such myDummy1 is an invalid pointer and causes the crash.

Oh my gawd…I can’t believe that was it. I thought that since the pointer successfully called the function, it meant that the object had been successfully initialized. But you’re correct; the function failed at referencing other variables too. I’ll write up a more thorough answer based on your comment. Thanks much!

It turns out HarryCodeRK’s answer was correct. The Dummy2’s BeginPlay was being called before the pointer had been set.

The solution is to use the SpawnActorDeferred() method rather than the SpawnActor() method so that you can set the pointer before calling BeginPlay on Dummy2:

	const FTransform transform(GetActorLocation());

	ADummy2* newDummy2 = GetWorld()->SpawnActorDeferred<ADummy2>(ADummy2::StaticClass(), transform);
	if (newDummy2 != nullptr) {
		newDummy2->myDummy1 = this;
	}
	newDummy2->FinishSpawning(transform);