.h 파일에는 무엇을 넣어야합니까?


93

코드를 여러 파일로 나눌 때 정확히 .h 파일에 들어가야하는 것과 .cpp 파일에 들어가야하는 것은 무엇입니까?



7
이것은 순수한 스타일 문제이지만 .hppC 선언은 .h파일에 들어가고 C 선언은 파일에 들어간다고 생각 합니다. 이는 C와 C ++ 코드를 혼합 할 때 매우 유용합니다 (예 : C의 레거시 모듈).
Thomas Matthews

@ThomasMatthews 말이 되네요. 그 관행이 자주 사용됩니까?
ty

@lightningleaf : 예, 특히 C ++와 C 언어를 혼합 할 때 자주 사용됩니다.
Thomas Matthews

답변:


113

헤더 파일 ( .h)은 여러 파일에 필요한 정보를 제공하도록 설계되었습니다. 클래스 선언, 함수 프로토 타입 및 열거와 같은 것은 일반적으로 헤더 파일에 들어갑니다. 한마디로 "정의".

코드 파일 ( .cpp)은 하나의 파일에서만 알아야하는 구현 정보를 제공하도록 설계되었습니다. 일반적으로 다른 모듈에서 액세스해야하거나 절대로 액세스하지 않을 함수 본문 및 내부 변수는 .cpp파일에 속합니다 . 한마디로 "구현".

자신에게 무엇이 속하는지 스스로에게 물어 보는 가장 간단한 질문은 "이것을 변경하면 다시 컴파일되도록 다른 파일의 코드를 변경해야합니까?"입니다. 대답이 "예"이면 헤더 파일에있을 수 있습니다. 대답이 "아니오"이면 아마도 코드 파일에있을 것입니다.


4
개인 클래스 데이터를 제외하고는 헤더로 이동해야합니다. 템플릿은 완전히 헤더로 정의되어야합니다 (를 지원하는 몇 안되는 컴파일러 중 하나를 사용하지 않는 한 export). # 1 주변의 유일한 방법은 PIMPL입니다. # 2 export는 지원되는 경우 가능하며 C ++ 0x 및 extern템플릿을 사용하여 가능할 수 있습니다 . IMO, C ++의 헤더 파일은 많은 유용성을 잃습니다.
KitsuneYMG 2009

23
모두 좋지만 용어가 정확하지 않습니다. 한마디로 "선언"- "정의"라는 용어는 "구현"과 동의어입니다. 선언적 코드, 인라인 코드, 매크로 정의 및 템플릿 코드 만 헤더에 있어야합니다. 즉, 코드 나 데이터를 인스턴스화하는 것은 없습니다.
Clifford

8
나는 Clifford에 동의해야합니다. 당신은 선언과 정의라는 용어를 다소 느슨하고 다소 번갈아 사용합니다. 그러나 그들은 C ++에서 정확한 의미를 가지고 있습니다. 예 : 클래스 선언은 클래스의 이름을 소개하지만 그 안에있는 내용은 말하지 않습니다. 클래스 정의는 모든 멤버와 친구 기능을 나열합니다. 둘 다 문제없이 헤더 파일에 넣을 수 있습니다. "함수 프로토 타입"이라고 부르는 것은 함수 선언 입니다. 그러나 함수 정의 는 함수의 코드를 포함하며 인라인이거나 템플릿의 일부가 아닌 한 cpp 파일에 배치되어야하는 것입니다.
sellibitze

5
C ++에서는 정확한 의미가 있지만 영어에서는 정확한 의미가 없습니다. 내 대답은 후자에 기록되었습니다.
Amber

54

사실, C ++에서 이것은 C 헤더 / 소스 조직보다 다소 복잡합니다.

컴파일러는 무엇을 봅니까?

컴파일러는 헤더가 제대로 포함 된 하나의 큰 소스 (.cpp) 파일을 봅니다. 소스 파일은 개체 파일로 컴파일되는 컴파일 단위입니다.

그렇다면 헤더가 필요한 이유는 무엇입니까?

한 컴파일 단위에 다른 컴파일 단위의 구현에 대한 정보가 필요할 수 있기 때문입니다. 예를 들어 한 소스에서 함수의 구현을 작성하고이를 사용해야하는 다른 소스에서이 함수의 선언을 작성할 수 있습니다.

