Crash when accessing a custom C++ class

I’m trying to make a talent tree system on C++ based on two classes, a Talent class to store simple data and a TalentTree class to store, manage and access a list of talents. Since they both share the same purpose, both ae in the same source files (TalentTree.h/.cpp) Everything seems to be working as intended, but when I try to access a talent from the list in my GameMode class it causes UE4 to crash.

I may not be initializing everything as needed, but I can’t find any answers on why it doesn’t work. I’ve tried changing the classes that inherit from it a few times, but I started inheriting both from UObject. Here’s my code, with :

TalentTree.h,

UTalent:
    UCLASS()
    class PROJECTWAIFU_API UTalent : public UObject
    {
    	GENERATED_BODY()
    
    	UPROPERTY()
    		int id;
    	UPROPERTY()
    		int level;
    	UPROPERTY()
    		FString name;
    
    public:
    	UTalent(const FObjectInitializer &ObjectInitializer);
    
    	UTalent(FString _name, int32 _id);
    
    	// Getters and setters
    	UFUNCTION()
    		void SetLevel(int _lvl) { level = _lvl; }
    	UFUNCTION()
    		int GetLevel() { return level; }
    	UFUNCTION()
    		void SetID(int _id) { id = _id; }
    	UFUNCTION()
    		int GetID() { return id; }
    	UFUNCTION()
    		void SetName(FString _name) { name = _name; }
    	UFUNCTION(BlueprintCallable, Category = "Talent Tree")
    		FString GetName();
    
    	UFUNCTION()
    		void ResetLevel() { level = 0; }
    	UFUNCTION()
    		void LevelIncrease() { level++; }
    };

TalentTree.cpp,

UTalent:
    UTalent::UTalent(const FObjectInitializer &ObjectInitializer) : Super(ObjectInitializer)
    {
    	level = 0;
    }
    
    UTalent::UTalent(FString _name, int _id)
    {
    	name = _name;
    	id = _id;
    	level = 0;
    }
    
    FString UTalent::GetName() 
    { 
    	return name; 
    }

TalentTree.h,

ATalentTree:

    UCLASS()
    class PROJECTWAIFU_API ATalentTree : public AActor
    {
    	GENERATED_BODY()
    
    		UPROPERTY()
    		TArray<UTalent*> TalentList;
    	
    public:
    	ATalentTree(const FObjectInitializer &ObjectInitializer);
    
    	virtual void BeginPlay() override;
    	
    	UFUNCTION(BlueprintCallable, Category = "Talent Tree")
    		UTalent* FindByID(int _id);
    };

TalentTree.cpp,

 ATalentTree(with one WIP method):
    ATalentTree::ATalentTree(const FObjectInitializer &ObjectInitializer) : Super(ObjectInitializer)
    {
    }
    
    void ATalentTree::BeginPlay()
    {
    	UTalent* t = NewObject<UTalent>();
    	t->SetID(1);
    	t->SetName("Test talent");
    	TalentList.Add(t);
    }
    
    UTalent* ATalentTree::FindByID(int _id)
    {
    	return TalentList[0];
    
    	return nullptr;
    }

Hey Herreteman-

Can you provide the callstack and log files from the crash to indicate where the crash is occurring? Does the crash occur when you press play or during a specific action? Are you trying to access a specific talent from your game mode or does the crash happen when you access any talent from the game mode? Can you post the code where you’re accessing this class from your game mode as well?

The crash will consistently happen when pressing play, but just because the time I try to access a talent is on a BeginPlay. Right now, as I was just testing if it worked, I have one talent initialized and I access it directly on my test function FindByID(int _id), so it should happen with any talent.

Here’s the crash log: /questions/510836/ - crash log - Pastebin.com

The crash logs indicate an access violation when the call to FindByID is made (at line 42 of your talenttree.cpp file). Can you run the project in debug mode and let me know if the TalentList array (specifically element 0) is populated?

Sorry for the late response, I didn’t have access to my project for a day.

According to debug, the TalentList array isn’t populated, which means I forgot something when initializing it. In traditional C++ the code shouldn’t have any problem, so I may be missing something on UE4’s part.

When and where are you calling FindByID?
It may be that you are trying to access FindByID before BeginPlay is called.

You can place your initialization in the constructor:

ATalentTree::ATalentTree()
    :
    AActor()
{
    UTalent* t = NewObject<UTalent>();
    if (t) {
        t->SetID(1);
        t->SetName("Test talent");
        TalentList.Emplace(t);
    }
}

