-
헤더파일은 뭐고 왜 필요한가?Programming🧑💻/Cpp 2025. 2. 12. 14:17
헤더 파일의 기본 개념
헤더 파일은 간단히 말해서 선언(Declaration)을 포함하는 파일입니다.
여기서 선언이란 "이런 것들이 어딘가에 있을 것이다."라고 컴파일러에게 알려주는 것을 의미합니다.
예를 들어, 다음과 같은 함수 선언은 "이런 함수가 어딘가에 있을 것이다."라고 알려주는 것입니다.
// math.h #ifndef MATH_H #define MATH_H int calculateSum(int a, int b); #endif
반면, 구현(Implementation)은 실제로 그 함수가 어떻게 동작하는지를 정의하는 것입니다.
// math.cpp #include "math.h" int calculateSum(int a, int b) { return a + b; }
위에서 정의한 헤더파일과 cpp 파일은 다음과 같이 사용될 수 있습니다.
// main.cpp #include "math.h" int main() { int result = calculateSum(5, 3); return 0; }
작성한 코드는 다음과 같은 절차를 거쳐 실행파일이 됩니다.
1. 개별 파일 컴파일 단계
- main.cpp를 컴파일 할때, 컴파일러는 #include를 통해 math.h 선언을 확인합니다.
- math.h 선언을 통해 calculateSum 함수가 "어딘가에 있을 것이며" , 두개의 int를 받아 int를 반환한다는 것을 알게 됩니다.
- 컴파일러는 위 정보를 바탕으로 main.cpp에서 calculateSum(5,3) 호출이 올바른지 검사할 수 있습니다.
- 만약 calculateSum(5.5, "HEllO")와 같이 잘못된 타입으로 호출하면, 컴파일러는 선언 정보를 보고 오류를 발견할 수 있습니다.
- main.cpp는 오브젝트 파일(main.o)로 컴파일되며, calculateSum 함수 호출 부분에는 "나중에 채워질 공간"이라는 표시가 남습니다.
2. 링킹 단계
- 링커는 모든 오브젝트 파일들(main.o, math.o)를 검사합니다.
- main.o에서 "나중에 채워질 공간"으로 표시된 calculateSum 함수 호출을 발견합니다.
- math.o 파일을 검사하여 calculateSum 함수의 실제 구현을 찾습니다.
- 발견한 구현의 메모리 주소를 main.o의 호출 부분에 연결(링크) 합니다.
헤더 파일이 없다면?
헤더파일의 필요성을 이해하기 위해, 헤더파일이 없는 상황을 가정해봅시다.
현재 도형의 넓이를 계산하는 기능이 필요한 프로젝트를 진행하고 있고 해당 기능은 세 개의 다른 파일에서 사용됩니다.
// graphics.cpp ... ... ... class Shape { public: double getCircleArea(double radius) { return 3.14159 * radius * radius; } double getRectangleArea(double width, double height) { return width * height; } };
// game.cpp ... ... ... class Shape { // 같은 코드를 복사해서 붙여넣음 public: double getCircleArea(double radius) { return 3.14159 * radius * radius; } double getRectangleArea(double width, double height) { return width * height; } };
// simulation.cpp ... ... ... class Shape { // 또 다시 같은 코드를 복사해서 붙여넣음 public: double getCircleArea(double radius) { return 3.14159 * radius * radius; } double getRectangleArea(double width, double height) { return width * height; } };
이제 원의 넓이를 계산할 때 사용하는 π 값을 더 정확하게 수정해야할 필요가 생겼다고 가정해봅시다. 그러면 다음과 같은 단계가 필요합니다.
- graphics.cpp 파일을 열어서 3.14159를 3.141592653589793으로 수정
- game.cpp 파일을 열어서 같은 수정
- simulation.cpp 파일을 열어서 또 같은 수정
이 과정에서 발생할 수 있는 문제들
- 하나라도 수정을 빼먹으면 프로그램의 다른 부분에서 다른 결과가 나옴
- 파일이 100개라면? 모든 파일을 찾아서 수정해야 함
- 나중에 삼각형 넓이 계산 기능을 추가하려면? 역시 모든 파일에 추가해야 함
하지만 헤더 파일을 사용한다면 상황이 다음과 같이 바뀝니다.
// shape.h #ifndef SHAPE_H #define SHAPE_H class Shape { public: double getCircleArea(double radius); double getRectangleArea(double width, double height); }; #endif // shape.cpp #include "shape.h" double Shape::getCircleArea(double radius) { return 3.14159 * radius * radius; } double Shape::getRectangleArea(double width, double height) { return width * height; } // graphics.cpp #include "shape.h" // Shape 클래스를 바로 사용할 수 있음 // game.cpp #include "shape.h" // 여기서도 동일한 Shape 클래스를 사용 // simulation.cpp #include "shape.h" // 여기서도 동일한 Shape 클래스를 사용
이제 π 값을 수정해야 할 때의 과정
- shape.cpp 파일만 열어서 수정하면 됩니다
- 모든 파일이 이 하나의 구현을 공유하므로, 자동으로 모든 곳에서 수정된 값을 사용하게 됩니다.
위와 같이 각 파일들에 흩어져 있던 동일한 기능을
헤더 파일을 이용해 선언부를 공유하도록하고 하나의 구현 파일을 이용해 사용하게 함으로써
- 코드 중복을 없애주고
- 수정이 필요할 때 한 곳만 수정하면 되며
- 새 기능 추가가 용이해집니다.
즉 헤더 파일의 도입으로 인해 코드 작성시 실수할 가능성이 크게 줄어들며 코드 관리가 훨씬 쉬워집니다.
'Programming🧑💻 > Cpp' 카테고리의 다른 글
Macro가 뭘까? (0) 2025.02.12 빌드 과정과 #include의 역할 이해하기 (0) 2025.02.12 L-value와 R-value (0) 2025.02.06 함수 호출시 매개변수 전달 방법 3가지 (0) 2025.02.05 콜 스택 동작을 통해 알아보는 함수 호출 (0) 2025.02.04