C ++에 헤더 파일과 .cpp 파일이있는 이유는 무엇입니까?
C ++에 헤더 파일과 .cpp 파일이있는 이유는 무엇입니까?
답변:
글쎄, 주된 이유는 인터페이스를 구현에서 분리하는 것입니다. 헤더는 클래스 (또는 구현되는 모든 것)가 수행 할 "무엇"을 선언하지만 cpp 파일은 "어떻게"기능을 수행 할 것인지를 정의합니다.
이것은 헤더를 사용하는 코드가 구현의 모든 세부 사항과 그에 필요한 다른 클래스 / 헤더를 알아야 할 필요가 없도록 종속성을 줄입니다. 이렇게하면 구현 시간이 변경 될 때 컴파일 시간과 재 컴파일 양이 줄어 듭니다.
완벽하지는 않으며 일반적으로 인터페이스와 구현을 올바르게 분리하기 위해 Pimpl Idiom 과 같은 기술에 의존 하지만 좋은 출발입니다.
C ++ 컴파일은 다음 두 가지 주요 단계로 수행됩니다.
첫 번째는 "원본"텍스트 파일을 이진 "객체"파일로 컴파일하는 것입니다. CPP 파일은 컴파일 된 파일이며 원시 선언 또는 헤더 포함. CPP 파일은 일반적으로 .OBJ 또는 .O "object"파일로 컴파일됩니다.
두 번째는 모든 "객체"파일을 연결하여 최종 이진 파일 (라이브러리 또는 실행 파일)을 만드는 것입니다.
HPP는이 모든 과정에서 어디에 적합합니까?
각 CPP 파일의 컴파일은 다른 모든 CPP 파일과 독립적이므로 A.CPP가 B.CPP에 정의 된 기호가 필요한 경우 다음과 같습니다.
// A.CPP
void doSomething()
{
doSomethingElse(); // Defined in B.CPP
}
// B.CPP
void doSomethingElse()
{
// Etc.
}
A.CPP에 "doSomethingElse"가 존재하는지 알 수있는 방법이 없기 때문에 컴파일되지 않습니다. A.CPP에 다음과 같은 선언이없는 한 :
// A.CPP
void doSomethingElse() ; // From B.CPP
void doSomething()
{
doSomethingElse() ; // Defined in B.CPP
}
그런 다음 동일한 기호를 사용하는 C.CPP가 있으면 선언을 복사 / 붙여 넣습니다.
예, 문제가 있습니다. 복사 / 붙여 넣기는 위험하며 유지 관리가 어렵습니다. 복사 / 붙여 넣기를하지 않고 심볼을 계속 선언 할 수있는 방법이 있다면 멋질 것입니다. 어떻게하면 되나요? 일반적으로 .h, .hxx, .h ++ 또는 C ++ 파일에 선호되는 .hpp 접미사로 일부 텍스트 파일을 포함하면 다음과 같습니다.
// B.HPP (here, we decided to declare every symbol defined in B.CPP)
void doSomethingElse() ;
// A.CPP
#include "B.HPP"
void doSomething()
{
doSomethingElse() ; // Defined in B.CPP
}
// B.CPP
#include "B.HPP"
void doSomethingElse()
{
// Etc.
}
// C.CPP
#include "B.HPP"
void doSomethingAgain()
{
doSomethingElse() ; // Defined in B.CPP
}
include
작동합니까?파일을 포함하면 본질적으로 CPP 파일의 내용을 구문 분석 한 후 복사하여 붙여 넣습니다.
예를 들어, A.HPP 헤더가있는 다음 코드에서 :
// A.HPP
void someFunction();
void someOtherFunction();
... 원본 B.CPP :
// B.CPP
#include "A.HPP"
void doSomething()
{
// Etc.
}
... 포함 후 :
// B.CPP
void someFunction();
void someOtherFunction();
void doSomething()
{
// Etc.
}
현재의 경우에는 이것이 필요하지 않으며 B.HPP에는 doSomethingElse
함수 선언이 있고 B.CPP에는doSomethingElse
함수 정의 (그 자체로 선언)가 있습니다. 그러나 B.HPP가 선언 (및 인라인 코드)에 사용되는보다 일반적인 경우에는 해당 정의 (예 : 열거 형, 일반 구조체 등)가 없으므로 B.CPP 인 경우 포함이 필요할 수 있습니다 B.HPP의 선언을 사용합니다. 대체로 소스가 기본적으로 헤더를 포함하는 것이 "좋은 맛"입니다.
C ++ 컴파일러는 기호 선언 만 검색 할 수 없으므로 헤더 파일이 필요하므로 이러한 선언을 포함 시켜서이를 지원해야합니다.
마지막으로 한마디 : HPP 파일의 내용 주위에 헤더 가드를 두어야 여러 포함이 아무 것도 깨지지 않도록해야합니다. 그러나 HPP 파일이 존재하는 주된 이유는 위에 설명되어 있습니다.
#ifndef B_HPP_
#define B_HPP_
// The declarations in the B.hpp file
#endif // B_HPP_
또는 더 간단한
#pragma once
// The declarations in the B.hpp file
You still have to copy paste the signature from header file to cpp file, don't you?
: 필요 없습니다. CPP가 HPP를 "포함"하는 한, 프리 컴파일러는 HPP 파일의 내용을 CPP 파일에 자동으로 복사하여 붙여 넣습니다. 나는 그것을 명확히하기 위해 대답을 업데이트했습니다.
While compiling A.cpp, compiler knows the types of arguments and return value of doSomethingElse from the call itself
. 아닙니다. 사용자가 제공 한 유형 만 알기 때문에 반 시간 동안 반환 값을 읽지 않아도됩니다. 그런 다음 암시 적 변환이 발생합니다. 그런 다음 코드 가 있으면 함수 foo(bar)
인지 확신 할 수 없습니다 foo
. 따라서 컴파일러는 헤더의 정보에 액세스하여 소스가 올바르게 컴파일되는지 여부를 결정해야합니다. 그런 다음 코드가 컴파일되면 링커는 함수 호출을 함께 연결합니다.
Seems, they're just a pretty ugly arbitrary design.
: C ++이 2012 년에 만들어 졌다면, 정말로. 그러나 C ++은 1980 년대에 C를 기반으로 구축되었으며 당시에는 제약 조건이 상당히 달랐습니다 (IIRC, 채택 목적으로 C와 동일한 링커를 유지하기로 결정했습니다).
foo(bar)
포인터로 얻은 경우 함수 인지 확실하지 않은 이유는 무엇 입니까? 사실, 나쁜 디자인에 대해서는 C ++이 아니라 C를 비난합니다. 나는 헤더 파일을 가지고 있거나 함수가 하나의 값만 반환하는 것과 같은 순수한 C의 제약을 좋아하지 않는다. ; 왜 여러개의 논증이 있지만, 단 하나의 결과물인가?) :)
Why can't I be sure, that foo(bar) is a function
: foo는 유형일 수 있으므로 클래스 생성자가 있습니다. In fact, speaking of bad design, I blame C, not C++
: 나는 많은 것들에 대해 C를 비난 할 수 있지만 70 년대에 설계된 것은 그중 하나가 아닙니다. 다시 말하지만, 그 시간의 제약은 ... such as having header files or having functions return one and only one value
: 튜플은 그것을 완화시키고 참조로 인수를 전달하는 데 도움이 될 수 있습니다. 이제 반환 된 여러 값을 검색하는 구문은 무엇이며 언어를 변경하는 것이 가치가 있습니까?
개념이 시작된 C는 30 년이 지난 지금, 여러 파일의 코드를 함께 연결할 수있는 유일한 방법이었습니다.
오늘날 C ++에서 컴파일 시간을 완전히 없애고 무수한 의존성을 유발하는 끔찍한 해킹입니다 (헤더 파일의 클래스 정의가 구현에 대해 너무 많은 정보를 노출하기 때문에).
라이브러리 형식을 디자인 한 사람들은 C 전 처리기 매크로 및 함수 선언과 같이 거의 사용되지 않는 정보를위한 공간을 "폐기"하고 싶지 않기 때문입니다.
컴파일러에게 "이 기능은 나중에 링커가 작업을 수행 할 때 사용할 수 있습니다"라고 알리기 위해 해당 정보가 필요하기 때문에이 공유 정보를 저장할 수있는 두 번째 파일이 필요했습니다.
C / C ++ 이후의 대부분의 언어는이 정보를 출력 (예 : Java 바이트 코드)에 저장하거나 사전 컴파일 된 형식을 전혀 사용하지 않고 항상 소스 형식으로 배포되고 즉시 컴파일됩니다 (Python, Perl).
종종 전체 코드를 제공하지 않고도 인터페이스 정의를 원할 것입니다. 예를 들어, 공유 라이브러리가있는 경우 공유 라이브러리에 사용 된 모든 기능과 기호를 정의하는 헤더 파일이 제공됩니다. 헤더 파일이 없으면 소스를 배송해야합니다.
단일 프로젝트 내에서 헤더 파일은 IMHO라는 최소한 두 가지 목적으로 사용됩니다.
에 응답 MadKeithV의 대답은 ,
이것은 헤더를 사용하는 코드가 구현의 모든 세부 사항과 그에 필요한 다른 클래스 / 헤더를 알아야 할 필요가 없도록 종속성을 줄입니다. 이렇게하면 컴파일 시간이 줄어들고 구현의 내용이 변경 될 때 필요한 재 컴파일 양도 줄어 듭니다.
또 다른 이유는 헤더가 각 클래스에 고유 한 ID를 제공하기 때문입니다.
우리가 같은 것을 가지고 있다면
class A {..};
class B : public A {...};
class C {
include A.cpp;
include B.cpp;
.....
};
A는 B의 일부이므로 헤더를 사용하면 이러한 종류의 두통을 피할 수 있기 때문에 프로젝트를 빌드하려고 할 때 오류가 발생합니다.