이 경우 동일한 정보의 사본이 두 개 있습니다. 어느 것이 악 ...

해결책은 몇 가지 세부 사항을 공유하는 것입니다. 구현은 소스에 남아 있어야하지만 함수와 같은 공유 심볼의 선언이나 구조, 클래스, 열거 형 등의 정의는 공유해야 할 수 있습니다.

헤더는 공유 된 세부 정보를 입력하는 데 사용됩니다.

여러 소스간에 공유해야하는 항목의 선언을 헤더로 이동

더 이상은 없나요?

C ++에는 공유가 필요하기 때문에 헤더에 넣을 수있는 다른 것들이 있습니다.

  • 인라인 코드
  • 템플릿
  • 상수 (일반적으로 스위치 내부에서 사용하려는 것 ...)

공유 구현을 포함하여 공유해야 할 모든 것을 헤더로 이동

그러면 헤더 내부에 소스가있을 수 있다는 의미입니까?

예. 사실, "헤더"안에있을 수있는 많은 다른 것들이 있습니다 (즉, 소스간에 공유 됨).

  • 앞으로 선언
  • 함수 / 구조체 / 클래스 / 템플릿의 선언 / 정의
  • 인라인 및 템플릿 코드 구현

복잡해지고 어떤 경우에는 (심볼 간의 순환 종속성) 하나의 헤더에 보관할 수 없습니다.

헤더는 세 부분으로 나눌 수 있습니다.

즉, 극단적 인 경우 다음을 수행 할 수 있습니다.

  • 전방 선언 헤더
  • 선언 / 정의 헤더
  • 구현 헤더
  • 구현 소스

템플릿 MyObject가 있다고 가정 해 봅시다. 우리는 다음을 가질 수 있습니다.

// - - - - MyObject_forward.hpp - - - - 
// This header is included by the code which need to know MyObject
// does exist, but nothing more.
template<typename T>
class MyObject ;

.

// - - - - MyObject_declaration.hpp - - - - 
// This header is included by the code which need to know how
// MyObject is defined, but nothing more.
#include <MyObject_forward.hpp>

template<typename T>
class MyObject
{
   public :
      MyObject() ;
   // Etc.
} ;

void doSomething() ;

.

// - - - - MyObject_implementation.hpp - - - - 
// This header is included by the code which need to see
// the implementation of the methods/functions of MyObject,
// but nothing more.
#include <MyObject_declaration.hpp>

template<typename T>
MyObject<T>::MyObject()
{
   doSomething() ;
}

// etc.

.

// - - - - MyObject_source.cpp - - - - 
// This source will have implementation that does not need to
// be shared, which, for templated code, usually means nothing...
#include <MyObject_implementation.hpp>

void doSomething()
{
   // etc.
} ;

// etc.

와!

"실생활"에서는 일반적으로 덜 복잡합니다. 대부분의 코드에는 소스에 일부 인라인 코드가 포함 된 단순한 헤더 / 소스 구성 만 있습니다.

그러나 다른 경우 (서로 알고있는 템플릿 객체)에서는 컴파일 오류를 확인하는 데 도움이되도록 해당 헤더를 포함하는 빈 소스와 함께 각 객체에 대해 별도의 선언 및 구현 헤더를 가져야했습니다.

헤더를 별도의 헤더로 나누는 또 다른 이유는 컴파일 속도를 높이고, 필요에 따라 파싱되는 심볼의 수를 제한하고, 인라인 메서드 구현이 변경되었을 때 포워드 선언 만 신경 쓰는 소스의 불필요한 재 컴파일을 피하는 것입니다.

결론

코드 구성은 가능한 한 단순하고 가능한 모듈 식으로 만들어야합니다. 소스 파일에 가능한 많이 넣으십시오. 공유해야하는 항목 만 헤더에 노출하십시오.

그러나 템플릿 객체간에 순환 종속성이있는 날에는 코드 구성이 일반 헤더 / 소스 구성보다 "흥미 롭다"고해서 놀라지 마십시오.

^ _ ^


17

