C++ Create instance of class then call function on it

Hi,

I have a soft class reference of a class called “ItemBaseRef”. I’m trying to load it and create an instance of it so I can call a function on it. I’ve got the code to check it’s valid and load it, but it doesn’t recognize the function when I try and call it. I have the ItemBase class included in my header.

Have I done this right and how do I call the function?

void AMDC_CharBase::GetItemProperties(FName& ItemName)
{
	if(ItemBaseRef.IsValid())
	{
		// Load class synchronously
		UClass* ItemClass = ItemBaseRef.LoadSynchronous();

		if(ItemClass) // Class loaded
		{
			// Create an instance of the class
			UObject* ItemInstance = NewObject<UObject>(GetTransientPackage(), ItemClass);

			// I tried ItemClass->MyFunction as well as ItemInstance->MyFunction here but neither Work??
		}
	}
}

Thanks so much in advance for any help!

you have to cast it. and to call the functions on an object, cpp needs you to explicitly state the class.
so UObject* is not enough. you need to have something like
" UBaseClass* ItemCasted = Cast< UBaseClass >(ItemInstance);"
and make sure you define the function on the base class. it will work using polymorfism which is somewhat basic functionality.
you can also use UFUNCTION(BlueprintNativeEvent) to allow bps to override it too.

otherwise you can use interfaces, but i don’t really like them much.

there are other ways to call a function by name on bp, but that’d be a bit hackish from my pov.

Thank you. That did it. Does casting like this create a hard reference? Or will it not load the object in memory until it’s been loaded?

glad it helped.
im not entirely sure. i don´t think it really matters that much. i never had problems with it, and i doubt you need to optimize that much.
i would think it creates a reference to the class.
Maybe you might have issues if the class is in a different pak file, but i doubt it’s your case.
i assume there’s a difference between loading a class and an object.
but there’s also the possibility that the cdo is created for that class upon reference.

but neither of those things i’m super confident.
put some logs on the constructor and you might find out, thought i wouldn’t call that conclusive.
what i can say is that by the time you .Load the itemclass, if it’s a child of the other class, it might be loaded already.

In this case, you shouldn’t be casting. Whatever type you’re casting to you should use as the template parameter (the thing inside the < >) when calling NewObject.

References work in a little bit of a different way in C++. In native you can only cast to types that are available all the time anyway.

In the case of your blueprint type derived from your C++ type, the Cast would not be causing the hard reference. First the call to LoadSynchronous will cause the type to be loaded (if it’s not already). After that, the instance of the object that you create through NewObject has the hard reference to the class. So as long as that object exists, the class will be loaded. UObjects don’t stay alive on their own, so if you don’t assign that new object to a property somewhere it will get destroyed (and the class may get unloaded if there aren’t any other objects of that type).

The reason that Casts in blueprint cause a hard reference is because there is an implicit LoadSynchronous wrapped up with the cast because the blueprint wouldn’t be able to do anything with the return value if the type weren’t loaded at the same time as the blueprint doing the cast.

1 Like

This is really helpful, thank you. I’ll try changing the <> object to my object so I don’t need to cast.

One more related question if it’s not too much trouble…

When I pickup an item, I need to keep a reference to its class so I can spawn it in the world if I want to drop it. I’ve been using a generic actor class array. Let’s say my item has lots of variables (icon texture, name, description, SFX, VFX etc.) so my child class (which is a child of the generic actor class) is 100MB. Obviously an Actor Class is very lightweight as it’s a small container.

My understanding is that the actor array will be small with no references while there’s nothing in it. When I add the child item to it then it will load the full child item in memory (100MB) even though it only knows it as an actor. Is that right? I’m thinking of changing the Actor Class Array to a Soft Actor Class Array so it doesn’t load all of my inventory into memory and instead I control when to load something in for use. I’ll always need access to the icon, name and description for when I view my inventory, but I can pull those out in a struct without loading the whole class. Just trying to think through the most optimal way to set things up. Thanks!

I think you’re mixing up classes, references and instances, but I can’t be sure without you sharing more code.

In order to spawn an actor in the first place. The class has to be loaded. If that class is loaded, all the hard references from it will be loaded. It doesn’t matter if you’ve created any instances of that class. If you’ve loaded it (and kept any sort of reference to it to keep the garbage collector from unloading it) all that stuff is loaded. The good news is that it’s only loaded once, and not 100 times if you’ve got 100 instances. (well it sort of is, but not the things it’s referencing like the textures or vfx that are actually causing the size to be large).

Once you have the actor, it doesn’t matter if you store it in a TArray<AActor*> or TArray<AItem*>. The type there doesn’t make any difference for sizes. The only difference is that the AActor* array can hold references to things that aren’t AItem’s.

If you’ve got an array of TSubclassOf< AItem >, then yes you probably want to have that be an array of TSoftClassPtr< AItem > instead. The latter would allow you to load them as you want/need them.

You could pull stuff out into a structure, but it gets tricky because you’ve got to keep it in sync with the source. Another option is to make the things referenced by your actor (AItem) also soft. So instead of UTexture2D*, you’d have a TSoftObjectPtr. This works a lot like TSoftClassPtr such that when you load the blueprint it would no longer immediately load the texture, you’d have to trigger it loading at some other time when you need it. You can do the same for any other reference to content. If the icon should always be loaded, leave it a hard reference and change just the VFX/SFX/etc to be soft pointers and load them when you need them.

Don’t worry about optimal. Any advice you may receive (including this one) is generic and may not be best for whatever it is you’re trying to build. You’ve just got to try things and learn more so that when it’s not working how you want to, you have more information to talk about it.
There are lots of different solutions for this problem. Many of them much more complicated and in ways that are hard to explain and probably beyond your current skill level. That’s not meant to be a dig or an insult, only recognition that the complicated ways tend to require the experience of the simpler ways not working to understand why it’s worth it and the vocabulary to discuss the problem.

1 Like

Thanks very much. And no offense taken… I’m very new to C++ so I just appreciate people like you taking the time to help explain things. I think what you said is clear though so thank you for that.

1 Like

Just showing here that it doesn’t work without casting (per earlier comment)…

Here’s the code without casting but putting the AMDC_ItemBase in the <> instead of the UObject. With this method I can’t access the function or properties from it…

			UObject* ItemInstance = NewObject<AMDC_ItemBase>(GetTransientPackage(), ItemClass);

If I cast it like this it works great and I can access the properties and functions from it…

UObject* ItemInstance = NewObject<UObject>(GetTransientPackage(), ItemClass);
			AMDC_ItemBase* ItemCasted = Cast<AMDC_ItemBase>(ItemInstance);

Well you have to assign it to an AMDC_ItemBase*. Similar to what you’re doing with the result of the Cast.

AMDC_ItemBase* ItemInstance = NewObject<AMDC_ItemBase>(GetTransientPackage(), ItemClass)

Ah got it. This works, thank you.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.