💬 최신 소스로 당겨 빌드를 하는데 빌드에러가 났다.
원인은 헤더파일에 다른 헤더파일을 직접 참조한 게 원인이었다.
//A.h
#include "B.h" // 직접참조로 인한 error
에러는 누구나 발생시킬 수 있지만 직접참조와 전방선언 차이에 대해 제대로 이해를 못 하면 유사한 이슈가 재발하기에 이참에 정리해 본다.
직접참조? 와 전방선언
우선 직접참조와 전방선언의 모양을 임의의 MyClass 클래스를 예로 확인해 보자.
// MyClass.h
class MyClass {
public:
void someFunction();
int someVariable;
};
직접 선언(Direct declaration)의 예
#include "MyClass.h" //직접선언
int main() {
MyClass obj;
obj.someFunction();
return 0;
}
사실 직접참조라는 용어는 없고, 헤더파일에 MyClass 전체가 포함되어 있다면 직접 선언이 포함된 것이다.
전방선언(Forward declaration)의 예
class MyClass; // 전방 선언
MyClass* ptr = new MyClass(); // 올바른 사용
이 둘의 차이는 단순히 모양만 다른 것이 아니다.
직접 선언을 하게 되면
- 코드 수정 시 이를 포함하는 모든 파일을 다시 컴파일한다.
- 여러 헤더파일을 직접 선언하면서 '순환 의존 오류', 같은 클래스의 함수가 중복 선언되는 '심볼 재정의 오류'가 발생할 수 있다.
하지만 전방 선언을 하게 되면
- 헤더 간 순환참조를 방지하여 컴파일 속도를 향상 시킨다.
💬 "이렇게 얘기하면 전방 선언만 해야 할 것 같은데?"란 생각을 할 수도 있는데 전방 선언을 사용할 수 없는 상황이 있다.
class MyClass; // 전방선언
MyClass* ptr = new MyClass(); // 컴파일 가능
// ptr->someFunction(); // 컴파일 에러
// int x = ptr->someVariable; // 컴파일 에러
전방 선언 자체로는 메모리 할당도 하지 않고, 동적 메모리 할당도 런타임에 발생하기에 컴파일 속도가 빠르지만,
멤버함수나 변수 호출은 완전한 클래스가 정의된 이후에나 가능하다.
💬 "이렇게 얘기하면 전방 선언을 어떻게 사용해?"란 생각을 할 수도 있는데 클래스 설계에 따라 사용 방법이 달라지니 확인해 보자.
직접 선언을 해야 하는 경우
//UsingClass.h
#include "MyClass.h" //직접 선언
class UsingClass : public MyClass { //1. 상속
public:
MyClass mc; // 2. 객체 생성
public:
someFunction() {
mc.someFunction(); // 3. 멤버함수 호출
};
MyClass getFunction(); //4. MyClass 리턴
}
완전한 클래스 정의가 되어 있어야 위 4가지가 가능하기에 이런 케이스에는 직접 선언을 사용해야 한다.
전방 선언 사용형태
//UsingClass.h
class MyClass; // 전방선언
class UsingClass {
MyClass *mc;
public:
someFunction(MyClass *mc);
//UsingClass.cpp
#include "MyClass.h" //직접선언
UsingClass::someFuction(MyClass *mc){
mc = new MyClass();
}
하지만 이 코드처럼 UsingClass의 정의 시 MyClass가 굳이 정의되어 있을 필요가 없다면, 이 처럼 cpp파일에서 직접선언을 하고 헤더에서는 전방선언으로 순환참조를 막고 컴파일러 속도를 향상하는 방법으로 설계를 하길 바란다.
728x90
'L C++' 카테고리의 다른 글
🤔 스마트 포인터(unique_ptr, shared_ptr) 생성 시 make_shared(or make_unique)를 써야 하는 이유? (0) | 2025.03.14 |
---|---|
남들 쓰듯이 쓰는 virtual이라면 고민할 필요 없는 알쓸 virtual 정보 📖 (0) | 2024.10.06 |
trivial 뜻 : 하찮은, 그렇지만 하찮지 않은 trivial‼ (0) | 2024.08.25 |
컴파일러와 페어 프로그래밍 - 특별 멤버 함수 편 🚀 (0) | 2024.08.15 |
'0' 대신 'nullptr'를 써야 하는 이유 (0) | 2024.08.04 |