C로 할 수 없을 때 왜 C ++의 헤더 파일 안에 메소드 정의를 가질 수 있습니까?


23

C에서는 헤더 파일 내에 함수 정의 / 구현을 가질 수 없습니다. 그러나 C ++에서는 헤더 파일 내에 전체 메서드를 구현할 수 있습니다. 왜 행동이 다른가요?

답변:


28

C에서 헤더 파일에 함수를 정의하면 해당 헤더 파일을 포함하는 컴파일 된 각 모듈에 해당 함수가 나타나고 함수에 대한 공용 기호가 내보내집니다. 따라서 additup 함수가 header.h에 정의되어 있고 foo.c 및 bar.c에 모두 header.h가 있으면 foo.o 및 bar.o에 additup의 사본이 모두 포함됩니다.

이 두 객체 파일을 함께 연결하면 링커에서 기호 additup이 두 번 이상 정의되어 허용되지 않음을 알 수 있습니다.

함수를 정적으로 선언하면 심볼이 내보내지지 않습니다. 객체 파일 foo.o와 bar.o에는 여전히 함수에 대한 별도의 코드 사본이 포함되어 있으며 사용할 수는 있지만 링커는 함수의 사본을 볼 수 없으므로 함수 사본을 볼 수 없습니다. 불평하지 않습니다. 물론 다른 모듈도이 기능을 볼 수 없습니다. 그리고 프로그램은 동일한 기능의 동일한 사본 두 개로 구성됩니다.

헤더 파일에서만 함수를 선언하고 정의하지 않은 다음 하나의 모듈에서만 정의하면 링커는 함수의 사본 하나를보고 프로그램의 모든 모듈에서이를보고 사용해. 그리고 컴파일 된 프로그램에는 함수의 사본 하나만 포함됩니다.

따라서 C의 헤더 파일에 함수 정의를 가질 있습니다. 스타일이 잘못되었거나 형식이 좋지 않으며 모든 것이 좋지 않습니다.

( "declare"는 본문이없는 함수 프로토 타입을 제공한다는 의미이고, "define"은 함수 본문의 실제 코드를 제공하는 것을 의미합니다. 이것은 표준 C 용어입니다.)


2
그렇게 나쁜 생각 은 아닙니다. 이런 종류의 것들은 GNU Libc 헤더에서도 찾을 수 있습니다.
SK-logic

그러나 조건부 컴파일 지시문에서 헤더 파일의 관용적 래핑은 어떻습니까? 그런 다음 헤더에 AND로 선언 된 함수를 사용하더라도 한 번만로드됩니다. 저는 C를 처음 사용하므로 오해 할 수 있습니다.
user305964

2
@papiro 문제는 랩핑이 컴파일러를 한 번만 실행할 때만 보호한다는 것입니다. 따라서 foo.c가 한 실행에서 foo.o로 컴파일되고 다른 실행에서 bar.c에서 bar.o로, foo.o 및 bar.o가 세 번째로 a.out에 링크되면 각 객체 파일에 하나씩 여러 인스턴스를 방지하지 않습니다.
David Conrad

여기에 설명 된 문제 #ifndef HEADER_H가 무엇 을 방지해야합니까?
Robert Harvey

27

C와 C ++는 이와 관련하여 거의 동일하게 동작합니다 inline. 헤더에 함수를 가질 수 있습니다 . C ++에서 본문이 클래스 정의 안에있는 모든 메소드는 암시 적으로 inline있습니다. C에서 동일한 작업을 수행하려면 함수를 선언하십시오 static inline.


" 함수 선언static inline "... 함수를 사용하는 각 번역 단위에 여러 개의 함수 사본이 계속 남아 있습니다. static inline함수 가 아닌 C ++에서는 사본이 하나만 있습니다. 실제로 C의 헤더에 구현을하려면 1) 구현을 inline(예 :)로 표시 inline void func(){do_something();}하고 2) 실제로이 기능은 특정 번역 단위 (예 :)에 있다고 말해야합니다 void func();.
Ruslan

6

헤더 파일의 개념에는 약간의 설명이 필요합니다.

컴파일러의 명령 행에 파일을 제공하거나 '#include'를 수행하십시오. 대부분의 컴파일러는 확장자 c, C, cpp, c ++ 등을 가진 명령 파일을 소스 파일로 받아들입니다. 그러나 일반적으로 소스 파일에 대한 임의의 확장명을 사용할 수있는 명령 줄 옵션이 포함되어 있습니다.

일반적으로 명령 행에 제공된 파일은 '소스'이며 포함 된 파일은 '헤더'입니다.

전 처리기 단계는 실제로 모든 것을 취해 모든 것이 하나의 큰 파일처럼 보이도록 만듭니다. 헤더 나 소스에있는 것은 실제로이 시점에서 관련이 없습니다. 일반적으로이 단계의 출력을 표시 할 수있는 컴파일러 옵션이 있습니다.

