목차
- 01. Intro
- 02. 전방 선언
- 정의 및 특징
- 기존 문제점과 전방 선언 사용 예시
- 03. 마무리
- 04. 연관 내용
01. Intro
오늘은 [전방 선언]에 대해 알아보겠습니다.
02. 전방 선언
정의 및 특징
전방 선언(Forward Declaration)은 클래스 또는 함수의 선언만 전방에 미리 하는 것을 말합니다.
그래서, 정의가 적힌 구현부는 나중에 작성합니다.
전방 선언을 사용하는 이유는 다음과 같습니다.
1. 한 코드 파일에 모두 작성할 경우, 함수 선언 순서에 대해 고민할 필요가 없습니다.
2. 헤더 파일 간의 의존성을 줄일 수 있습니다.
3. 컴파일 시간을 줄일 수 있습니다.
이에 대한 내용을 한 번 살펴보도록 하겠습니다.
기존 문제점과 전방 선언 사용 예시
1. 함수/클래스 선언 순서에 대한 고민 필요
#include <iostream>
using namespace std;
int addComplex(int inNum)
{
return inNum + addSimple(inNum);
}
int addSimple(int inNum)
{
return inNum + inNum;
}
int main()
{
int num = 10;
num = addComplex(num);
return 0;
}
위와 같은 코드가 있다고 가정하고 실행하면, 아래와 같은 컴파일 에러 문구가 출력됩니다.
'addSimple': identifier not found
분명히, addSimple이라는 함수를 선언하고 구현했지만, 컴파일러는 이를 인지하지 못합니다.
이를 이해하려면, C++언어의 특성에 대해서 먼저 이해해야 합니다.
- C++은 컴파일 언어
C++는 컴파일 언어입니다.
컴파일 시점에 함수가 선언되지 않으면, 컴파일러가 찾을 수 없어 오류를 발생합니다.
addSimple 함수가 선언되기 전에 해당 함수를 사용했습니다.
그래서, 컴파일러가 addComplex 함수를 처리할 때, addSimple 함수가 정의되지 않아 오류가 발생합니다.
여담으로 파이썬이나 자바는 인터프리터 언어여서 위와 같은 상황을 고려하지 않아도 됩니다.
컴파일 언어와 인터프리터 언어의 차이점은 추후에 다루도록 하겠습니다.
- 해결 방안 1. 함수 선언 순서 교체
#include <iostream>
using namespace std;
int addSimple(int inNum)
{
return inNum + inNum;
}
int addComplex(int inNum)
{
return inNum + addSimple(inNum);
}
int main()
{
int num = 10;
num = addComplex(num);
return 0;
}
addComplex 함수 내에서 addSimple 함수를 호출하니, addSimple 함수를 가장 먼저 선언 및 구현을 하면, 해결할 수 있습니다.
- 해결 방안 2. 전방 선언 활용
#include <iostream>
using namespace std;
// Forward Declaration
int addComplex(int inNum);
int addSimple(int inNum);
int addComplex(int inNum)
{
return inNum + addSimple(inNum);
}
int addSimple(int inNum)
{
return inNum + inNum;
}
int main()
{
int num = 10;
num = addComplex(num);
return 0;
}
위 코드처럼 각 함수의 선언부만 미리 작성하는 것을 전방 선언이라고 합니다.
그래서, 해당 코드를 실행하면, 컴파일 에러 없이 실행되는 것을 확인할 수 있습니다.
int addComplex(int);
int addSimple(int);
컴파일러에게 해당 형태의 함수를 사용한다고 알려주기만 하면 됩니다.
그래서, 위 코드처럼 매개 변수의 이름이 없어도 정상적으로 작동합니다.
하지만, 다른 사람들이 해당 코드를 봤을 때, 어떤 목적으로 사용되는 매개 변수인지 한 번에 이해할 필요가 있습니다.
그러므로, 매개 변수 이름을 명시적으로 작성하는 것을 더 선호합니다.
2. 헤더 파일에 대한 의존성 문제 고려
다른 헤더 파일에 있는 내용을 참고하려면, #include "Test.h"처럼 헤더 파일 포함 전처리어를 사용해야 합니다.
이때, 서로 다른 헤더 파일이 서로를 참조하는 순환 참조(Circular Dependency)가 발생할 수 있습니다.
아래 예시를 통해 자세히 살펴보도록 하겠습니다.
A.h 파일과 B.h 파일이 있고 각 파일에는 아래와 같이 코드가 작성되어 있다고 가정합니다.
"A.h" 파일
// A.h
#include "B.h"
// TODO
"B.h" 파일
// B.h
#include "A.h"
// TODO
그러면, A.h는 B.h의 코드를 모두 복사하고 B.h는 A.h의 코드를 모두 복사하는 반복 과정을 거칩니다.
즉, 컴파일러는 끝없이 A.h → B.h → A.h → B.h → A.h → ...를 반복하는 상황이 발생합니다.
그러므로, 이를 방지하기 위해 컴파일러는 순환 참조를 막아 오류가 발생합니다.
위 문제를 해결하기 위해 전방 선언을 활용합니다.
헤더 파일(.h)에서는 #include로 다른 라이브러리나 다른 헤더 파일을 참조하지 않습니다.
헤더 파일에서는 단순히 사용하려는 클래스나 함수에 대해서 전방 선언을 함으로써, 해당 형태를 사용할 것임을 컴파일러에게 단순히 알려줍니다.
전방 선언한 클래스가 있는 라이브러리는 소스 파일(.cpp)에서 #include를 합니다.
아래는 서로 다른 파일에 대한 전방 선언을 한 예시입니다.
"A.h" 파일
class Sport
{
public:
int _num;
};
"B.h" 파일
class Stardium
{
public:
Stardium()
class Sport* sport;
}
"B.cpp" 파일
#include "A.h"
Stardium::Stardium()
{
sport = new Sport();
sport->num = 10;
}
"B.h" 파일에서 "A.h" 파일의 Sport 클래스를 사용할 때, #include를 하지 않고 class Sport*로 전방 선언해서 사용할 수 있습니다.
sport에 저장된 포인터 주소값으로 이동하면, Sport 클래스에 대한 객체가 있음을 알려주기만 합니다.
그래서, 구현부인 "B.cpp" 파일에서 #include "A.h"를 사용하면, Sport 클래스에 대한 내부 정보를 알 수 있어 접근할 수 있습니다.
class Sport는 Sport 클래스의 형태가 있다는 것만 알려주고 구체적인 내용을 모릅니다.
그래서, 헤더 파일에서 Sport 클래스에 대해서 접근은 불가능합니다.
접근이 가능하도록 만들려면, 헤더 파일에서 #include "A.h"를 사용해야 합니다.
추가로, "A.h"에 있는 Sport 클래스에 대해서 "B.h" 파일에서 상속을 받으려면, 반드시 "A.h"를 include 해줘야 합니다.
03. 마무리
언리얼 엔진 작업을 하다가 전방 선언에 대해서 알게 되었는데, 오늘 글로 작성하면서 한 번 더 복습하는 계기가 되었습니다.
04. 연관 내용
읽어주셔서 감사합니다.
틀린 내용 지적은 언제나 환영입니다!
'게임 개발 > C++' 카테고리의 다른 글
| [C++] static 키워드 (1) | 2025.03.16 |
|---|---|
| [C++] 부동소수점 (0) | 2025.03.15 |
| [C++] 접근 지정자 (0) | 2025.03.12 |
| [C++] 포인터 vs 참조 (0) | 2025.03.11 |
| [C++] mutable 키워드 (0) | 2025.03.10 |