다른 모든 답변 외에도 헤더 파일에 넣지 말아야 할 내용을 알려 드리겠습니다.
using선언 (가장 일반적인 존재 using namespace std;)은 포함 된 소스 파일의 네임 스페이스를 오염 시키므로 헤더 파일에 나타나지 않아야합니다. .


+1 세부 사항 네임 스페이스 (또는 익명 네임 스페이스)에있는 한 사용할 수 있다는 점에주의하십시오. 그러나 예, using헤더의 전역 네임 스페이스로 물건을 가져 오는 데 사용하지 마십시오 .
KitsuneYMG 2009

+1 이것은 대답하기가 훨씬 쉽습니다. :) 또한 헤더 파일은 익명 네임 스페이스를 포함 하지 않아야 합니다 .
sellibitze 2009

그것이 의미하는 바를 이해한다면 헤더 파일에 익명 네임 스페이스를 포함하는 것은 괜찮습니다. 즉, 각 번역 단위는 네임 스페이스를 정의한 내용의 다른 복사본을 갖게됩니다. 익명 네임 스페이스의 인라인 함수 static inline는 내부 연결을 템플릿과 결합 할 때 발생하는 일과 관련이 있기 때문에 C99에서 사용하는 경우 C ++에서 권장됩니다 . Anon 네임 스페이스를 사용하면 외부 연결을 유지하면서 기능을 "숨길"수 있습니다.
Steve Jessop

스티브, 당신이 쓴 글이 나를 설득하지 못했습니다. anon 네임 스페이스가 헤더 파일에서 전체적으로 의미가 있다고 생각하는 구체적인 예를 선택하십시오.
sellibitze 2009

6

무엇 아무것도로 컴파일 (영 진 풋 프린트) 헤더 파일로 들어갑니다.

변수는 아무것도 컴파일되지 않지만 유형 선언은 수행합니다 (변수가 작동하는 방식 만 설명하므로).

함수는 그렇지 않지만 인라인 함수는 호출 된 위치에서만 코드를 생성하기 때문에 수행합니다 (또는 매크로).

템플릿은 코드가 아니라 코드를 만드는 방법 일뿐입니다. 그래서 그들은 또한 h 파일로 이동합니다.


1
"인라인 함수 ... 호출 된 곳에서만 코드 생성". 그건 사실이 아니야. 인라인 함수는 호출 사이트에서 인라인 될 수도 있고 아닐 수도 있지만 인라인 된 경우에도 실제 함수 본문은 인라인이 아닌 함수와 마찬가지로 여전히 존재합니다. 헤더에 인라인 함수를 넣어도 괜찮은 이유는 코드 생성 여부와 관련이 없습니다. 인라인 함수는 하나의 정의 규칙을 트리거하지 않기 때문입니다. 따라서 인라인이 아닌 함수와 달리 두 개의 다른 번역 단위를 연결하는 데 문제가 없습니다. 둘 다 헤더를 포함했습니다.
Steve Jessop

3

일반적으로 헤더 파일에는 선언을, 구현 (.cpp) 파일에는 정의를 넣습니다. 이에 대한 예외는 정의가 헤더에 있어야하는 템플릿입니다.

이 질문과 이와 유사한 질문은 자주 묻는 질문 입니다. C ++에 헤더 파일과 .cpp 파일이있는 이유를 참조하십시오 . C ++ 헤더 파일, 코드 분리 예.


물론 클래스 정의 를 헤더 파일에 넣을 수도 있습니다. 템플릿이 아니어도됩니다.
sellibitze

1

클래스 및 함수 선언과 문서, 인라인 함수 / 메소드에 대한 정의 (일부는 별도의 .inl 파일에 배치하는 것을 선호하지만).


1

주로 헤더 파일에는 클래스 스켈레톤 또는 선언이 포함됩니다 (자주 변경되지 않음).

cpp 파일에는 클래스 구현이 포함되어 있습니다 (자주 변경됨).


5
비표준 용어는 사용하지 마십시오. "클래스 스켈레톤"은 무엇이며 "클래스 구현"은 무엇입니까? 또한 클래스 컨텍스트에서 선언이라고 부르는 것은 아마도 클래스 정의를 포함 할 것입니다.
sellibitze 2009

