C / C ++ 헤더 파일 순서 포함


288

어떤 순서로 파일을 포함시켜야합니까, 즉 한 헤더를 다른 헤더보다 먼저 포함시키는 이유는 무엇입니까?

예를 들어, 시스템 파일, STL 및 Boost가 로컬 포함 파일 이전 또는 이후로 이동합니까?


2
그리고 많은 답변이 Java 개발자가 별도의 헤더에 대해 결정한 이유입니다. :-) 그러나 정말 좋은 답변, 특히 자신의 헤더 파일을 독립적으로 만들 수있는 훈계.
Chris K

37
나는 100 개 이상의 투표권을 가지고 있고 일부 사람들에게 명백하게 흥미로운 질문이 "비 건설적"으로 폐쇄되는 방식이 마음에 든다.
Andreas


3
@mrt, SO는 수프 나치 커뮤니티를 강력하게 상기시킵니다 : 당신은 매우 엄격한 규칙을 따르거나 "적절한 답변 / 의견이 없습니다!" 그럼에도 불구하고 누군가 프로그래밍과 관련하여 어떤 문제가 있다면, 이것은 (보통) 첫 번째 사이트입니다 ..
Imago

답변:


289

컴파일 된 한 권장 순서가 없다고 생각합니다! 성가신 것은 일부 헤더가 다른 헤더를 먼저 포함해야 할 때입니다. 포함 순서가 아닌 헤더 자체에 문제가 있습니다.

개인적으로 선호하는 것은 지역에서 세계로, 각 하위 섹션을 사전 순으로하는 것입니다.

  1. 이 cpp 파일에 해당하는 h 파일 (해당되는 경우)
  2. 동일한 구성 요소의 헤더
  3. 다른 구성 요소의 헤더
  4. 시스템 헤더.

1.에 대한 나의 이론적 근거는 각 헤더 (cpp가있는)가 #include전제 조건없이 종료 될 수 있음을 증명해야한다는 것입니다 (터미 너스 테크니 쿠스 : 헤더는 "자체 포함"). 그리고 나머지는 논리적으로 거기에서 흘러 나오는 것 같습니다.


16
전역에서 로컬로 이동하고 소스 파일에 해당하는 헤더는 특별한 처리를받지 않는다는 것을 제외하고는 당신과 거의 동일합니다.
Jon Purdy

127
@ 존 : 나는 그 반대입니다! :-) myclass.cpp에 <string>에 <myclass.h>가 포함되어 있으면 빌드시 myclass.h 자체가 문자열에 의존 할 수있는 방법이 없다고 말하면 메소드가 숨겨진 종속성을 도입 할 수 있다고 주장합니다. 나중에 나 자신이나 다른 사람이 myclass.h를 포함하지만 문자열이 필요하지 않으면 cpp 또는 헤더 자체에서 수정 해야하는 오류가 발생합니다. 하지만 사람들이 장기적으로 더 잘 작동한다고 생각하는지 알고 싶습니다. 제안에 대한 답변을 게시하면 누가 "승리"하는지 알 수 있을까요? ;-)
squelart

3
일반적인 주문과 관련된 것은 Dave Abrahams의 추천에서 현재 사용하는 것입니다. 그리고 @squelart가 누락 된 헤더 포함을 로컬에서보다 일반적인 소스로 밝히는 것과 같은 이유를 지적합니다. 중요한 열쇠는 타사 및 시스템 라이브러리보다 실수를 저지를 가능성이 높다는 것입니다.
GrafikRobot

7
@PaulJansen 그것은 나쁜 습관이며, 숨어있는 대신 나쁜 습관을 고칠 수 있도록 그것을 날려 버릴 가능성이 높은 기술을 사용하는 것이 좋습니다. 현지에서 전 세계 FTW
bames53

