문제

C++에서는 함수, 변수, 상수의 선언과 정의를 다음과 같이 구분할 수 있습니다.

function someFunc();

function someFunc()
{
  //Implementation.
}

실제로 클래스 정의에서는 이런 경우가 종종 있습니다.클래스는 일반적으로 .h 파일의 멤버로 선언되며, 그런 다음 해당 .C 파일에 정의됩니다.

이 접근 방식의 장점과 단점은 무엇입니까?

도움이 되었습니까?

해결책

역사적으로 이것은 컴파일러를 돕는 것이 었습니다. 실제 사용법이든 전진 선언 (C의 기본 기능 프로토 타입을 제외한)이든 이름 목록을 사용하기 전에 이름 목록을 제공해야했습니다.

현대 언어를위한 현대적인 컴파일러는 이것이 더 이상 필수가 아니라는 것을 보여줍니다. 따라서 C & C ++ (대상 C 및 아마도 다른) 구문은 역사적 수하물입니다. 실제로 이것은 적절한 모듈 시스템의 추가조차도 해결되지 않는 C ++의 큰 문제 중 하나입니다.

단점은 다음과 같습니다. 많은 중첩이 많이 포함되어 있습니다. 파일 (나는 이전에 나무를 포함하고 놀랍게도 거대합니다)과 선언과 정의 사이의 중복성이 포함되어 있습니다. 모두 더 긴 코딩 시간과 더 긴 컴파일 시간 (비슷한 C ++와 Compile Time을 비교합니다. C# 프로젝트? 이것이 차이의 이유 중 하나입니다). 제공하는 구성 요소 사용자의 경우 헤더 파일이 제공되어야합니다. ODR 위반 가능성. 사전 프로세서 (많은 현대 언어에는 사전 프로세서 단계가 필요하지 않음)에 대한 의존으로 인해 도구를 구문 분석하기가 더욱 깨지기 쉽고 어렵습니다.

장점 :별로. 문서화 목적으로 한 곳으로 함께 그룹화 된 기능 이름 목록을 얻을 수 있지만 대부분의 IDE에는 요즘 어떤 종류의 코드 폴딩 기능이 있으며, 모든 규모의 프로젝트는 어쨌든 문서 생성기 (예 : doxygen)를 사용해야합니다. 더 깨끗하고 사전 프로세서가없는 모듈 기반 구문을 사용하면 도구가 코드를 따르고 더 많은 것을 제공하기가 더 쉽기 때문에이 "이점"은 무의미하다고 생각합니다.

다른 팁

이는 C/C++ 컴파일러의 작동 방식에 대한 인공물입니다.

소스 파일이 컴파일되면 전처리기는 각 #include 문을 포함된 파일의 내용으로 대체합니다.그 후에야 컴파일러는 이 연결의 결과를 해석하려고 시도합니다.

그런 다음 컴파일러는 해당 결과를 처음부터 끝까지 검토하여 각 문의 유효성을 검사합니다.코드 줄이 이전에 정의되지 않은 함수를 호출하면 포기됩니다.

그러나 상호 재귀 함수 호출의 경우에는 문제가 있습니다.

void foo()
{
  bar();
}

void bar()
{
  foo();
}

여기, foo 다음과 같이 컴파일되지 않습니다 bar 알 수 없습니다.두 가지 기능을 전환하면 bar 다음과 같이 컴파일되지 않습니다 foo 알 수 없습니다.

그러나 선언과 정의를 분리하는 경우 원하는 대로 함수의 순서를 지정할 수 있습니다.

void foo();
void bar();

void foo()
{
  bar();
}

void bar()
{
  foo();
}

여기서 컴파일러가 처리할 때 foo 이미 다음과 같은 함수의 서명을 알고 있습니다. bar, 그리고 행복합니다.

물론 컴파일러는 다른 방식으로 작동할 수 있지만 이것이 C, C++ 및 어느 정도 Objective-C에서 작동하는 방식입니다.

단점:

직접적인 것은 없습니다.어쨌든 C/C++를 사용하고 있다면 이것이 작업을 수행하는 가장 좋은 방법입니다.언어/컴파일러를 선택할 수 있다면 이것이 문제가 되지 않는 언어/컴파일러를 선택할 수도 있습니다.선언을 헤더 파일로 분할할 때 고려해야 할 유일한 사항은 상호 재귀적인 #include 문을 피하는 것입니다. 그러나 이것이 포함 가드의 목적입니다.

장점:

  • 컴파일 속도:포함된 모든 파일이 연결된 후 구문 분석되므로 포함된 파일의 코드 양과 복잡성이 줄어듭니다. ~ 할 것이다 컴파일 시간을 향상시킵니다.
  • 코드 중복/인라인을 피하세요.헤더 파일에서 함수를 완전히 정의하는 경우 이 헤더를 포함하고 이 함수를 참조하는 각 개체 파일에는 해당 함수의 자체 버전이 포함됩니다.참고로, 만약 당신이 원하다 인라인을 사용하려면 전체 정의를 헤더 파일(대부분의 컴파일러에서)에 넣어야 합니다.
  • 캡슐화/명확성:잘 정의된 클래스/함수 세트와 일부 문서만 있으면 다른 개발자가 코드를 사용할 수 있을 만큼 충분합니다.(이상적으로는) 코드가 어떻게 작동하는지 이해할 필요가 없습니다. 그런데 왜 코드를 살펴보라고 요구합니까?(구현에 액세스하는 것이 유용할 수 있다는 반론 필요할 때 물론 여전히 그렇습니다).

물론 함수를 전혀 노출하는 데 관심이 없다면 일반적으로 헤더가 아닌 구현 파일에서 함수를 완전히 정의하도록 선택할 수 있습니다.

선언과 정의를 C ++ 헤더 및 소스 파일로 분리하는 데 두 가지 주요 장점이 있습니다. 첫 번째는 문제를 피하는 것입니다 하나의 정의 규칙 당신의 수업/기능/무엇이있을 때 #includeD 이상의 장소에서. 둘째, 이런 식으로 작업을 수행함으로써 인터페이스와 구현을 분리합니다. 클래스 또는 라이브러리 사용자는 사용하는 코드를 작성하기 위해 헤더 파일 만 볼 필요가 있습니다. 당신은 또한 이것을 PIMPL 관용구 그리고 라이브러리 구현이 변경 될 때마다 사용자 코드가 다시 컴파일 할 필요가 없도록하십시오.

이미 .h와 .cpp 파일 사이의 코드 반복의 단점을 이미 언급했습니다. 어쩌면 C ++ 코드를 너무 오랫동안 작성했을 수도 있지만 생각하지 않습니다. 저것 나쁜. 어쨌든 함수 서명을 변경할 때마다 모든 사용자 코드를 변경해야합니다. 따라서 파일이 더란 무엇입니까? 수업을 처음 작성할 때만 성가신 일이며 헤더에서 새 소스 파일로 복사하고 붙여 넣어야합니다.

실제로 다른 단점은 타사 라이브러리를 사용하는 좋은 코드를 작성하고 디버그하기 위해서는 일반적으로 그 안에있는 것을 봐야한다는 것입니다. 이는 소스 코드를 변경할 수없는 경우에도 소스 코드에 대한 액세스를 의미합니다. 당신이 가진 모든 것이 헤더 파일과 컴파일 된 객체 파일 만 있으면 버그가 당신의 결함인지 또는 그들의 결함인지 결정하기가 매우 어려울 수 있습니다. 또한 소스를 살펴보면 문서가 다루지 않을 수있는 라이브러리를 올바르게 사용하고 확장하는 방법에 대한 통찰력을 제공합니다. 모든 사람이 라이브러리와 함께 MSDN을 배송하는 것은 아닙니다. 그리고 훌륭한 소프트웨어 엔지니어들은 당신이 꿈꾸지 않은 코드로 일을하는 불쾌한 습관을 가지고 있습니다. ;-)