0

헤더 파일 (.h)은 클래스, 구조체 및 메서드, 프로토 타입 등의 선언을위한 것이어야합니다. 이러한 객체의 구현은 cpp에서 이루어집니다.

.h에서

    class Foo {
    int j;

    Foo();
    Foo(int)
    void DoSomething();
}

0

나는 기대한다 :

  • 선언
  • 코멘트
  • 인라인으로 표시된 정의
  • 템플릿

하지만 정말 답은 넣지 말아야 할 것입니다.

  • 정의 (복수 정의로 이어질 수 있음)
  • 선언 / 지시문 사용 (헤더를 포함한 모든 사람에게 강제로 지정하면 이름 충돌이 발생할 수 있음)

1
클래스 정의 를 헤더 파일에도 확실히 넣을 수 있습니다 . 클래스 선언은 그 구성원에 대해 아무 말도하지 않습니다.
sellibitze

0

헤더 정의 무언가를 하지만 구현에 대해 알려주지 않습니다. (이 "metafore"의 템플릿 제외.

즉, "정의"를 하위 그룹으로 분할해야합니다.이 경우 두 가지 유형의 정의가 있습니다.

  • 주변 사용 그룹에 필요한만큼만 알려주는 구조의 "레이아웃"을 정의합니다.
  • 변수, 함수 및 클래스의 정의.

이제 저는 물론 첫 번째 하위 그룹에 대해 이야기하고 있습니다.

헤더는 나머지 소프트웨어가 구현을 사용하도록 돕기 위해 구조의 레이아웃을 정의하기위한 것입니다. 당신은 그것을 당신의 구현의 "추상화"로보고 싶을지도 모릅니다. 이것은 우스꽝스럽게 말하지만, 저는 이것이이 경우에 아주 적합하다고 생각합니다.

이전 포스터에서 개인 및 공용 사용 영역과 헤더를 선언하고 표시했듯이 여기에는 개인 및 공용 변수도 포함됩니다. 이제 여기에서 코드를 디자인하고 싶지는 않지만 헤더에 넣은 내용은 최종 사용자와 구현 사이의 레이어이므로 고려할 수 있습니다.


0
  • 헤더 파일-개발 중에 너무 자주 변경해서는 안됩니다.-> 생각하고 한 번에 작성해야합니다 (이상적인 경우).
  • 소스 파일-구현 중 변경

이것은 하나의 관행입니다. 일부 소규모 프로젝트의 경우 갈 길이 될 수 있습니다. 그러나 시그니처를 변경하거나 제거하는 대신 함수와 프로토 타입 (헤더 파일에서)을 더 이상 사용하지 않을 수도 있습니다. 적어도 메이저 번호를 변경할 때까지. 1.9.2가 2.0.0 베타로 올라 갔을 때와 같습니다.
TamusJRoyce

0

헤더 (.h)

  • 인터페이스에 필요한 매크로 및 포함 (가능한 한 적음)
  • 함수와 클래스의 선언
  • 인터페이스 문서
  • 인라인 함수 / 메서드 선언 (있는 경우)
  • 전역 변수에 대한 extern (있는 경우)

본문 (.cpp)

  • 나머지 매크로 및 포함
  • 모듈 헤더 포함
  • 기능 및 방법의 정의
  • 전역 변수 (있는 경우)

경험상 모듈의 "공유"부분은 .h (다른 모듈이 볼 수 있어야하는 부분)에, "공유되지 않음"부분은 .cpp에 배치합니다.

PD : 네, 전역 변수를 포함 시켰습니다. 나는 그것들을 몇 번 사용했고 헤더에 그것들을 정의하지 않는 것이 중요합니다. 그렇지 않으면 여러분은 각각 자체 변수를 정의하는 많은 모듈을 얻게 될 것입니다.

편집 : David의 코멘트 이후 수정


경험상 .h 파일에는 가능한 한 적은 수의 include가 있어야하며 .cpp 파일에는 필요한 헤더가 포함되어야합니다. 이는 컴파일 시간을 단축하고 네임 스페이스를 오염시키지 않습니다.
David Thornley
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.