C++의 정방향 선언 소개

전방 선언은 클래스, 함수, 템플릿 또는 구조의 선언을 말하며 이는 선언일 뿐이며 관련 특정 정의를 포함하지 않습니다. 대부분의 경우 #include 문 대신 정방향 선언을 사용할 수 있습니다. 클래스의 전방 선언은 컴파일러에게 이것이 유형임을 알려주지만 크기 및 멤버와 같은 유형의 특정 내용을 알려줄 수는 없습니다. 이 클래스의 개체는 완전한 클래스가 제공될 때까지 인라인 멤버 함수에서 정의하거나 사용할 수 없습니다. 헤더 파일이 하나씩 알려줄 것입니다. 좋다:

class Screen;

전방 선언(Forward Declaration)은 전방 선언(Forward Declaration)이라고도 합니다. 선언 후와 정의 전에 Screen 클래스는 불완전한 유형(불완전한 유형)입니다.

불완전한 유형은 제한된 방식으로만 사용할 수 있습니다. 이 유형의 개체를 정의할 수 없습니다. 불완전한 유형은 해당 유형에 대한 포인터 및 참조를 정의하거나 해당 유형을 매개변수 유형 또는 반환 유형으로 사용하는 함수를 선언(정의는 아님)하는 데에만 사용할 수 있습니다. 포인터 또는 참조 유형 선언과 함께 정방향 선언을 사용하여 컴파일 종속성을 줄일 수 있습니다.

정방향 선언으로 충분할 때 헤더를 #include하지 마십시오.

전방 선언의 역할:

  •  컴파일 종속성을 줄이고 컴파일 시간을 줄일 수 있습니다(헤더 파일이 수정되면 여러 번의 재컴파일이 발생함).
  •  세부 정보를 숨길 수 있습니다.
  •  클래스의 크기를 줄일 수 있습니다(정방향 선언은 클래스 정의의 모든 세부 정보를 제공하지 않고 이 클래스의 존재를 알려줍니다).
  •  포함을 줄여 클래스 간의 상호 참조를 방지하여 종속성을 형성하여 컴파일 실패를 방지합니다.

 다음은 Google C++ 스타일 가이드에 전방 선언이 설명된 방법입니다.

  •  가능하면 전방 선언을 피하십시오. 필요한 헤더 파일을 포함하려면 #include를 사용하십시오.
  •  소위 정방향 선언은 수반되는 정의가 없는 클래스, 함수 및 템플릿의 순수한 선언입니다.

 이점:

  •  정방향 선언은 컴파일 시간을 절약할 수 있으며 중복 #include는 컴파일러가 더 많은 파일을 확장하고 더 많은 입력을 처리하도록 합니다.
  •  정방향 선언은 불필요한 재컴파일 시간을 절약할 수 있습니다. #include는 헤더 파일의 관련 없는 변경 사항에 대해 코드가 여러 번 다시 컴파일되도록 합니다.

 결점:

  •  전방 선언은 종속성을 숨기고 헤더 파일이 변경되면 사용자 코드는 필요한 재컴파일 프로세스를 건너뜁니다.
  •  전방 선언은 라이브러리에 대한 후속 변경으로 인해 손상될 수 있습니다. 함수 또는 템플릿의 전면 선언으로 인해 헤더 파일 개발자가 API를 변경하지 못하는 경우가 있습니다(예: 매개변수 유형 확장, 자체 기본 매개변수가 있는 템플릿 매개변수 추가 등).
  •  네임스페이스 std::에서 기호를 앞으로 선언하는 동작은 정의되지 않습니다.
  •  전방 선언을 사용해야 하는 경우와 #include를 사용해야 하는 경우를 구분하기는 어렵습니다. 극단적인 경우 포함을 정방향 선언으로 바꾸면 코드의 의미가 암시적으로 변경될 수도 있습니다.

 결론적으로:

  •  다른 프로젝트에 정의된 엔터티를 앞으로 선언하지 않도록 하세요.
  •  기능: 항상 #include를 사용합니다.
  •  클래스 템플릿: 먼저 #include를 사용하세요.

 다음은 "불완전(앞으로) 선언 사용"에서 발췌한 것입니다.

 불완전한 선언(불완전한 선언은 종종 정방향 선언이라고 함)은 키워드 class 또는 struct 다음에 클래스 또는 구조 유형의 이름이 옵니다. 이는 명명된 클래스 또는 구조 유형이 존재함을 컴파일러에 알리지만 아무 말도 하지 않습니다. 클래스 또는 구조체의 멤버 함수 또는 변수에 관한 모든 것; 이 생략은 유형의 (심각한) 불완전한 선언임을 의미합니다. 불완전한 선언은 컴파일러에게 클래스 또는 구조체에 무엇이 있는지 알려주지 않으므로 컴파일러가 완전한 선언을 얻을 때까지 클래스 또는 구조체의 멤버를 참조하는 코드를 컴파일할 수 없습니다. 클래스 또는 구조체 객체의 크기(크기를 알기 위해서는 멤버 변수의 유형을 알아야 함).

 가능하면 헤더 파일에서 불완전한 선언을 사용하십시오. 헤더 파일에서 불완전한 선언을 사용하면 클래스 또는 구조체에 대한 헤더 파일을 #include할 필요가 없으므로 모듈 간의 결합 또는 종속성이 줄어들어 컴파일 속도가 빨라지고 개발이 쉬워집니다. .cpp 파일이 클래스 또는 구조체의 멤버에 액세스해야 하는 경우 완전한 선언을 포함하는 헤더를 #include합니다.

 불완전한 선언이 헤더 파일에서 작동하는 경우:

 (1)、클래스 유형 X가 함수 프로토타입에서 매개변수 유형 또는 반환 유형으로만 나타나는 경우.