표준은 함수를 사용할 때 선언이 범위에 있어야합니다. 즉, 컴파일러는 프로토 타입 (헤더 파일의 선언)에 대해 전달하는 내용을 확인할 수 있어야합니다. 물론, 변동성 인 함수의 경우, 그러한 함수는 인수를 검증하지 않습니다.

이것이 필요하지 않은 경우 C를 생각해보십시오. 당시 컴파일러는 int로 기본적으로 반환 유형 사양을 처리하지 않았습니다. 이제 void에 대한 포인터를 반환 한 함수 foo ()가 있다고 가정합니다. 그러나 선언이 없었기 때문에 컴파일러는 정수를 반환해야한다고 생각합니다. 예를 들어 일부 Motorola 시스템에서는 정수와 포인터가 다른 레지스터로 반환됩니다. 이제 컴파일러는 더 이상 올바른 레지스터를 사용하지 않고 대신 포인터 캐스트를 다른 레지스터의 정수로 반환합니다. 이 포인터로 작업하려고하는 순간 - 모든 지옥이 느슨해집니다.

헤더 내에서 기능을 선언하는 것은 괜찮습니다. 그러나 헤더에서 선언하고 정의하면 인라인인지 확인하십시오. 이를 달성하는 한 가지 방법은 정의를 클래스 정의 내부에 넣는 것입니다. 그렇지 않으면 포장하십시오 inline 예어. 헤더가 여러 구현 파일에 포함되어있을 때 그렇지 않으면 ODR 위반이 발생합니다.

