C ++에서 정방향 선언이란 무엇입니까?


215

: http://www.learncpp.com/cpp-tutorial/19-header-files/

다음이 언급됩니다 :

add.cpp :

int add(int x, int y)
{
    return x + y;
}

main.cpp :

#include <iostream>

int add(int x, int y); // forward declaration using function prototype

int main()
{
    using namespace std;
    cout << "The sum of 3 and 4 is " << add(3, 4) << endl;
    return 0;
}

컴파일러는 add컴파일 할 때 " "가 무엇인지 알 수 있도록 정방향 선언을 사용했습니다 main.cpp. 앞에서 언급했듯이 다른 파일에있는 모든 함수에 대해 선언을 작성하면 지루할 수 있습니다.

" 선언 선언 "을 더 설명 할 수 있습니까 ? main()함수 에서 사용하면 어떤 문제가 있습니까?


1
"전달 선언"은 실제로 선언 일뿐입니다. 이 답변 참조 (끝) : stackoverflow.com/questions/1410563/…
sbi

답변:


381

C ++에서 앞으로 선언이 필요한 이유

컴파일러는 철자가 틀리거나 잘못된 수의 인수를 함수에 전달하지 않도록합니다. 따라서 사용하기 전에 먼저 'add'(또는 다른 유형, 클래스 또는 함수) 선언을 볼 것을 주장합니다.

이것은 실제로 컴파일러가 코드의 유효성을 검사하는 더 나은 작업을 수행하고 느슨한 끝을 정리하여 깔끔한 모양의 객체 파일을 생성 할 수있게합니다. 선언을 전달할 필요가 없다면 컴파일러는 'add'함수가 무엇인지에 대한 모든 가능한 추측에 대한 정보를 포함해야하는 오브젝트 파일을 생성합니다. 그리고 링커가 add를 사용하여 생성하는 것과 다른 객체 파일에 'add'함수가있을 수있을 때 실제로 호출하려는 'add'를 시도하고 해결하기 위해 링커에는 매우 영리한 논리가 포함되어야합니다. dll 또는 exe. 링커에서 잘못된 추가가 발생했을 수 있습니다. int add (int a, float b)를 사용하고 싶지만 실수로 작성하는 것을 잊었지만 링커가 이미 존재하는 int add (int a, int b) 그리고 그것이 옳은 것이라고 생각하고 그것을 대신 사용했습니다. 코드가 컴파일되지만 예상 한대로 수행되지 않습니다.

따라서 명시 적으로 유지하고 추측 등을 피하기 위해 컴파일러는 사용하기 전에 모든 것을 선언해야한다고 주장합니다.

선언과 정의의 차이점

따로 선언과 정의의 차이점을 아는 것이 중요합니다. 선언은 무언가 모양을 보여주기에 충분한 코드를 제공하므로 함수의 경우 반환 유형, 호출 규칙, 메서드 이름, 인수 및 유형입니다. 그러나 메소드의 코드는 필요하지 않습니다. 정의를 위해서는 선언과 함수 코드가 필요합니다.

사전 선언으로 빌드 시간을 크게 줄일 수있는 방법

이미 함수 선언이 포함 된 헤더를 # includ'ing하여 현재 .cpp 또는 .h 파일로 함수 선언을 가져올 수 있습니다. 그러나 프로그램의 .cpp 대신 # .h에 헤더를 포함하면 # 작성하는 .h를 포함하는 모든 것이 결국 모든 헤더를 포함하므로 # 컴파일 속도가 느려질 수 있습니다 당신도 #includes를 썼습니다. 갑자기 컴파일러에는 하나 또는 두 개의 함수 만 사용하려는 경우에도 컴파일해야하는 #included 페이지 및 코드 페이지가 있습니다. 이를 피하려면, 순방향 선언을 사용하고 파일 맨 위에 함수 선언을 직접 입력하면됩니다. 함수를 몇 개만 사용하지 않으면 헤더를 포함하여 항상 # 컴파일하는 것보다 컴파일 속도가 빨라질 수 있습니다. 정말 큰 프로젝트의 경우

두 정의가 서로를 사용하는 순환 참조