10
@PaulJansen 예, 표준 행동을 무시하는 것을 언급하고있었습니다. 예를 들어 ODR을 깨는 것은 우연히 일어날 수있는 것처럼 우연히 발생할 수 있습니다. 해결책은 그러한 사고가 발생했을 때 숨기는 관행을 사용하는 것이 아니라 가능한 한 큰 소리로 폭파시킬 수있는 관행을 사용하여 실수를 가능한 빨리 발견하고 수정할 수 있도록하는 것입니다.
bames53

106

명심해야 할 것은 헤더가 먼저 포함되는 다른 헤더에 의존해서는 안된다는 것입니다. 이를 보장하는 한 가지 방법은 다른 헤더보다 먼저 헤더를 포함시키는 것입니다.

Lakos의 "대규모 C ++ 소프트웨어 디자인"을 참조하여 "C ++로 생각하기"는 특히 이것을 언급합니다.

외부 제공 선언이나 정의없이 구성 요소의 .h 파일이 자체적으로 구문 분석되도록하여 잠재적 사용 오류를 피할 수 있습니다. .h 파일을 .c 파일의 첫 번째 행으로 포함하면 중요한 부분이 없어집니다. 구성 요소의 물리적 인터페이스에 내재 된 정보가 .h 파일에서 누락되었거나 존재하는 경우 .c 파일을 컴파일하려고하면 정보를 찾을 수 있습니다.

즉, 다음 순서로 포함하십시오.

  1. 이 구현을위한 프로토 타입 / 인터페이스 헤더 (즉,이 .cpp / .cc 파일에 해당하는 .h / .hh 파일)
  2. 필요에 따라 동일한 프로젝트의 다른 헤더
  3. 다른 비표준 비 시스템 라이브러리의 헤더 (예 : Qt, Eigen 등)
  4. 다른 "거의 표준"라이브러리의 헤더 (예 : Boost)
  5. 표준 C ++ 헤더 (예 : iostream, 기능 등)
  6. 표준 C 헤더 (예 : cstdint, dirent.h 등)

헤더에이 순서에 포함되는 데 문제가있는 경우 헤더를 수정하거나 사용하지 마십시오. 깔끔한 헤더를 작성하지 않는 보이콧 라이브러리.

구글의 C ++ 스타일 가이드는 주장 거의 모두 정말 정당화와, 반대로; 나는 개인적으로 Lakos 접근법을 선호하는 경향이 있습니다.


13
현재 Google C ++ 스타일 가이드 는 Lakos의 제안에 따라 관련 헤더 파일을 먼저 포함 할 것을 권장합니다.
Filip Bártek

프로젝트 내 헤더를 포함하기 시작하면 많은 시스템 종속성을 가져 오기 때문에 첫 번째 관련 헤더를 넘어서는 것은 아닙니다.
미가

@Micah- "많은 시스템 의존성"을 끌어내는 프로젝트 내 헤더는 나쁜 디자인입니다. 정확하게 우리가 여기서 피하려고하는 것은 아닙니다. 요점은 불필요한 포함과 해결되지 않은 종속성을 피하는 것입니다. 다른 헤더를 먼저 포함하지 않고 모든 헤더를 포함 할 수 있어야합니다. 프로젝트 내 헤더에 시스템 종속성이 필요한 경우 해당 파일에 대한 로컬 코드가 해당 시스템 뎁의 항목을 사용하지 않는 한 시스템 종속성을 포함하지 않아야합니다. 사용하는 시스템 뎁을 포함하기 위해 헤더 (자신의 헤더)에 의존해서는 안됩니다.
Nathan Paul Simons

49

나는 대부분의 문제를 피하는 두 가지 간단한 규칙을 따릅니다.

  1. (실제로 모든 헤더 모든 소스 파일) 그들이 필요로하는 것을 포함해야한다. 사물을 포함하여 사용자에게 의존 해서는 안됩니다 .
  2. 부가 적으로, 모든 헤더에는 위의 규칙 1을 과도하게 적용하여 여러 번 포함되지 않도록 가드가 있어야합니다.