기본적으로 클래스/기능/무엇이든지 2 개의 조회수가 있습니다.

이름, 매개 변수 및 멤버 (구조물/클래스의 경우)를 선언하는 선언 및 함수의 기능을 정의하는 정의.

단점 중에는 반복이 있지만 한 가지 큰 장점은 기능을 다음과 같이 선언 할 수 있다는 것입니다. int foo(float f) 구현 (= 정의)에 세부 사항을 남겨 두므로 FOO를 사용하려는 사람은 누구나 헤더 파일과 라이브러리/ObjectFile에 대한 링크 만 포함하므로 라이브러리 사용자와 컴파일러는 정의 된 인터페이스를 관리해야합니다. 인터페이스를 이해하고 컴파일 타임을 속도를 높이는 데 도움이됩니다.

내가 아직 보지 못한 한 가지 장점 : API

오픈 소스가 아닌 모든 라이브러리 또는 타사 코드 (IE 독점)는 배포와 함께 구현되지 않습니다. 대부분의 회사는 소스 코드를 제공하는 데 익숙하지 않습니다. 쉬운 솔루션은 DLL을 사용할 수있는 클래스 선언 및 기능 서명을 배포합니다.

면책 조항 : 나는 그것이 옳거나 잘못되었는지 또는 정당한 지 말하지 않습니다. 나는 단지 그것을 많이 보았습니다.

이점

클래스는 선언을 포함하여 다른 파일에서 참조 할 수 있습니다. 그런 다음 정의는 나중에 컴파일 프로세스에서 연결할 수 있습니다.

전방 선언의 가장 큰 장점 중 하나는 신중하게 사용하면 모듈 간의 컴파일 시간 종속성을 줄일 수 있다는 것입니다.

classa.h가 classb.h에서 데이터 요소를 참조 해야하는 경우 종종 classa.h에서 전방 참조를 사용하고 classa.h가 아닌 classa.cc에 classb.h를 포함시킬 수 있으므로 컴파일 타임을 줄입니다. 의존.

큰 시스템의 경우 이것은 빌드에서 큰 시간 절약이 될 수 있습니다.

  1. 분리는 프로그램 요소에 대한 깨끗하고 깔끔한 견해를 제공합니다.
  2. 소스를 공개하지 않고 바이너리 모듈/라이브러리를 생성하고 연결할 수 있습니다.
  3. 소스를 다시 컴파일하지 않고 바이너리를 연결합니다.

올바르게 수행하면이 분리는 구현 만 변경된 컴파일 시간을 줄입니다.

불리

이것은 많은 반복으로 이어집니다. 대부분의 기능 서명은 두 개 이상의 (Paulious) 장소에 넣어야합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top