또한 전진 선언을 통해주기를 중단 할 수 있습니다. 두 기능이 서로를 사용하려고하는 곳입니다. 이런 일이 발생하면 (그리고 완벽하게 유효한 일입니다) # 헤더 파일 하나를 포함시킬 수 있지만, 해당 헤더 파일은 현재 쓰고있는 헤더 파일을 #include하려고 시도합니다 .... 그런 다음 # 다른 헤더를 포함합니다 # 쓰고있는 것을 포함합니다. 각 헤더 파일이 다른 것을 #include하려고 시도하면서 닭고기와 달걀 상황에 빠졌습니다. 이를 해결하기 위해 파일 중 하나에서 필요한 부분을 전달하고 #include를 해당 파일에서 제외시킬 수 있습니다.

예 :

파일 Car.h

#include "Wheel.h"  // Include Wheel's definition so it can be used in Car.
#include <vector>

class Car
{
    std::vector<Wheel> wheels;
};

파일 Wheel.h

흠 ... Wheel에 Car에 대한 포인터가 있기 때문에 Car의 선언이 필요하지만 컴파일러 오류가 발생할 수 있으므로 Car.h는 여기에 포함될 수 없습니다. Car.h가 포함되어 있으면 Wheel.h를 포함하는 Car.h를 포함하는 Wheel.h를 포함 시키려고 시도합니다. 이것은 영원히 계속되므로 컴파일러가 오류를 발생시킵니다. 해결책은 대신 Car 선언을 전달하는 것입니다.

class Car;     // forward declaration

class Wheel
{
    Car* car;
};

Wheel 클래스에 car 메소드를 호출해야하는 메소드가있는 경우 해당 메소드를 Wheel.cpp에 정의 할 수 있으며 Wheel.cpp는 이제주기를 발생시키지 않고 Car.h를 포함 할 수 있습니다.


4
함수가 두 개 이상의 클래스에 친숙한 경우에도 앞으로 선언이 필요합니다.
Barun

1
Scott, 빌드 시간에 대한 요점 : 항상 .cpp 파일에 필요한대로 선언을 선언하고 포함하는 것이 일반적 / 모범 사례라고 생각하십니까? 당신의 대답을 읽으면 그렇게 보일 것 같지만주의 사항이 있는지 궁금합니다.
Zepee

5
@ 제피 그것은 균형이다. 빠른 빌드를 위해서는 좋은 습관이라고 말하고 시도해 보는 것이 좋습니다. 그러나 유형 이름 등이 여전히 변경되는 경우 도구가 자동으로 이름을 바꾸는 데 도움이 되더라도 약간의 노력과 추가 코드 줄이 필요할 수 있습니다. 따라서 절충안이 있습니다. 아무도 귀찮게하지 않는 코드베이스를 보았습니다. 동일한 정의를 반복해서 발견하면 언제든지 별도의 헤더 파일에 넣을 수 있습니다. stackoverflow.com/questions/4300696/what-is-the-iosfwd-header
Scott Langham

헤더 파일이 서로를 참조 할 때 정방향 선언이 필요합니다. 예 : stackoverflow.com/questions/396084/…
Nicholas Hamilton

1
이를 통해 팀의 다른 개발자가 실제로 코드베이스의 나쁜 시민이 될 수 있습니다. forward 선언에 주석이 필요하지 않은 경우 // From Car.h길을 따라 정의를 찾으려고하는 털이 많은 상황을 만들 수 있습니다.
Dagrooms 2016 년

25

컴파일러는 현재 변환 단위에서 사용중인 각 기호가 이전에 현재 단위로 선언되었거나 선언되어 있는지 찾습니다. 소스 파일의 시작 부분에 모든 메소드 서명을 제공하는 것은 스타일 문제 일 뿐이며, 나중에 정의가 제공됩니다. 클래스의 포인터를 다른 클래스의 멤버 변수로 사용하는 경우가 많이 사용됩니다.

//foo.h
class bar;    // This is useful
class foo
{
    bar* obj; // Pointer or even a reference.
};

// foo.cpp
#include "bar.h"
#include "foo.h"

따라서 가능하면 수업 시간에 앞으로 선언을 사용하십시오. 프로그램에 함수 (호 헤더 파일 포함) 만 있다면 시작 부분에 프로토 타입을 제공하는 것은 스타일의 문제 일뿐입니다. 헤더 파일이 기능 만있는 헤더가있는 일반 프로그램에 존재하는 경우에도 마찬가지입니다.


