1. 관계 정의의 종류: Is-A vs Has-A
설계 단계에서 가장 먼저 결정해야 할 것은 클래스 간의 관계입니다.
Is-A (상속 관계)
- 정의: 한 클래스가 다른 클래스의 일종임을 나타냅니다. (A는 B이다)
- 언리얼 구현: class AMyCharacter : public ACharacter
- 용도: 부모 클래스의 기능을 확장하거나, 공통 인터페이스를 통해 여러 객체를 동일한 방식으로 다룰 때(다형성) 사용합니다.
Has-A (포함/소유 관계 - Composition)
- 정의: 한 객체가 다른 객체를 구성 요소로 가지고 있음을 나타냅니다. (A는 B를 가지고 있다)
- 언리얼 구현: UPROPERTY()로 선언된 컴포넌트 포인터나 멤버 변수.
- 용도: 기능을 모듈화하고 객체 간의 결합도를 낮추는 데 유리합니다. 현대 프로그래밍에서는 "Inheritance보다 Composition을 우선하라"는 원칙이 권장됩니다.
2. 타입 체크와 클래스 비교: IsA() vs GetClass() == ...
런타임에 객체의 타입을 확인해야 할 때, 언리얼에서는 두 가지 방식을 주로 고민하게 됩니다.
| 구분 | IsA() 함수 | == 직접 비교 (GetClass()) |
| 특징 | 다형성 지원 (상속 관계 포함) | 엄격한 타입 체크 (정확히 일치) |
| 동작 | 해당 클래스나 하위 클래스면 true | 클래스 정보가 정확히 일치해야 true |
| 비용 | 계층 구조를 순회하므로 상대적 오버헤드 발생 | 포인터 주소 비교로 매우 빠름 |
[Engine Insight]
IsA()는 언리얼 리플렉션 시스템을 사용하여 계층 구조를 확인합니다. 일반적인 이벤트 로직에서는 문제가 없으나, 수천 개의 객체를 매 프레임 Tick에서 체크해야 한다면 Enum이나 Bit Flag를 통한 O(1) 비교가 더 효율적일 수 있습니다.
3. 다형성 (Polymorphism)
다형성이란 "하나의 인터페이스로 여러 가지 형태의 객체를 조작하는 능력"입니다.
- 핵심 기제: virtual 키워드를 통한 가상 함수(Virtual Function)와 동적 바인딩(Dynamic Binding).
- 동작 원리: 가상 함수가 포함된 클래스는 vtable(가상 함수 테이블)을 가지게 되며, 런타임에 실제 객체의 타입을 확인하여 적절한 함수를 호출합니다.
- 장점: 새로운 자식 클래스가 추가되어도 부모 포인터를 사용하는 기존 코드를 수정할 필요가 없어 확장성이 극대화됩니다.
4. 업캐스팅(Upcasting) vs 다운캐스팅(Downcasting)
다형성을 구현하기 위해 객체의 타입을 변환하는 과정입니다.
① 업캐스팅 (Upcasting)
- 정의: 자식 클래스 포인터를 부모 클래스 타입으로 변환.
- 특징: 항상 안전하며 암시적(Implicit)으로 가능합니다.
- 용도: 여러 종류의 자식 객체들을 하나의 배열(TArray<Parent*>)로 관리할 때 사용합니다.
② 다운캐스팅 (Downcasting)
- 정의: 부모 클래스 포인터를 구체적인 자식 클래스 타입으로 변환.
- 특징: 위험성 존재. 실제 객체가 해당 타입이 아닐 경우 문제가 발생합니다.
- 언리얼 방식: Cast<T>()를 사용하며, 실패 시 nullptr을 반환합니다.
- 최적화 팁: 모바일 환경(Android Vulkan 등)에서는 런타임 캐스팅 비용을 고려해야 합니다. 빈번한 캐스팅보다는 가상 함수 호출을 통한 해결이 권장됩니다.
5. 결론 및 요약
| 개념 | 목적 | 복잡도(Big-O) |
| Is-A | 계층 구조 설계 및 다형성 활용 | - |
| Has-A | 기능 모듈화 및 결합도 감소 | - |
| IsA() | 상속 관계를 포함한 타입 확인 | O(H) (H: 상속 깊이) |
| Casting | 타입 변환을 통한 특수 기능 접근 | O(1) (언리얼 Cast 기준) |
게임을 설계할 때 상속(Is-A)은 공통된 인터페이스가 필요할 때만 엄격하게 사용하고, 기능의 확장은 컴포넌트(Has-A) 기반으로 구성하는 것이 현대적인 게임 엔진 활용의 정석입니다. 특히 모바일 환경에서는 런타임 타입 체크와 가상 함수 호출의 비용을 인지하고 적절한 캐싱 전략을 세우는 것이 중요합니다.
'C++ 공부' 카테고리의 다른 글
| [C++ Study] 가상 함수부터 인터페이스까지: 객체지향의 핵심 정리 (0) | 2026.04.24 |
|---|---|
| [C++ Study] 가상 함수와 V-Table: 동적 바인딩의 내부 메커니즘 (0) | 2026.04.19 |
| [C++ Study] 키워드와 메모리 구조를 통한 안전한 코드 설계 (explicit, 형변환연산자, cast, mutable, 메모리영역) (0) | 2026.04.17 |
| [C++ Study] friend, 상속의 경계, 그리고 static의 메모리 전략 (0) | 2026.04.10 |
| [C++ Study] Side Effect, Android Vulkan, 그리고 현대적 상수(const/constexpr/consteval) (0) | 2026.04.09 |