따라서 컴파일러 명령 행에 제공된 각 파일에 대해 큰 파일이 컴파일러에 제공됩니다. 여기에는 메모리를 차지하거나 다른 파일에서 참조 할 심볼을 만드는 코드 / 데이터가있을 수 있습니다. 이제 이들 각각은 '객체'이미지를 생성합니다. 링커는 서로 연결된 두 개 이상의 객체 파일에서 동일한 심볼이 발견되면 '중복 심볼'을 제공 할 수 있습니다. 아마도 이것이 이유입니다. 객체 파일에 심볼을 생성 할 수있는 헤더 파일에 코드를 넣지 않는 것이 좋습니다.

'인라인'은 일반적으로 인라인됩니다. 그러나 디버깅 할 때는 인라인되지 않을 수 있습니다. 그렇다면 링커는 왜 다중 정의 오류를 제공하지 않습니까? 단순 ... 이들은 '약한'기호이며, 모든 개체의 약한 기호에 대한 모든 데이터 / 코드가 동일한 크기와 내용이라면 링크 된 것이 다른 개체의 복사본 하나와 복사본을 유지합니다. 작동합니다.


3

C99에서이 작업을 수행 할 수 있습니다. inline함수가 다른 곳에 제공되도록 보장되므로 함수가 인라인되지 않은 경우 해당 정의는 선언으로 변환됩니다 (즉, 구현이 삭제됨). 물론 사용할 수 있습니다 static.


1

C ++ 표준 인용문

C ++ 17 N4659 표준 초안 10.1.6 "인라인 지정자는"그 방법은 암시 적으로 인라인 말합니다 :

4 클래스 정의 내에 정의 된 함수는 인라인 함수입니다.

더 나아가서 인라인 메소드 는 모든 번역 단위에서 정의 될 수있을 뿐만 아니라 정의 되어야한다는 것을 알 수 있습니다.

6 인라인 함수 또는 변수는 그것이 사용되는 모든 번역 단위에 정의되어야하며 모든 경우에 정확히 동일한 정의를 가져야한다 (6.2).

이것은 12.2.1 "멤버 함수"의 노트에 명시 적으로 언급되어 있습니다.

1 멤버 함수는 클래스 정의에서 정의 될 수 있으며 (11.4),이 경우 인라인 멤버 함수입니다 (10.1.6) [...]

3 [참고 : 프로그램에서 인라인이 아닌 멤버 함수에 대한 정의는 최대 하나만있을 수 있습니다. 프로그램에 둘 이상의 인라인 멤버 함수 정의가있을 수 있습니다. 6.2 및 10.1.6을 참조하십시오. — 끝 참고]

GCC 8.3 구현

main.cpp

struct MyClass {
    void myMethod() {}
};

int main() {
    MyClass().myMethod();
}

컴파일 및 심볼보기 :

g++ -c main.cpp
nm -C main.o

산출:

                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 W MyClass::myMethod()
                 U __stack_chk_fail
0000000000000000 T main

그러면 ELF 객체 파일 man nm에서 MyClass::myMethod심볼이 약한 것으로 표시되어 여러 객체 파일에 나타날 수 있음을 알 수 있습니다.

"W" "w"기호는 약한 개체 기호로 특별히 태그가 지정되지 않은 약한 기호입니다. 약한 정의 된 기호가 일반 정의 된 기호와 연결되면 일반 정의 된 기호가 오류없이 사용됩니다. 정의되지 않은 약한 심볼이 연결되고 심볼이 정의되지 않은 경우 심볼 값은 오류없이 시스템 별 방식으로 결정됩니다. 일부 시스템에서 대문자는 기본값이 지정되었음을 나타냅니다.


-4

아마도 같은 이유로 Java의 클래스 정의 안에 전체 메소드 구현을 넣어야합니다.

구불 구불 한 괄호와 같은 키워드가 많이있어 비슷해 보이지만 언어가 다릅니다.


1
아니요, 실제로는 실제 답변이 있으며 C ++의 주요 목표 중 하나는 C와 호환되는 것입니다.
Ed S.

4
아니요, "A 높은 수준의 C 호환성"과 "C와의 비 호환성 없음"을 갖도록 설계되었습니다. (Stroustrup에서 모두). 이 특정 비 호환성이 왜 무용지물인지 강조하기 위해보다 심층적 인 답변을 제공 할 수 있음에 동의합니다. 자유롭게 공급하십시오.
Paul Butcher

나는 그랬지만 Simon Richter는 내가 그것을 게시했을 때 이미 가지고 있었다. "이전 버전과 호환 가능"과 "고도의 C 호환성"의 차이점에 대해 간략하게 설명 할 수 있지만이 답변은 여전히 ​​틀립니다. 마지막 문장은 C #과 C ++를 비교하고 있지만 C와 C ++와 비교해 보면 정확할 것입니다.
Ed S.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.