12

C ++은 위에서 아래로 구문 분석되므로 컴파일러는 사용하기 전에 알아야 할 사항이 있습니다. 따라서 참조 할 때 :

int add( int x, int y )

메인 함수에서 컴파일러는 그것이 존재한다는 것을 알아야합니다. 이것을 증명하기 위해 주 함수 아래로 옮기면 컴파일러 오류가 발생합니다.

따라서 ' 전달 선언 '은 주석에서 말하는 것입니다. 사용하기 전에 무언가를 선언하고 있습니다.

일반적으로 헤더 파일에 정방향 선언을 포함시킨 다음 iostream 이 포함 된 것과 같은 방식으로 해당 헤더 파일을 포함합니다.


12

C ++에서 " 정방향 선언 " 이라는 용어 는 대부분 클래스 선언 에만 사용됩니다 . 클래스의 "전달 선언"이 실제로 멋진 이름을 가진 단순한 클래스 선언 인 이유는 이 답변 을 참조하십시오 .

로 즉, "앞으로"그냥 용어에 안정기를 추가 어떤 선언에서 지금까지 어떤 식별자를 선언으로 앞으로 것으로 볼 수있다 전에 이 사용됩니다.

( 정의 와 반대 되는 선언무엇입니까? 정의와 선언의 차이점무엇입니까? )


2

컴파일러가 볼 때 add(3, 4)그 의미를 알아야합니다. forward 선언을 사용하면 기본적으로 컴파일러에게 add두 개의 정수를 가져 와서 정수를 반환하는 함수임을 알립니다. 이것은 스택에 올바른 표현으로 4와 5를 넣어야하며 add에 의해 반환되는 것이 어떤 유형인지 알아야하기 때문에 컴파일러에 중요한 정보입니다.

그 때, 컴파일러는 걱정하지 않는 실제 의 구현 add, 즉 어디 있는지 (또는이 경우 입니다 심지어 하나)하고 컴파일합니다. 링커가 호출 될 때 소스 파일 컴파일 한 후에 나중에 볼 수 있습니다 .


1
int add(int x, int y); // forward declaration using function prototype

"앞으로 선언"을 더 자세히 설명 할 수 있습니까? main () 함수에서 사용하면 어떤 문제가 있습니까?

와 동일합니다 #include"add.h". 알고 있으면 프리 프로세서 #include#include지시문 을 작성하는 .cpp 파일에서 언급 한 파일을 확장합니다 . 즉, 당신이 쓰면 #include"add.h"같은 것을 얻는다는 것입니다. 마치 "포워드 선언"을하는 것과 같습니다.

나는 add.h이 줄이 있다고 가정합니다 .

int add(int x, int y); 

1

한 가지 간단한 부록 : 일반적으로 함수 / 변수 등이 구현되는 .c (pp) 파일에 속하는 헤더 파일에 이러한 순방향 참조를 넣습니다. 귀하의 예에서 다음과 같이 보일 것입니다 : add.h :

extern int add (int a, int b);

extern 키워드는 함수가 실제로 외부 파일에 선언되어 있음을 나타냅니다 (라이브러리 등일 수도 있음). main.c는 다음과 같습니다.

#포함 
#include "add.h"

int main ()
{
.
.
.


그러나 선언을 헤더 파일에만 넣지 않습니까? 이것이 함수가 "add.cpp"에 정의되어 있고 앞으로 선언을 사용하는 이유라고 생각합니다. 감사.
단순성

0

한 가지 문제는 컴파일러가 알지 못한다는 것입니다. 어떤 종류의 값이 함수에 의해 전달되는지; int이 경우 함수가를 반환한다고 가정 하지만 이것이 잘못 될 수있는만큼 정확할 수 있습니다. 또 다른 문제는 컴파일러가 알지 못하는, 함수가 어떤 종류의 인수를 기대하는지, 그리고 잘못된 종류의 값을 전달하면 경고 할 수 없다는 것입니다. 전달할 때 적용되는 특수 "프로모션"규칙이 있습니다. 예를 들어 부동 소수점 값을 선언되지 않은 함수 (컴파일러는 double을 넓히기 위해 확장해야 함)에 해당합니다. 런타임에.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.