class X;
X foo(X x);


 (2)、클래스 타입 X가 포인터(X*) 또는 참조(X&)에 의해서만 참조되는 경우, Ah에 선언된 클래스의 멤버 변수라도 마찬가지입니다.

class X;
class A {
/* other members */
private:
    X* x_ptr;
    X& x_ref;
};


 (3)、Ah에서 선언된 클래스의 멤버 변수로 불투명한 유형 X를 사용하는 경우 이것은 포인터를 통해서만 참조되는 유형이며 완전한 선언을 사용할 수 없으며 헤더 파일에 없습니다. . 따라서 유형의 불완전한 선언은 코드가 Ah 또는 A.cpp에서 작성하거나 필요로 하는 유일한 선언입니다.


불완전한 선언이 헤더 파일에서 작동하지 않는 경우:

 (1)、Ah 헤더 파일이 불완전하게 선언된 유형 X가 멤버 변수의 유형으로 나타나는 클래스 A를 선언하는 경우.

class X;
class A {
private:
    X x_member; // error- can't declare a member variable of incomplete type!
};


 (2)、Ah 헤더 파일이 불완전하게 선언된 유형 X가 기본인 클래스 A를 선언하는 경우

class (A inherits from X).
class X;
class A : public X { // error - baseclass is incomplete type!


 (3)、실제로 유형의 이름을 모르는 경우. 올바른 이름을 모르면 형식을 전달할 수 없습니다. 이는 표준 라이브러리에 정의된 일부 유형에서 문제가 될 수 있습니다. 여기서 유형의 일반 이름은 실제로는 일반적으로 여러 템플릿 매개변수를 사용하여 다른 유형으로 인스턴스화된 특정 템플릿에 대한 typedef입니다. 예를 들어 다음은 std::string 클래스를 불완전하게 선언하는 데 작동하지 않습니다.

class std::string;


Google C++ 스타일 가이드에서는 전방 선언을 최대한 피해야 한다고 지적하고 있습니다. "미완성(정방향) 선언 사용하기"에서 #include 대신 정방향 선언을 사용할 수 있는 경우에는 정방향 선언을 최대한 사용해야 한다고 지적하고 있다.
 다음은 http://stackoverflow.com/questions/553682/when-can-i-use-a-forward-declaration에서 발췌한 것입니다.

#ifndef FBC_MESSY_TEST_FORWARD_DECLARATION_HPP_
#define FBC_MESSY_TEST_FORWARD_DECLARATION_HPP_
 
// reference: http://stackoverflow.com/questions/553682/when-can-i-use-a-forward-declaration
 
/*
 Put yourself in the compiler's position: when you forward declare a type,
 all the compiler knows is that this type exists; it knows nothing about
 its size, members, or methods. This is why it's called an incomplete type.
 Therefore, you cannot use the type to declare a member, or a base class,
 since the compiler would need to know the layout of the type.
*/
// Assuming the following forward declaration.
class X;
 
// Here's what you can and cannot do.
 
// 1. What you can do with an incomplete type:
// 1.1 Declare a member to be a pointer or a reference to the incomplete type:
class Foo_1 {
    X *pt1;
    X &pt2;
};
 
// 1.2 Declare functions or methods which accept/return incomplete types:
void f1(X);
X f2();
 
/* 1.3 Define functions or methods which accept/return pointers/references to
 the incomplete type (but without using its members): */
void f3(X*, X&) {}
X& f4() { X* x = nullptr; return *x; }
X* f5() { X* x = nullptr; return x; }
 
// 2. What you cannot do with an incomplete type:
// 2.1 Use it as a base class
// class Foo_2 : X {} // compiler error!
 
// 2.2 Use it to declare a member:
/* class Foo_2 {
    X m; // compiler error!
}; */
 
// 2.3 Define functions or methods using this type
// void f6(X x) {} // compiler error!
// X f7() {} // compiler error!
 
/* 2.4 Use its methods or fields,
 in fact trying to dereference a variable with incomplete type */
/* class Foo_3 {
    X *m;
    void method() {
        m->someMethod();      // compiler error!
        int i = m->someField; // compiler error!
    }
}; */

/*
 When it comes to templates, there is no absolute rule:
 whether you can use an incomplete type as a template parameter is
 dependent on the way the type is used in the template.
*/
 
/*
 "In computer programming, a forward declaration is a declaration of an identifier
 (denoting an entity such as a type, a variable, or a function) for which the
 programmer has not yet given a complete definition."
 In C++, you should forward declare classes instead of including headers.
 Don’t use an #include when a forward declaration would suffice.
 When you include a header file you introduce a dependency
 that will cause your code to be recompiled whenever the header file changes.
 If your header file includes other header files, any change to those files will
 cause any code that includes your header to be recompiled.
 Therefore, you should prefer to minimize includes,
 particularly includes of header files in other header files.
 You can significantly reduce the number of header files
 you need to include in your own header files by using forward declarations.
*/
 
#endif // FBC_MESSY_TEST_FORWARD_DECLARATION_HPP_


 

Guess you like

Origin blog.csdn.net/weixin_58045467/article/details/130721604