TIL_060 : check/ensure, Isvalid, CDO, 정적/동적 클래스

김펭귄·2025년 11월 12일

Today What I Learned (TIL)

목록 보기
60/112

오늘 학습 키워드

  • 매크로

  • 프로젝트 상대 경로 추가

  • check vs ensure

  • Isvalid

  • CDO

  • StaticClass vs GetClass

1. 클래스/구조체/Enum 매크로

  1. UCLASS() : 클래스 매크로
  2. USTRUCT() : 구조체 매크로
  3. UENUM() : 열거형 매크로
UCLASS()	//  클래스 매크로
class PROJECTNAME_API UMyObject : public UObject
{
	GENERATED_BODY()
};
  • PROJECTNAME_API : 다른 모듈에서 해당 객체에 접근하게 해주는 키워드

2. C++ 상대 경로 추가

// 프로젝트명.Build.cs

using UnrealBuildTool;

public class ShooterX : ModuleRules
{
    public ShooterX(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        PublicDependencyModuleNames.AddRange(new string[]
            {
            "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput"
            }
        );

        PrivateDependencyModuleNames.AddRange(new string[] {});

        PublicIncludePaths.AddRange(new string[] { "ShooterX" });
    }
}
  • PublicIncludePaths.AddRange(~) : 모듈의 퍼블릭 헤더 파일을 찾을 때 참고하는 경로에 "~" 폴더를 추가

  • 모듈 컴파일러에게 "~" 폴더 안에 있는 헤더 파일들을 포함 경로로 참조하라고 알려줌

  • 해당 모듈이 존재하는 디렉토리 내에서 "~"를 찾기 때문에 이를 고려하여 폴더 이름을 설정해야함

3. check / ensure

check()

check(GameInstance != nullptr)

  • 인자로 전달한 조건식이 거짓일 경우 프로그램을 중단시킴

  • 디버깅 중이라면 중단점을 걸고, 릴리즈(shipping)빌드에선 게임이 꺼짐

checkf(GameInstance != nullptr, TEXT(”GameInstance is invalid.”))

  • 그래서 보통 checkf를 사용하여 추가적인 디버깅 정보를 출력함

  • 게임을 멈출정도로 중요한거여서 유효성 검사 필요할 때 사용

ensure()

ensure(nullptr != GameInstance)

  • 조건식이 거짓이더라고 프로그램을 계속 동작시킴

  • 대신, 해당 위치까지의 콜스택을 출력해서 디버깅에 도움을 주고, 경고 메시지를 남김

  • 릴리즈에서도 게임이 꺼지지 않음

ensureMsgf(nullptr != GameInstance, TEXT(”GameInstance is inalid.”))

  • 마찬가지로 디버깅 정보 출력 가능

4. IsValid()

  • 런타임 중에 UObject 개체의 포인터가 유효한지 검사 할때 쓰는 함수

  • nullptr로만 확인하기에는 언리얼의 경우 GC 대상이거나 Pending Kill같은 경우가 있어 IsValid(~)을 사용하는 것이 안전

  • 유효하다면 true를 반환

5. Class Default Object (CDO)

  • Native C++의 객체 생성자를 이용하여 객체를 초기화해도, 에디터에서 수정도 가능하고, 블루프린트에서 override되기도 하므로 초기화가 많이 일어남

  • 따라서 CDO를 이용하여 클래스가 정의한 속성의 초기값을 한 번만 세팅해 두고, 객체 인스턴스가 생성될 때마다 CDO의 값을 복사하여 빠르게 초기화함

  • 매번 동일한 초기화 코드를 돌리는 대신, 미리 만들어둔 CDO를 복사하므로 메모리 사용과 속도면에서 유리

  • 먼저 CDO를 이용하여 초기화하고, 그 뒤에 생성자가 호출되어 남은 작업을 처리하므로 생성자도 가벼워짐

  • 에디터에서 속성을 바꾸면 CDO에도 반영되며, 인스턴스 생성 시에도 적용됨

CDO 생성 시점

  • 언리얼 엔진이 초기화되면 엔진 구동에 필요한 모듈이 순차적으로 로딩됨

  • 해당 모듈에 작성된 언리얼 클래스들도 함께 로드되면서 클래스마다 CDO가 생성

GetDefault()

  • 엔진 초기화 이후 모든 CDO는 만들어지고 메모리에 로드됨

  • 이 CDO를 GetDefault()를 이용하여 가져올 수 있음

  • CDO는 엔진이 종료될 때까지 메모리에 상주

사례

USXGameInstance::USXGameInstance()
{
	Name = TEXT("CDO");		// CDO에서의 이름
}

void USXGameInstance::Init()
{
	Super::Init();

	Name = TEXT("Instance");	// 런타임 도중 이름 바꿈

	// 런타임에 인스턴스 생성
	UE_LOG(LogTemp, Log, TEXT("GameInstance : %s"), 
    	*(RuntimeClassInfo->GetDefaultObject<USXGameInstance>()->Name));
    // 현재 객체
	UE_LOG(LogTemp, Log, TEXT("GameInstance : %s"), *Name);
}

  • 첫 번째 로그는 런타임에 인스턴스를 새로 생성하였으므로, 해당 인스턴스는 CDO를 복사하여 초기화 됨.
    따라서 CDO에서의 Name값인 CDO가 출력

  • 두 번째 로그는 현재 인스턴스의 Name이므로, "Instance"로 초기화했으므로 "Instance"를 출력

6. StaticClass() vs GetClass()

  • StaticClass()
    프로그램이 돌다가 해당 함수가 호출되면, 해당 객체의 컴파일 타임 시점의 UClass*를 반환

  • GetClass()
    마찬가지로 런타임 중 호출되면, 해당 시점에서의 객체의 UClass*를 반환

예시 코드

// MyCharacter.h
UCLASS()
class INTOTHEPARADOX_API AMyCharacter : public ACharacter
{
	GENERATED_BODY()

protected:
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Bullet")
	TSubclassOf<ABullet> BulletClass;
};    

// MyCharacter.cpp
void AMyCharacter::Fire() {
	ABullet* Bullet = GetWorld()->SpawnActor<ABullet>(
		BulletClass,
		SpawnLocation,
		SpawnRotation
	);
    
    check(Bullet->StaticClass() == Bullet->GetClass())
};
  • 캐릭터 객체에 총알 클래스를 멤버변수로 가짐

  • 그러나 에디터 상에서 해당 멤버변수에 총알 클래스를 기반으로한 블루프린트(BP_Bullet)를 연결

  • 그러면 Bullet의 컴파일타임 시점의 클래스는 ABullet이고, 런타임에는 블루프린트 객체인 BP_Bullet으로 연결됨
    따라서 서로 다르므로 check에서 중단점이 걸림

  • 마찬가지로, BulletClass도 컴파일 시점엔 ABullet이지만, 런타임에 블루프린트가 연결되었으므로 서로 다름
    따라서 BulletClass->GetClass() == BulletClass->StaticClass()도 거짓

profile
반갑습니다

0개의 댓글