이 글을 읽기 전에 C++ 템플릿 프로그래밍에 대해 [검색]해보고 이해를 해보셔야 합니다. 여기서는 결론만 말합니다.
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef CLASSA_H #define CLASSA_H class A { public: A () {} A (int e) { e = element; } int foo() { return element; } private: int element=0; }; #endif |
보통 C++을 공부할 때 클래스를 배우게 되면 클래스의 선언과 구현 부분을 아래와 같이 분리하라고 배우게 됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef CLASSA_H #define CLASSA_H class A { public: A (); A (int); int foo(); private: int element=0; }; #endif |
A.h
1 2 3 |
A::A () {} A::A (int e) { e = element; } int A::foo() { return element; } |
A.cpp
그런데 템플릿 프로그래밍으로 넘어오게 되면 위와 같은 방식으로 클래스를 나누었을 때 링크 에러가 나타나게 됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#ifndef CLASSA_H #define CLASSA_H template <typename Object> class A { public: A (); A (Object); Object foo(); private: Object element=0; }; #endif |
A.h
1 2 3 4 5 6 7 8 |
template <typename Object> A::A () {} template <typename Object> A::A (Object e) { e = element; } template <typename Object> Object A::foo() { return element; } |
A.cpp
위와 같이 코드를 작성하면 빌드 시 링크 에러가 납니다.
보통 사람들은 “템플릿은 컴파일 타임에 모든것이 준비되어야 하므로 헤더 안에 구현까지 들어있어야 한다”고 얘기합니다. 그런데 반은 맞고 반은 틀린 말입니다. 꼭 구현까지 들어있을 필요는 없습니다. 아래와 같은 방법으로 해결할 수 있습니다.
13 |
#include "A.hpp" |
A.h
1 2 3 4 5 6 7 8 |
template <typename Object> A::A () {} template <typename Object> A::A (Object e) { e = element; } template <typename Object> Object A::foo() { return element; } |
A.hpp
C++에서는 템플릿도 선언과 구현을 나눌 수 있도록 hpp라는 파일 확장자를 지원합니다. 보통 IDE에선 헤더 파일로 인식합니다. 위와 같이 “클래스명.h”의 #endif 전처리 바로 앞에 #include 전처리를 이용하여 포함시키면 됩니다.
만약 “난 죽어도 선언과 구현을 한 파일 안에 담아두고 싶다”는 생각을 가진 분이 있다면 아래와 같이 해주면 됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#ifndef CLASSA_H #define CLASSA_H template <typename Object> class A { public: A (); A (Object); Object foo(); private: Object element=0; }; template <typename Object> A::A () {} template <typename Object> A::A (Object e) { e = element; } template <typename Object> Object A::foo() { return element; } // #include "A.hpp" #endif |
A.h
차이점을 아시겠나요? 전 이 해결법으로 인해 #include 전처리를 확실히 이해하게 되었습니다. #include는 해당 파일의 내용을 그냥 전처리 단계에서 때려박아버립니다. 우리가 마르고 닳도록 쓰던 <stdio.h>나 <iostream> 헤더도 사실은 컴파일 단계에선 우리의 프로그램 코드 맨 앞에 해당 헤더의 모든 것이 들어가게 되는 겁니다.
사실 단계까지 왔으면 #ifndef-#define-#endif를 써야하는 이유를 아시겠지만 혹시나 해서 덧붙여 설명해드리자면, 방금 말한것처럼 #include는 헤더 혹은 파일의 모든 내용을 박아버리는데 우리가 만든 여러 헤더 파일에서 특정 헤더를 다들 #include로 포함했다면 중복 포함이 생겨 컴파일 시에 오류가 발생할 수 있습니다. 보통 이것을 해결하는 방법이 두 가지입니다.
1 |
#pragma once |
첫 번째는 #pragma 키워드를 이용하는 방법입니다. 컴파일 시 전처리 단계에서 위와 같은 코드를 만나면 한 번만 포함하고 건너뛰게 됩니다.
1 2 3 4 |
#ifndef CLASSA_H #define CLASSA_H /* Some codes... */ #endif |
두 번째는 방금 말한 #ifndef-#define-#endif 키워드를 이용하는 방법입니다. 이것은 #define을 통한 정의가 되어 있지 않으면 내용을 포함시키고, 이미 되어있으면 건너뛰도록 하는 방법으로 중복 포함을 방지합니다.
감사합니다! 덕분에 2시간 동안 헤메던 문제를 해결했네요!
감사합니다 😊👍
너무너무 감사해요!!!! 엄청 유용한 것 같아요
감사합니다 ^~^ 좋은 하루 되세용
감사합니다. 많은 도움이 되었습니다.
큰 도움이 되었습니다. ㅎㅎㅎ