본문 바로가기
C++ 200제/코딩 IT 정보

#ifndef #define 사용법이란 헤더 파일 중복 컴파일 (꼬임 방지)

by vicddory 2017. 2. 3.

#ifndef, #define 사용법, 헤더 파일 중복 컴파일, 꼬임 방지


헤더 파일은 서브 시스템이나 다른 코드에 추상화된 인터페이스를 전달하는 메커니즘이다. 한 가지 까다로운 부분은 여러 헤더 파일을 이용할 때 순환 참조나 중복 인크루드되는 경우를 피하는 것이다. 


이때 『#ifndef, #define』 사용법으로 해결할 수 있다. 이 두가지를 혼합해 헤더 파일이 중복 컴파일되는, 일명 헤더 파일 꼬임 방지도 할 수 있다.


#ifndef, #define 사용법, 헤더 파일 중복 컴파일, 꼬임 방지c++ header 한번만, 충돌 꼬임 방지


예를 들어 Logger 클래스를 작성해서 모든 오류 메시지를 로깅해야 할 때, 사용자 설정 정보를 받기 위해 Preferences 클래스를 사용해야 할 수 있다. 그런데 Preferences 클래스가 사용하는 헤더 중에서 의도하지 않게 Logger 클래스를 인클루드 해버릴 수 있다.


다음 코드에서 볼 수 있듯이 #ifndef #define 패턴을 이용하면 순환 참조나 중복 인클루드를 막을 수 있다. 모든 헤더 파일의 시작 부분에 #ifndef 지시자를 넣어서 특정 키워드가 정의되지 않았다는 것을 확인한다.

만약 키워드가 정의되었다면 컴파일러가 #endif 지시자가 있는 블록까지 코드를 무시한다. 만약 해당 키워드가 정의되어 있지 않다면 정상적으로 코드를 인클루드한다.


이러한 메커니짐을 인클루드 가드(include guards)라고 부른다.


1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef __LOGGER__
#define __LOGGER__
 
#include "Preferences.h"
 
class Logger
{
public:
static void setPreferences(const Preferences& inPrefs);
static void logError(const char* inError);
};
 
#endif // __LOGGER__
cs


만약 사용하는 컴파일러가 『#pragma once 지시자』를 지원한다면 같은 메커니즘을 다음처럼 더 간단하게 구현할 수 있다.


c++ 헤더 파일 중복 컴파일c++ header 한번만, 충돌 꼬임 방지




#pragma once

마이크로소프트 비주얼 C++와 GCC에서 지원된다.


1
2
3
4
5
6
7
8
9
10
#pragma once
 
#include "Preferences.h"
 
class Logger
{
public:
static void setPreferences(const Preferences& inPrefs);
static void logError(const char* inError);
};
cs


#ifndef ... #endif (#define 생략) 메커니즘이나 #pragma once 같은 지시자는 헤더 파일이 중복 인클루드 되는 것뿐만 아니라 심볼이 중복 정의되는 것도 막아준다.

예를 들어 A.h가 Logger.h를 인클루드하고 B.h도 Logger.h를 인클루드한다고 하자. 만약 소스 파일 App.cpp에서 두 헤더 파일 A.h와 B.h를 모두 인클루드 하더라도 Logger.h 자체에 중복 인클루드를 막는 장치가 되어있기 때문에 Logger 클래스의 중복 정의로 인한 컴파일 에러가 발생하지 않는다.


순환 참조에 대응하기 위해 포워드 선언이라는 기능도 제공된다. 어떤 클래스를 작성할 때 작성 중인 클래스를 광범위하게 참조하는 다른 클래스를 참조해야만 할 때가 있다.


그런데 그 클래스가 정의된 헤더 파일을 인클루드하게 되면 그 헤더에서 참조하고 있는 자기 자신도 인클루드하게 되어 정상적으로 컴파일될 수가 없다.


ifndef define 중복 컴파일 인클루드 가드c++ header 한번만, 충돌 꼬임 방지


이때는 참조하는 클래스가 있다는 사실만 컴파일러에 알려주고 상세한 정의 부분을 상황에 따라 생략할 수 있다. 당연하지만 정의부를 컴파일러에 알려주지 않았기 때문에 해당 소스에서 문제의 클래스를 직접 이용할 수는 없다.


  • 대신 나중에 해당 이름으로 클래스 정의를 찾아서 링크될 수는 있다. 그리고 이렇게 되려면 참조하는 클래스의 객체 자체를 선언해서는 안 되고 그 포인터나 참조를 사용해야 한다.


다음의 코드는 Logger 클래스에서 Preferences 클래스를 포워드 선언을 통해 참조하되 그 정의가 있는 헤더 파일은 인클루드하지 않는 예이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef __LOGGER__
#define __LOGGER__
 
class Preferences; // 포워드 선언
 
class Logger
{
public:
static void setPreferences(const Preferences& inPrefs);
static void logError(const char* inError);
};
 
#endif // __LOGGER__
cs


ps. 히잉.... 이건 정말 귀찮아....


  출처 : 전문가를 위한 C++11 1권 430쪽.

#ifndef, #define 사용법, 헤더 파일 중복 컴파일, 꼬임 방지

댓글