[c++] 헤더 파일에 C ++ 정의를 배치하는 것이 좋은 방법입니까?

C ++을 사용하는 개인 스타일은 항상 클래스 선언을 포함 파일에, .cpp 파일에 정의를 넣어야합니다. Loki의 C ++ 헤더 파일, 코드 분리에 대한 답변 과 매우 유사 합니다. 분명히, 내가이 스타일을 좋아하는 이유 중 일부는 아마도 Modula-2와 Ada를 코딩하는 데 소비했던 모든 해와 관련이있을 것입니다.이 두 가지 모두 사양 파일과 본문 파일과 유사한 체계를 가지고 있습니다.

가능한 모든 C ++ 선언이 가능한 경우 헤더 파일에 정의를 포함시켜야한다고 주장하는 C ++에 대한 지식이 많은 동료가 있습니다. 그는 이것이 유효한 대체 스타일이거나 심지어 약간 더 나은 스타일이라고 말하지는 않지만 이제는 모두가 C ++에 사용하는 보편적으로 받아 들여지는 새로운 스타일입니다.

나는 내가 예전만큼 유연하지 않았기 때문에, 나는 그와 함께 몇 명의 사람들을 더 볼 때까지이 밴드의 마차에 움켜 쥐는 것이 정말로 불안하지 않다. 그렇다면이 관용구는 얼마나 흔한가?

답변에 구조를 부여하기 위해 : 지금 은 길 입니까 , 매우 흔하고, 다소 흔하거나, 드물거나, 버그가 있습니까?



답변

동료가 잘못되었습니다. 일반적인 방법은 항상 .cpp 파일 (또는 원하는 확장자)에 코드를 넣고 헤더에 선언하는 것입니다.

헤더에 코드를 넣는 것이 때로는 장점이 있습니다. 이렇게하면 컴파일러가 더 영리하게 인라인 할 수 있습니다. 그러나 동시에 모든 코드는 컴파일러에 포함될 때마다 처리되어야하므로 컴파일 시간을 파괴 할 수 있습니다.

마지막으로, 모든 코드가 헤더 일 때 순환 객체 관계 (때때로 원하는 경우)를 갖는 것은 성가신 일입니다.

결론은, 당신이 맞았어요. 그는 틀 렸습니다.

편집 : 나는 당신의 질문에 대해 생각하고 있습니다. 그가 말한 것이 사실 인 경우 가 하나 있습니다 . 템플릿. boost와 같은 많은 최신 “현대”라이브러리는 템플릿을 많이 사용하며 종종 “헤더 만”입니다. 그러나 템플릿을 다룰 때 수행 할 수있는 유일한 방법이므로 템플릿을 다룰 때만 수행해야합니다.

편집 : 일부 사람들은 좀 더 설명을 원합니다. 여기에 “헤더 전용”코드 작성에 대한 단점이 있습니다.

주변을 검색하면 부스트를 처리 할 때 컴파일 시간을 줄이는 방법을 찾는 많은 사람들이 있습니다. 예 : Boost Asio를 사용하여 컴파일 시간을 줄이는 방법- 부스트가 포함 된 단일 1K 파일의 14 초 컴파일을 볼 수 있습니다. 14s는 “폭발”하지 않는 것 같지만 확실히 일반적인 것보다 훨씬 길며 매우 빠르게 누적 될 수 있습니다. 큰 프로젝트를 다룰 때. 헤더 전용 라이브러리는 상당히 측정 가능한 방식으로 컴파일 시간에 영향을줍니다. 부스트가 너무 유용하기 때문에 우리는 그것을 용납합니다.

또한 헤더에서만 수행 할 수없는 많은 것들이 있습니다 (부스트조차도 스레드, 파일 시스템 등과 같은 특정 부분에 링크 해야하는 라이브러리가 있습니다). 기본 예는 여러 정의 오류가 발생할 때 헤더 전용 라이브러리에 단일 전역 객체를 가질 수 없다는 것입니다 (단일 인 가증에 의존하지 않는 한). 참고 : C ++ 17의 인라인 변수는 나중에이 특정 예제를 실행할 수있게합니다.

마지막으로, 헤더 전용 코드의 예로 부스트를 사용하면 큰 세부 사항이 종종 누락됩니다.

Boost는 사용자 수준 코드가 아닌 라이브러리입니다. 자주 바뀌지 않습니다. 사용자 코드에서 모든 것을 헤더에 넣으면 작은 변화가있을 때마다 전체 프로젝트를 다시 컴파일해야합니다. 그것은 엄청난 시간 낭비입니다 (그리고 컴파일에서 컴파일로 변경되지 않는 라이브러리의 경우는 아닙니다). 헤더 / 소스와 더 나은 부분을 나눌 때 포워드 선언을 사용하여 포함을 줄이면 하루에 추가 할 때 재 컴파일 시간을 절약 할 수 있습니다.


답변

C ++ 코더들이 The Way에 동의 한 날 , 양들은 사자와 함께 누워 팔레스타인 사람들은 이스라엘 사람들을 받아들이고 고양이와 개들은 결혼 할 수있게됩니다.

.h와 .cpp 파일 사이의 분리는이 시점에서 대부분 임의적이며, 오래 전의 컴파일러 최적화의 흔적입니다. 내 눈에 선언은 헤더에 속하고 정의는 구현 파일에 속합니다. 그러나 그것은 단지 습관이 아니라 종교입니다.


답변