또한 다음 지침을 따릅니다.

  1. 구분선이있는 시스템 헤더를 먼저 포함하십시오 (stdio.h 등).
  2. 논리적으로 그룹화하십시오.

다시 말해:

#include <stdio.h>
#include <string.h>

#include "btree.h"
#include "collect_hash.h"
#include "collect_arraylist.h"
#include "globals.h"

비록 지침이기는하지만 주관적인 것입니다. 반면에 규칙은 난해한 제 3 자 개발자가 내 비전에 가입하지 않은 경우 포함 가드 및 그룹화 된 포함으로 '래퍼'헤더 파일을 제공하는 시점까지 엄격하게 시행합니다.


6
+1 "모든 헤더 (실제로 소스 파일)에는 필요한 것이 포함되어야합니다. 사물을 포함하여 사용자에게 의존해서는 안됩니다." 그러나 많은 사람들이 NULL과 같은이 암시 적 포함 동작에 의존하고 <cstddef>를 포함하지 않습니다. 이 코드를 이식하려고 할 때 NULL에서 컴파일 오류가 발생하는 것은 매우 성가신 일입니다 (한 가지 이유는 지금 0을 사용합니다).
stinky472

20
왜 시스템 헤더를 먼저 포함합니까? 첫 번째 규칙 때문에 다른 이유가 더 좋을 것입니다.
jhasse

기능 테스트 매크로를 사용하는 경우 첫 번째 포함은 표준 라이브러리 헤더가 아니어야합니다. 따라서 일반적으로 "로컬 우선, 나중에 글로벌"정책이 가장 좋습니다.
hmijail는 사임 자 애도한다

1
"사용자에게 의존하지 않음"에 대한 첫 번째 제안과 관련하여 헤더 파일에 포함 할 필요가없는 헤더 파일의 전달 선언은 어떻습니까? 앞으로 선언하면 헤더 파일의 사용자에게 적절한 파일을 포함시키기 위해 onus를 배치하기 때문에 헤더 파일을 계속 포함해야합니다.
Zoso

22

벽에 내 자신의 벽돌을 추가합니다.

  1. 각 헤더는 자급 자족해야하며, 적어도 한 번 이상 포함 된 경우에만 테스트 할 수 있습니다
  2. 심볼 (매크로, 유형 등)을 도입하여 타사 헤더의 의미를 실수로 수정해서는 안됩니다.

그래서 나는 보통 다음과 같이 간다 :

// myproject/src/example.cpp
#include "myproject/example.h"

#include <algorithm>
#include <set>
#include <vector>

#include <3rdparty/foo.h>
#include <3rdparty/bar.h>

#include "myproject/another.h"
#include "myproject/specific/bla.h"

#include "detail/impl.h"

각 그룹은 다음 줄과 빈 줄로 구분됩니다.

  • 이 cpp 파일에 먼저 대응하는 헤더 (신성 검사)
  • 시스템 헤더
  • 종속성 순서로 구성된 타사 헤더
  • 프로젝트 헤더
  • 프로젝트 개인 헤더

또한 시스템 헤더와는 별도로 각 파일은 네임 스페이스 이름이있는 폴더에 있습니다. 이러한 방식으로 파일을 쉽게 추적 할 수 있기 때문입니다.


2
따라서 다른 헤더 파일은 영향을받지 않습니다. 이러한 시스템 헤더가 정의하는 것 (X 포함 및 Windows 포함 모두 #define다른 코드를 망칠 수있는 것에 대해 나쁜 것 )과 암시 적 종속성을 방지합니다. 예를 들어, 코드베이스 헤더 파일이 foo.h실제로 의존 <map>하지만 .cc파일 에 사용 된 모든 곳 <map>에서 이미 포함 된 경우에는 눈치 채지 못할 것입니다. 누군가가 foo.h먼저 포함하지 않고 포함하려고 할 때까지 <map>. 그리고 그들은 화가 날 것입니다.

@ 0A0D : 두 번째 문제는 여기에서 순서대로 문제가되지 않습니다. 각 문제에는 먼저 문제를 포함하는 문제가 .h하나 이상 있기 때문입니다 .cpp(실제로 내 개인 코드에서는 단위 테스트와 관련된 문제가 먼저 포함되며 소스 코드는 올바른 그룹에 포함됨) ). 영향을받지 않는 것과 관련하여 헤더에 포함 <map>된 경우 이후에 포함 된 모든 헤더가 영향을 받으므로 나에게 싸움을 잃는 것처럼 보입니다.
Matthieu M.

