Ao desenvolver com o Unreal Engine 4, o ciclo de vida dos objetos é uma consideração crucial, especialmente diferenciando entre objetos C++ comuns e instâncias de UObject. Este guia aborda técnicas para prevenir falhas de acesso, como violações de memória ou erros de função pura virtual, focando no uso coreto de ponteiros inteligentes e funções de gerenciamento de raiz.
1. Objetos Não-UObject (C++ Puro)
Para instâncias de classes C++ padrão, o ciclo de vida pode ser simplificado com ponteiros inteligentes compartilhados (TSharedPtr), eliminando a necessidade de exclusão manual.
TSharedPtr<minhaclasse> InstanciaCompartilhada = MakeShareable(new MinhaClasse());</minhaclasse>
Para obter um ponteiro bruto a partir dele:
MinhaClasse* PonteiroBruto = InstanciaCompartilhada.Get();
Quando outra classe mantém uma referência que pode se tornar inválida, utilize um ponteiro fraco (TWeakPtr) para verificar a validade com segurança.
class GerenciadorDeRecursos
{
public:
TWeakPtr<minhaclasse> ReferenciaFraca;
};
// Uso
GerenciadorDeRecursos Gerenciador;
Gerenciador.ReferenciaFraca = InstanciaCompartilhada;
if (Gerenciador.ReferenciaFraca.IsValid())
{
// A instância ainda existe.
}</minhaclasse>
2. Objetos UObject Vinculados à Cena
Objetos deriavdos de UObject são gerenciados pelo coletor de lixo do UE4. No entanto, um erro comum ocorre ao tentar verificar se uma referência a um componente foi invalidada sem usar os macros adequados. Considere uma classe de personagem:
UCLASS()
class APersonagemPrincipal : public ACharacter
{
GENERATED_BODY()
public:
// Referência sem UPROPERTY - problema potencial
UStaticMeshComponent* ComponenteDeMalha;
};
Destruir o componente sem UPROPERTY pode levar a um estado inconsistente:
ComponenteDeMalha->DestroyComponent();
if (ComponenteDeMalha) // Geralmente avalia como verdadeiro, mesmo após a destruição.
{
UE_LOG(LogTemp, Aviso, TEXT("O componente ainda é acessível!"));
}
A solução é decorar a propriedade com o macro UPROPERTY para que o motor rastreie seu estado. Alternativamente, use um ponteiro de objeto fraco (TWeakObjectPtr) para verificações explícitas de validade.
TWeakObjectPtr<ustaticmeshcomponent> PonteiroFraco = MakeWeakObjectPtr(ComponenteDeMalha);
ComponenteDeMalha->DestroyComponent();
if (!PonteiroFraco.IsValid())
{
UE_LOG(LogTemp, Log, TEXT("Componente foi destruído corretamente."));
}</ustaticmeshcomponent>
3. Objetos UObject Criados Dinamicamente
Objetos instanciados com NewObject são suscetíveis à coleta de lixo se não forem referenciados. Isso pode causar falhas de execução, como erros de função pura virtual. Para evitar isso, adicione o objeto à raiz do motor.
UStaticMeshComponent* ComponenteDinamico = NewObject<ustaticmeshcomponent>(this, TEXT("ComponenteCriado"));
ComponenteDinamico->AddToRoot();</ustaticmeshcomponent>
Quando o objeto não for mais necessário, remova-o da raiz explicitamente antes de invalidar o ponteiro.
ComponenteDinamico->RemoveFromRoot();
ComponenteDinamico = nullptr;
4. Objetos de Interface (UInterface)
Para interfaces que seguem o padrão UInterface, a destruição também requer atenção. Ao terminar o uso de uma instância de interface, obtenha seu objeto UObject subjacente e gerencie sua raiz conforme necessário.
IMinhaInterface* PonteiroDeInterface = /* ... */;
if (PonteiroDeInterface)
{
UObject* ObjetoBase = PonteiroDeInterface->_getUObject();
ObjetoBase->RemoveFromRoot();
// Opcionalmente, chame Destroy() ou deixe o coletor de lixo agir.
}