Programing

C ++에서 헤더 파일을 제거해야합니까?

lottogame 2020. 11. 3. 07:33
반응형

C ++에서 헤더 파일을 제거해야합니까?


Java, C #과 같은 많은 언어는 구현에서 선언을 분리하지 않습니다. C #에는 부분 클래스의 개념이 있지만 구현 및 선언은 여전히 ​​동일한 파일에 남아 있습니다.

C ++에 동일한 모델이없는 이유는 무엇입니까? 헤더 파일이 더 실용적입니까?

현재 및 향후 버전의 C ++ 표준을 언급하고 있습니다.


나는 일상적으로 C #과 C ++를 번갈아 가며 C #에 헤더 파일이 없다는 점이 가장 큰 반려 동물 중 하나입니다. 헤더 파일을보고 클래스를 구현하는 코드 페이지를 거치지 않고도 클래스에 대해 알아야 할 모든 것을 배울 수 있습니다. 멤버 함수가 호출되는 것, 호출 구문 등이 있습니다.

그리고 예, 부분 클래스와 # 지역에 대해 알고 있지만 동일하지는 않습니다. 부분 클래스는 클래스 정의가 여러 파일에 분산되어 있기 때문에 실제로 문제를 더 악화시킵니다. #regions가 진행되는 한, 내가 현재하고있는 작업에 대해 내가 원하는 방식으로 확장되지 않는 것 같으므로 올바른보기를 얻을 때까지 이러한 작은 플러스를 확장하는 데 시간을 소비해야합니다.

아마도 Visual Studio의 intellisense가 C ++에서 더 잘 작동했다면 .h 파일을 자주 참조해야하는 설득력있는 이유가 없을 것입니다.하지만 VS2008에서도 C ++의 intellisense는 C #을 건드릴 수 없습니다.


이전 버전과의 호환성 -헤더 파일은 이전 버전과의 호환성을 깨뜨리기 때문에 제거되지 않습니다.


헤더 파일은 독립적 인 컴파일을 허용합니다. 파일을 컴파일하기 위해 구현 파일에 액세스하거나 구현할 필요가 없습니다. 이렇게하면 더 쉽게 배포 할 수 있습니다.

또한 SDK를 좀 더 쉽게 수행 할 수 있습니다. 헤더와 일부 라이브러리 만 제공 할 수 있습니다. 물론 다른 언어에서 사용하는 방법이 있습니다.


Bjarne Stroustrup 조차도 헤더 파일을 kludge라고 불렀습니다.

그러나 필요한 메타 데이터 (예 : Java 클래스 파일 또는 .Net PE 파일)를 포함하는 표준 바이너리 형식이 없으면 기능을 구현할 방법이 없습니다. 제거 된 ELF 또는 a.out 바이너리에는 추출해야 할 정보가 많지 않습니다. 그리고 정보가 Windows XCOFF 파일에 저장되지 않는다고 생각합니다.


에서 C의 설계 및 진화 ++ , 스트로브 스트 룹은 또 하나의 원인을 제공합니다 ...

동일한 헤더 파일에는 소스 제어 시스템 없이도 한 명 이상의 프로그래머가 동시에 작업 할 수있는 두 개 이상의 구현 파일이있을 수 있습니다.

요즘은 이상하게 보일지 모르지만 C ++가 발명되었을 때 중요한 문제라고 생각합니다.


C는 컴파일러를 쉽게 작성할 수 있도록 만들어졌습니다. 그것은 하나의 원칙에 기초하여 많은 일을합니다. 포인터는 헤더 파일처럼 컴파일러를 더 쉽게 작성하기 위해서만 존재합니다. C ++로 전달되는 많은 것들은 컴파일러 작성을 더 쉽게하기 위해 구현 된 이러한 기능과의 호환성을 기반으로합니다.

실제로 좋은 생각입니다. C가 만들어 졌을 때 C와 Unix는 일종의 쌍이었습니다. C는 유닉스를 포팅하고 유닉스는 C를 운영했습니다. 이런 식으로 C와 유닉스는 플랫폼간에 빠르게 확산 될 수있는 반면 어셈블리 기반 OS는 포팅을 위해 완전히 다시 작성해야했습니다.

하나의 파일에 인터페이스를 지정하고 다른 파일에 구현하는 개념은 전혀 나쁜 생각이 아니지만 C 헤더 파일이 아닙니다. 이는 컴파일러가 소스 코드를 통해 수행해야하는 패스 수를 제한하고 통신 할 수 있도록 파일 간의 계약을 제한적으로 추상화하는 방법입니다.

