📘 Forward declarations(전방 선언)의 정의/개념
C++로 언리얼 프로젝트를 개발하다 보면, 헤더 파일마다 include가 점점 늘어나 컴파일 시간이 길어지고, 클래스 간 의존성도 복잡해지는 현상이 발생한다. 이럴 때 유용하게 사용할 수 있는 것이 바로 전방 선언(Forward Declaration)이다.
전방 선언은 어떤 클래스나 구조체의 전체 정의를 굳이 include하지 않고, 그 이름만 미리 선언해두는 방식이다. 쉽게 말하면, "이런 클래스가 나중에 정의될 거야"라고 컴파일러에게 미리 알려주는 역할을 한다.
🔧 Forward declarations(전방 선언)의 예시
text
// UMyActor.h
// 전방 선언만 해둠
class UMyComponent;
class AMyActor : public AActor
{
UMyComponent* MyComponent; // 포인터만 사용하므로 정의는 필요 없음
};text
// UMyActor.cpp
#include "UMyComponent.h" // 실제 정의가 필요한 곳에서 include
void AMyActor::BeginPlay()
{
MyComponent->Activate(); // 이 시점에선 정의가 필요하므로 include 필수
}✅ Forward declarations(전방 선언)은 왜 필요할까?
헤더 파일에서 어떤 클래스의 포인터나 참조만 필요하다면, 굳이 그 클래스의 모든 내용을 알 필요는 없다.
이때 Forward declarations을 이용하면 다음과 같은 구조적 이점을 얻을 수 있다.
1. 컴파일 시간 단축
- 헤더 파일에 #include를 많이 쓰면, 의존성이 급격히 늘어나 전체 컴파일 타임이 길어짐.
- Forward declaration은 클래스의 정의가 필요하지 않은 경우, 헤더 파일 간 의존성을 줄여 컴파일 속도를 대폭 향상시킴.
반대로 #include "MyComponent.h"를 쓰면, MyComponent의 모든 내용을 읽어들이므로 빌드 시간이 증가.
2. 헤더 간의 순환 참조(Circular Dependency) 방지
- 클래스 A가 클래스 B를 include하고, B도 A를 include하면 순환 참조 문제가 발생.
전방 선언은 헤더 내에서 정의를 끌어오지 않기 때문에 순환 의존을 피할 수 있음.
3. 모듈성과 캡슐화 향상
- 어떤 타입이 내부 구현에만 필요하고, 헤더의 인터페이스를 구성하지 않는 경우, 전방 선언을 통해 구현을 은닉할 수 있음.
이는 모듈성을 높이고, 다른 클래스가 해당 구현에 불필요하게 의존하지 않도록 방지함.
❗ Unreal Engine에서 Forward declarations을 사용할 때 주의할 점
Forward declarations은 어떤 경우에 사용해야할까?
- 포인터나 참조만 사용하는 경우엔 전방 선언 가능
- 멤버로 값(value) 보유하거나, 해당 타입의 메서드를 호출하거나, 상속하는 경우는 반드시 include 필요
text
// ❌ 전방 선언만으로 불가능한 경우 (컴파일 에러 발생)
class MyClass;
class A {
MyClass MyObject; // 구조체 크기를 알 수 없기 때문에 전방 선언만으로는 불가능
};언리얼엔진의 공식 문서의 Programming with C++/Coding Standard를 보면 Header Including에 대해 다음과 같은 규칙을 권장하고 있다.
- All headers should protect against multiple includes with the #pragma once directive.- Note that all compilers we use support #pragma once.- Try to minimize physical coupling.- In particular, avoid including standard library headers from other headers.- Forward declarations are preferred to including headers.- When including a header, be as fine grained as possible.- For example, do not include Core.h. Instead, you should include the specific headers in Core that you need definitions from.- Try to include every header you need directly to make fine-grained inclusion easier.- Don't rely on a header that is included indirectly by another header you include.- Don't rely on anything being included through another header. Include everything you need.