In any event, it’s probably a good idea to do a check before accessing the array members:

    UTalent* ATalentTree::FindByID(int _id)
    {
        if (TalentList.IsValidIndex(0)) {
            return TalentList[0];

        }
        return nullptr;

    }

TArray crashes if you attempt reading non-existing index (in native array it would return junk or crash if there is nothing allocated in accessing address), do check if requested id exists with this function:

This looked like the way to go, but it doesn’t quite work. It seems to be crashing when I call TalentList.IsValidIndex(0)…

As for when I’m calling it, right now it’s called inside a function in my custom GameMode class that needs to be called in other classes, and I call it as a test inside a Character Blueprint class using an input event.

In looking at your UTalent class again, I notice you’re overriding the constructor. I don’t think UE4 will allow you to do this (you shouldn’t use “new” at any point). It’s also an older version of the native constructor.
That might be the problem; when it accesses the array of UTalent pointers it freaks out.

Try just this as your constructor declaration:

UTalent();

Also, to expose your ATalentTree to the character blueprint, you’ll want to modify the UCLASS macro:

     UCLASS(BlueprintType, Blueprintable)
     class PROJECTWAIFU_API ATalentTree : public AActor

If that doesn’t help, please post the contents of the ATalentTree and UTalent header and source files so we’re sure we’re all on the same page with the current state; and post the log file with the new crash data.

I didn’t think about constructors so I started messing with them and using your recommended constructor. Doing some debug messages I discovered that the constructor for ATalentTree is never called, so it would explain the invalid data.

I only have an ATalentTree inside my custom GameMode for now, but it should be calling the constructor. Is there some specific constructor rule in UE4 that I’m missing?

Declaring your object constructor is pretty straightforward. You’ll want it to be public, and it’s just the class name with no arguments:

public:

    ATalentTree();

In the source you can call the constructor of parent objects via the initialization list, as well as set default values for you variables.

ATalentTree::ATalentTree()
    :
    AActor()//<-- parent constructor
{
    //perform any construction tasks here, as normal
}

Some older tutorials and example code include the FObjectInitializer argument, but that’s no longer passed in; and most (if not all) of its functionality is natively accessible within the constructor method.

If you’ve declared the variable in your game mode header, remember you still need to manually spawn the actor.

You can do it in your constructor using CreateDefaultSubobject or CreateOptionalDefaultSubobject. If the object you’re creating is a UObject you can also use the standard NewObject method; but within the game mode constructor you can’t use SpawnActor because the world doesn’t yet exist.

Outside your constructor you should use NewObject or SpawnActor, depending on which type of object you’re building.

Each of them is templated, and has a number of different forms.

For your ATalentTree you’ll want to declare and instantiate it like this:

//.h

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Talents")
ATalentTree* TalentTree;


//.cpp
AMyGameMode::AMyGameMode() 
    :
    AGameMode()
{

    //either of these should work to spawn your actor

    //this
    TalentTree = CreateDefaultSubObject<ATalentTree>();

    //or this
    TalentTree = CreateOptionalDefaultSubObject<ATalentTree>();

}

Declaring your object constructor is pretty straightforward. You’ll want it to be public, and it’s just the class name with no arguments:

public:

    ATalentTree();

In the source you can call the constructor of parent objects via the initialization list, as well as set default values for you variables.

ATalentTree::ATalentTree()
    :
    AActor()//<-- parent constructor
{
    //perform any construction tasks here, as normal
}

Some older tutorials and example code include the FObjectInitializer argument, but that’s no longer passed in; and most (if not all) of its functionality is natively accessible within the constructor method.

If you’ve declared the variable in your game mode header, remember you still need to manually spawn the actor.

You can do it in your constructor using CreateDefaultSubobject or CreateOptionalDefaultSubobject. If the object you’re creating is a UObject you can also use the standard NewObject method; but within the game mode constructor you can’t use SpawnActor because the world doesn’t yet exist.

Outside your constructor you should use NewObject or SpawnActor, depending on which type of object you’re building.

Each of them is templated, and has a number of different forms.

For your ATalentTree you’ll want to declare and instantiate it like this:

//.h

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Talents")
ATalentTree* TalentTree;


//.cpp
AMyGameMode::AMyGameMode() 
    :
    AGameMode()
{

    //either of these should work to spawn your actor

    //this
    TalentTree = CreateDefaultSubObject<ATalentTree>();

    //or this
    TalentTree = CreateOptionalDefaultSubObject<ATalentTree>();

}

I was missing CreateDefaultSubobject! I’ve used this when initializing components like meshes but I didn’t know it was necessary to call a constructor. I just assumed the constructors were called in the same way as normal C++.

Thanks for helping me figure it out.

No problem, glad to help.