이러한 항목, 포인터, 헤더 파일 등은 다른 시스템에 비해 실제로 이점을 제공하지 않습니다. 컴파일러에 더 많은 노력을 기울이면 똑같은 개체 코드에 대한 포인터처럼 쉽게 참조 개체를 컴파일 할 수 있습니다. 이것이 C ++이 지금하는 일입니다.

C는 훌륭하고 단순한 언어입니다. 기능 세트가 매우 제한적이며 많은 노력없이 컴파일러를 작성할 수 있습니다. 포팅은 일반적으로 간단합니다! 나는 그것이 나쁜 언어라고 말하려는 것이 아니라 C가 만들어 졌을 때의 주요 목표가 지금은 다소 불필요하지만 호환성을 위해 유지 될 언어로 남은 것을 남길 수 있다는 것입니다.


어떤 사람들은 C가 Unix를 포팅하기 위해 쓰여졌다 고 정말로 믿지 않는 것 같습니다. 그래서 여기 : ( from )

UNIX의 첫 번째 버전은 어셈블러 언어로 작성되었지만 Thompson의 의도는 고급 언어로 작성하는 것이 었습니다.

Thompson은 1971 년에 PDP-7에서 Fortran을 사용하려고 처음 시도했지만 첫날 이후 포기했습니다. 그런 다음 그는 B라고 부르는 매우 간단한 언어를 작성하여 PDP-7을 사용했습니다. 효과가 있었지만 문제가있었습니다. 첫째, 구현이 해석 되었기 때문에 항상 느려질 것입니다. 둘째, 단어 지향 BCPL을 기반으로 한 B의 기본 개념은 새로운 PDP-11과 같은 바이트 지향 시스템에 적합하지 않았습니다.

Ritchie는 PDP-11을 사용하여 B에 유형을 추가했습니다. 잠시 동안 "New B"의 경우 NB라고 불린 다음이를위한 컴파일러를 작성하기 시작했습니다. Ritchie는 "C의 첫 번째 단계는 실제로 B에서 일부 언어 변경, 실제로 구문을 너무 많이 변경하지 않고 유형 구조를 추가하고 컴파일러를 수행하는 짧은 연속적인이 두 단계였습니다."라고 말했습니다.

그는 "두 번째 단계는 더 느렸다"고 C에서 UNIX를 다시 작성하는 것에 대해 말했다. Thompson은 1972 년 여름에 시작되었지만 두 가지 문제가있었습니다. 기본 코 루틴을 실행하는 방법, 즉 제어를 한 프로세스에서 다른; C의 원래 버전에는 구조가 없었기 때문에 적절한 데이터 구조를 얻는 데 어려움이 있습니다.

Ritchie는 "이러한 조합으로 인해 Ken은 여름 동안 포기했습니다."라고 말했습니다. "1 년 동안 나는 구조를 추가했고 아마도 컴파일러 코드를 좀 더 나은 (더 나은 코드) 만들었을 것입니다. 그래서 내년 여름에 우리가 공동으로 노력하고 실제로 C로 전체 운영 체제를 다시 실행했을 때였습니다."


여기 제가 의미하는 바에 대한 완벽한 예가 있습니다. 댓글에서 :

포인터는 컴파일러를 더 쉽게 작성하기 위해서만 존재합니까? 아니요. 포인터는 간접 개념에 대한 가장 단순한 추상화이기 때문에 존재합니다. – Adam Rosenfield (1 시간 전)

당신이 옳습니다. 간접을 구현하기 위해 포인터는 구현할 수있는 가장 간단한 추상화입니다. 결코 이해하거나 사용할 수있는 가장 간단한 방법은 아닙니다. 배열이 훨씬 쉽습니다.

문제? 포인터만큼 효율적으로 배열을 구현하려면 컴파일러에 엄청난 양의 코드를 추가해야합니다.

포인터없이 C를 설계 할 수 없었을 이유는 없지만 다음과 같은 코드를 사용하면됩니다.

int i=0;
while(src[++i])
    dest[i]=src[i];

명시적인 i + src 및 i + dest 추가를 제외하고 다음과 같은 코드를 생성하려면 컴파일러 부분에서 많은 노력이 필요합니다.

while(*(dest++) = *(src++))
    ;

사실 후에 그 변수 "i"를 분해하는 것은 어렵습니다. 새로운 컴파일러는 그것을 할 수 있지만, 그 당시에는 가능하지 않았고, 그 엉터리 하드웨어에서 실행되는 OS는 이와 같은 약간의 최적화가 필요했습니다.