1
물론, 나는 정기적으로 돌아가서 오래된 코드 (또는 최신 코드)를 수정하여 빌드 시간을 늘리기 때문에 불필요한 포함이 필요합니다.

@MatthieuM. 나는 당신의 요점 하나의 근거가되는 근거를 알고 싶습니다 Header corresponding to this cpp file first (sanity check). #include "myproject/example.h"모든 포함의 끝으로 이동 하면 특별한 것이 있습니까?
MNS

1
@MNS : 헤더는 독립형이어야합니다. 즉, 다른 헤더를 포함하지 않아도됩니다. 이를 확인하는 것은 헤더 작성자로서 귀하의 책임이며,이를 수행하는 가장 좋은 방법은이 헤더가 먼저 포함 된 하나의 소스 파일을 갖는 것입니다. 헤더 파일에 해당하는 소스 파일을 사용하는 것은 쉽지만 헤더 파일에 해당하는 단위 테스트 소스 파일을 사용하는 것이 좋지만 보편적이지 않습니다 (단위 테스트가 없을 수 있음).
Matthieu M.

16

나는 추천한다 :

  1. 빌드중인 .cc 모듈의 헤더입니다. 프로젝트의 각 헤더가 프로젝트의 다른 헤더에 암시 적으로 종속되지 않도록합니다.
  2. C 시스템 파일.
  3. C ++ 시스템 파일.
  4. 플랫폼 / OS / 기타 헤더 파일 (예 : win32, gtk, openGL).
  5. 프로젝트의 다른 헤더 파일.

물론 가능하면 각 섹션 내에서 알파벳 순서로 정렬됩니다.

#include헤더 파일에서 불필요한을 피하려면 항상 선언을 사용 하십시오.


+1인데 왜 알파벳순입니까? 기분이 나아질 수 있지만 실질적인 이점은없는 것 같습니다.
Ben

9
알파벳순은 임의의 순서이지만 쉬운 것입니다. 사전 순으로 할 필요는 없지만 모든 사람이 일관되게 수행하도록 순서를 선택해야합니다. 중복을 피하고 병합을 쉽게하는 데 도움이됩니다. 그리고 숭고한 텍스트를 사용하면 F5가 대신 주문합니다.
i_am_jorf

14

나는 이것이 제정신 세계 어디에서나 권장되는 관행이 아니라고 확신하지만, 시스템 길이를 파일 이름 길이별로 정렬하고 같은 길이 내에서 어휘 적으로 정렬하는 것을 좋아합니다. 이렇게 :

#include <set>
#include <vector>
#include <algorithm>
#include <functional>

포함 순서 종속성의 부끄러움을 피하기 위해 다른 사람들보다 자신의 헤더를 포함시키는 것이 좋습니다.


3
나는 순서대로 두 번째, 세 번째, 첫 번째 문자로 구성된 키를 사용하여 헤더를 정렬하고 싶습니다.
paxdiablo

@paxdiablo, 팁 주셔서 감사합니다. 나는 그것을 사용하는 것을 고려하고 있지만, 파일 이름의 더미가 불안정하고 넘어 질 가능성이 있다고 생각합니다. 어쩌면 - 누가 이런 경우 포함될 수 있습니다 알고 windows.h.
clstrfsck

40
길이 별로 정렬 ? 광기!
James McNellis