헤더 코드는 선언이 아닌 실제 코드를 변경할 때 헤더를 포함하는 모든 파일을 강제로 다시 컴파일해야하므로 일반적으로 나쁜 생각입니다. 또한 헤더를 포함하는 모든 파일에서 코드를 구문 분석해야하기 때문에 컴파일 속도가 느려집니다.

헤더 파일에 코드가있는 이유는 일반적으로 키워드 인라인이 제대로 작동하고 다른 cpp 파일에서 인스턴스화되는 템플릿을 사용할 때 필요하기 때문입니다.


답변

동료에게 알려주는 것은 대부분의 C ++ 코드를 최대한 활용하기 위해 템플릿해야한다는 개념입니다. 그리고 템플릿 화 된 경우 모든 것이 헤더 파일에 있어야 클라이언트 코드가이를보고 인스턴스화 할 수 있습니다. Boost와 STL에 충분하다면 우리에게 충분합니다.

나는이 견해에 동의하지 않지만 그것이 어디에서 왔는지에 대한 것입니다.


답변

나는 당신의 동료가 똑똑하고 당신도 정확하다고 생각합니다.

헤더에 모든 것을 넣는 것이 유용한 점은 다음과 같습니다.

  1. 헤더 및 소스를 작성 및 동기화 할 필요가 없습니다.

  2. 이 구조는 단순하며 순환 종속성이 없어서 코더가 “더 나은”구조를 만들도록 강요합니다.

  3. 이식성이 뛰어나 새 프로젝트에 쉽게 포함됩니다.

컴파일 시간 문제에 동의하지만 다음 사항에주의해야합니다.

  1. 소스 파일을 변경하면 헤더 파일이 변경되어 전체 프로젝트가 다시 컴파일됩니다.

  2. 컴파일 속도가 이전보다 훨씬 빠릅니다. 또한 오랜 시간 동안 고주파로 건설 할 프로젝트가있는 경우 프로젝트 설계에 결함이 있음을 나타낼 수 있습니다. 작업을 다른 프로젝트로 분리하면 모듈이이 문제를 피할 수 있습니다.

마지막으로 나는 단지 개인적인 관점에서 동료를 지원하고 싶습니다.


답변

종종 간단한 멤버 함수를 헤더 파일에 넣어 인라인 할 수 있습니다. 그러나 템플릿과 일관성을 유지하기 위해 코드 전체를 거기에 두려면? 일반 견과류입니다.

기억하십시오 : 어리석은 일관성은 작은 마음의 홉 고블린입니다 .


답변

Tuomas가 말했듯이 헤더는 최소화되어야합니다. 완료하기 위해 조금 확장하겠습니다.

저는 개인적으로 C++프로젝트 에서 4 가지 유형의 파일을 사용 합니다.

  • 공공의:
  • 전달 헤더 : 템플릿 등의 경우이 파일은 헤더에 나타날 전달 선언을 가져옵니다.
  • 헤더 :이 파일에는 전달 헤더가 포함되어 있으며 공개 할 모든 것을 선언하고 클래스를 정의합니다 …
  • 은밀한:
  • 개인 헤더 :이 파일은 구현을 위해 예약 된 헤더이며 헤더를 포함하고 도우미 함수 / 구조를 선언합니다 (예 : 술어 또는 술어). 불필요한 경우 건너 뛰십시오.
  • 소스 파일 : 개인 헤더 (또는 개인 헤더가없는 경우 헤더)를 포함하고 모든 것을 정의합니다 (템플릿이 아닌 …)

또한 이것을 다른 규칙과 결합합니다. 선언 할 수있는 것을 정의하지 마십시오. 물론 나는 거기에서 합리적이지만 (Pimpl을 모든 곳에서 사용하는 것은 상당히 번거 롭습니다).

그것은 #include내가 그것을 벗어날 수있을 때마다 헤더 의 지시문 보다 전방 선언을 선호한다는 것을 의미 합니다.

마지막으로 가시성 규칙도 사용합니다. 심볼의 범위를 최대한 제한하여 외부 범위를 오염시키지 않습니다.

그것을 종합하면 :

// example_fwd.hpp
// Here necessary to forward declare the template class,
// you don't want people to declare them in case you wish to add
// another template symbol (with a default) later on
class MyClass;
template <class T> class MyClassT;

// example.hpp
#include "project/example_fwd.hpp"

// Those can't really be skipped
#include <string>
#include <vector>

#include "project/pimpl.hpp"

// Those can be forward declared easily
#include "project/foo_fwd.hpp"

namespace project { class Bar; }

namespace project
{
  class MyClass
  {
  public:
    struct Color // Limiting scope of enum
    {
      enum type { Red, Orange, Green };
    };
    typedef Color::type Color_t;

  public:
    MyClass(); // because of pimpl, I need to define the constructor

  private:
    struct Impl;
    pimpl<Impl> mImpl; // I won't describe pimpl here :p
  };

  template <class T> class MyClassT: public MyClass {};
} // namespace project

// example_impl.hpp (not visible to clients)
#include "project/example.hpp"
#include "project/bar.hpp"

template <class T> void check(MyClass<T> const& c) { }

// example.cpp
#include "example_impl.hpp"

// MyClass definition

의 경우에만 필요합니다 다음 생명의 은인 여기에 대부분의 시간은 앞으로 헤더가 쓸모 있다는 것이다 typedeftemplate때문에 구현 헤더입니다)