이제 이러한 종류의 최적화가 필요한 시스템은 거의 없습니다 (가장 느린 플랫폼 중 하나 인 케이블 셋톱 박스에서 작업하고 대부분의 항목은 Java로되어 있습니다). 드물지만 필요할 수도있는 새로운 C 컴파일러 스스로 그런 종류의 전환을 할 수있을만큼 똑똑해야합니다.


헤더 파일이없는 C ++를 원한다면 좋은 소식이 있습니다.

이미 존재하며 D ( http://www.digitalmars.com/d/index.html ) 라고합니다.

기술적으로 D는 C ++보다 훨씬 좋은 것 같지만 현재 많은 애플리케이션에서 사용하기에 충분히 주류가 아닙니다.


C ++의 목표 중 하나는 C의 상위 집합이되는 것이며 헤더 파일을 지원할 수 없다면 그렇게하기가 어렵습니다. 그리고 확장으로 헤더 파일을 제거하려면 CPP (플러스가 아닌 전 처리기)를 모두 제거하는 것이 좋습니다. C #과 Java는 모두 해당 표준으로 매크로 전처리기를 지정하지 않습니다 (그러나 일부 경우에는 이러한 언어에서도 사용할 수 있고 사용되기도합니다).

C ++가 지금 설계되었으므로 외부 함수 및 클래스를 참조하는 컴파일 된 코드를 정적으로 확인하려면 C에서와 마찬가지로 프로토 타입이 필요합니다. 헤더 파일이 없으면 사용하기 전에 이러한 클래스 정의와 함수 선언을 입력해야합니다. C ++에서 헤더 파일을 사용하지 않으려면 Java의 import키워드 와 같은 것을 지원하는 언어로 기능을 추가해야 합니다. 그것은 주요 추가 사항이며 변경 될 것입니다. 그것이 실용적 일지에 대한 귀하의 질문에 대답하기 위해 : 나는 그렇게 생각하지 않습니다.


많은 사람들이 헤더 파일의 단점을 알고 있으며 C ++에 더 강력한 모듈 시스템을 도입 할 아이디어가 있습니다. Daveed Vandevoorde의 C ++ 모듈 (Revision 5)살펴볼 수 있습니다 .


음, C ++ 자체는 이전 버전과의 호환성 때문에 헤더 파일을 제거해서는 안됩니다. 그러나 나는 그들이 일반적으로 어리석은 생각이라고 생각합니다. 비공개 소스 라이브러리를 배포하려면이 정보를 자동으로 추출 할 수 있습니다. 구현을 보지 않고 클래스를 사용하는 방법을 이해하고 싶다면 문서 생성기가 필요한 것이며 훨씬 더 나은 작업을 수행합니다.


구현 파일에 대한 별도의 구성 요소에서 클래스 인터페이스를 정의하는 데 가치가 있습니다.

인터페이스로 할 수 있지만 그 길을 따라 가면 구현과 계약을 분리하는 측면에서 클래스가 부족하다는 것을 암시 적으로 말하는 것입니다.

Modula 2는 올바른 아이디어, 정의 모듈 및 구현 모듈을 가지고있었습니다. http://www.modula2.org/reference/modules.php

Java / C #의 대답은 동일한 것을 암시 적으로 구현하는 것입니다 (객체 지향적 임에도 불구하고).

헤더 파일은 구현 세부 사항 (예 : 개인 변수)을 표현하기 때문에 kludge입니다.

Java 및 C #으로 넘어 가면서 언어에 개발을위한 IDE 지원이 필요한 경우 (예 : 클래스 브라우저에서 공용 클래스 인터페이스를 탐색 할 수 있음) 이는 코드가 다음과 같은 장점을 가지고 있지 않다는 진술 일 수 있습니다. 특히 읽기 쉽습니다.

구현 세부 사항과 인터페이스의 혼합은 매우 끔찍합니다.

결정적으로, 구현과 관계없이 간결하고 잘 주석 처리 된 파일에 공개 클래스 서명을 문서화하는 능력이 부족하다는 것은 저에게 언어 디자인이 유지 관리의 편의보다는 저자의 편의를 위해 작성되었음을 나타냅니다. 글쎄, 나는 지금 자바와 C #에 대해 떠들고있다.


이러한 분리의 장점 중 하나는, 단지 인터페이스를보기 쉽다는 점이다 없이 필요로하는 고급 편집기를 .


이것이 결코 일어나지 않을 이유를 원한다면 기존의 거의 모든 C ++ 소프트웨어를 망가뜨릴 것입니다. C ++위원회 디자인 문서 중 일부를 보면 코드가 얼마나 많이 손상되는지 확인하기 위해 다양한 대안을 살펴 보았습니다.

switch 문을 중간 정도의 지능적인 것으로 변경하는 것이 훨씬 쉬울 것입니다. 그것은 약간의 코드 만 깨뜨릴 것입니다. 여전히 일어나지 않을 것입니다.

새로운 아이디어를 위해 편집 :

C ++ 헤더 파일을 필요로하는 C ++와 Java의 차이점은 C ++ 객체가 반드시 포인터가 아니라는 것입니다. Java에서 모든 클래스 인스턴스는 포인터로 참조되지만 그렇게 보이지는 않습니다. C ++에는 힙과 스택에 할당 된 개체가 있습니다. 즉, C ++에는 개체의 크기와 데이터 멤버가 메모리에있는 위치를 알 수있는 방법이 필요합니다.


헤더 파일 없이는 언어가 없습니다. 그것은 신화입니다.

Java 용 독점 라이브러리 배포판을 살펴보십시오 (C # 경험이 없지만 동일 할 것으로 예상합니다). 완전한 소스 파일을 제공하지는 않습니다. 그들은 단지 났을 당신에게 모든 메소드의 구현을 가진 파일을 제공 ( {}또는 {return null;}등) 모든 것이 그들이 숨겨진 숨기는 게 멀리 얻을 수 있습니다. 헤더 외에는 아무것도 부를 수 없습니다.

그러나 C 또는 C ++ 컴파일러가 extern파일이 직접 컴파일되지 않는 한 적절하게 표시된 파일의 모든 것을 계산할 수있는 이유는 기술적 인 이유가 없습니다 . 그러나 C도 C ++도 파싱 속도가 빠르지 않기 때문에 컴파일 비용이 엄청나게되며 이는 매우 중요한 고려 사항입니다. 헤더와 소스를 결합하는 더 복잡한 방법은 컴파일러가 객체의 레이아웃을 알아야하는 것과 같은 기술적 문제에 빠르게 직면하게됩니다.


헤더 파일은 언어의 필수 부분입니다. 헤더 파일이 없으면 모든 정적 라이브러리, 동적 라이브러리, 거의 모든 사전 컴파일 된 라이브러리가 쓸모 없게됩니다. 또한 헤더 파일을 사용하면 모든 것을 문서화하기가 더 쉬워지며 모든 코드를 검토하지 않고도 라이브러리 / 파일의 API를 살펴볼 수 있습니다.

They also make it easier to organize your program. Yes, you have to be constantly switching from source to header, but they also allow you define internal and private APIs inside the implementations. For example:

MySource.h:

extern int my_library_entry_point(int api_to_use, ...);

MySource.c:

int private_function_that_CANNOT_be_public();

int my_library_entry_point(int api_to_use, ...){
  // [...] Do stuff
}

int private_function_that_CANNOT_be_public() {

}

If you #include <MySource.h>, then you get my_library_entry_point.

If you #include <MySource.c>, then you also get private_function_that_CANNOT_be_public.

You see how that could be a very bad thing if you had a function to get a list of passwords, or a function which implemented your encryption algorithm, or a function that would expose the internals of an OS, or a function that overrode privileges, etc.


Oh Yes!

After coding in Java and C# it's really annoying to have 2 files for every classes. So I was thinking how can I merge them without breaking existing code.

In fact, it's really easy. Just put the definition (implementation) inside an #ifdef section and add a define on the compiler command line to compile that file. That's it.

Here is an example:

/* File ClassA.cpp */

#ifndef _ClassA_
#define _ClassA_

#include "ClassB.cpp"
#include "InterfaceC.cpp"

class ClassA : public InterfaceC
{
public:
    ClassA(void);
    virtual ~ClassA(void);

    virtual void methodC();

private:
    ClassB b;
};

#endif

#ifdef compiling_ClassA

ClassA::ClassA(void)
{
}

ClassA::~ClassA(void)
{
}

void ClassA::methodC()
{
}

#endif

On the command line, compile that file with

-D compiling_ClassA

The other files that need to include ClassA can just do

#include "ClassA.cpp"

Of course the addition of the define on the command line can easily be added with a macro expansion (Visual Studio compiler) or with an automatic variables (gnu make) and using the same nomenclature for the define name.


Still I don't get the point of some statements. Separation of API and implementation is a very good thing, but header files are not API. There are private fields there. If you add or remove private field you change implementation and not API.

참고URL : https://stackoverflow.com/questions/752793/should-c-eliminate-header-files

반응형