1
첫 번째 +1 실제로 눈으로 파일 내에서 헤더를 찾아야하는 경우 알파벳보다 훨씬 좋습니다.
Kugel

6

이것은 주관적이지 않습니다. 헤더가 #include특정 순서 로 d에 의존하지 않도록하십시오 . STL 또는 Boost 헤더를 포함하는 순서가 중요하지 않을 수 있습니다.


1
나는 암묵적인 의존성이 없다고 가정했다
Anycorn

예. 그러나 컴파일러는 이러한 가정을 할 수 없으므로 #include <A>, <B>는 컴파일 될 때까지 #include <B>, <A>와 절대 동일하지 않습니다.
Mikhail

4

먼저 .cpp ...에 해당하는 헤더를 포함하십시오. 즉, 다른 것을 source1.cpp포함 source1.h하기 전에 포함 해야 합니다. 내가 생각할 수있는 유일한 예외는 미리 컴파일 된 헤더와 함께 MSVC를 사용할 때입니다.이 경우 stdafx.h다른 것보다 먼저 포함시켜야 합니다.

추론 :source1.h 다른 파일 이전을 포함하면 파일이 종속되지 않고 독립형 일 수 있습니다. source1.h나중에 의존성을 취하는 경우 컴파일러는 필요한 전달 선언을 추가하도록 즉시 경고합니다.source1.h . 그러면 종속 항목이 헤더를 순서에 상관없이 포함시킬 수 있습니다.

예:

source1.h

class Class1 {
    Class2 c2;    // a dependency which has not been forward declared
};

source1.cpp

#include "source1.h"    // now compiler will alert you saying that Class2 is undefined
                    // so you can forward declare Class2 within source1.h
...

MSVC 사용자 : 사전 컴파일 된 헤더를 사용하는 것이 좋습니다. 따라서 #include표준 헤더 (및 변경되지 않는 다른 헤더)에 대한 모든 지시문을 로 이동하십시오 stdafx.h.


2

.cpp에 해당하는 .hpp (있는 경우)부터 시작하여 가장 구체적인 것부터 가장 작은 것까지 포함하십시오. 이렇게하면 충분하지 않은 헤더 파일의 숨겨진 종속성이 표시됩니다.

사전 컴파일 된 헤더를 사용하면 복잡합니다. 이 문제를 해결하는 한 가지 방법은 프로젝트 컴파일러별로 만들지 않고 사전 컴파일 된 헤더 포함 파일로 프로젝트 헤더 중 하나를 사용하는 것입니다.


1

C / C ++ 세계에서는 어려운 질문이며 표준을 넘어서는 많은 요소가 있습니다.

squelart가 말한 것처럼 헤더 파일 순서가 컴파일되는 한 심각한 문제는 아니라고 생각합니다.

내 아이디어는 : 모든 헤더에 기호 충돌이 없으면 순서가 정상이며 결함이있는 .h에 #include 행을 추가하여 헤더 종속성 문제를 나중에 해결할 수 있습니다.

어떤 헤더가 위에 있는지에 따라 일부 헤더가 동작을 변경하면 (#if 조건을 확인하여) 번거 로움이 발생합니다.

예를 들어 VS2005의 stddef.h에는 다음이 있습니다.

#ifdef  _WIN64
#define offsetof(s,m)   (size_t)( (ptrdiff_t)&(((s *)0)->m) )
#else
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
#endif

이제 문제 : offsetof시스템 헤더에 제공되지 않는 이전 컴파일러를 포함하여 많은 컴파일러와 함께 사용해야하는 사용자 지정 헤더 ( "custom.h")가 있으면 헤더에 작성해야합니다.

#ifndef offsetof
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
#endif

그리고 모든 시스템 헤더 #include "custom.h" 다음에 사용자에게 알려주십시오 . 그렇지 않으면 offsetofstddef.h 의 행에 매크로 재정의 오류가 발생합니다.

우리는 경력에서 더 이상 그러한 경우를 만나지 않기를기도합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.