Введение в предварительные объявления в C++

Предварительное объявление относится к объявлению класса, функции, шаблона или структуры, которое является только объявлением и не содержит связанных конкретных определений. Во многих случаях мы можем использовать предварительные объявления вместо операторов #include. Предварительное объявление класса просто сообщает компилятору, что это тип, но не может сообщить конкретное содержимое типа, такое как размер и члены. Объекты этого класса нельзя определить или использовать во встроенных функциях-членах, пока не будет предоставлен полный класс. Файлы заголовков расскажут вам один за другим. нравиться:

class Screen;

Форвардная декларация, также известная как форвардная декларация (форвардная декларация). После объявления и до определения класс Screen является неполным типом (incomplete type), то есть известно, что Screen является типом, но неизвестно, какие члены он содержит.

Неполные типы могут использоваться только ограниченным образом. Объект этого типа не может быть определен. Неполные типы можно использовать только для определения указателей и ссылок на тип или для объявления (но не определения) функций, которые используют тип в качестве типа параметра или типа возвращаемого значения. Зависимости компиляции можно уменьшить, используя предварительные объявления с объявлениями указателя или ссылочного типа.

Никогда не включайте заголовок #include, когда достаточно предварительного объявления.

Роль предварительного объявления:

  •  Это может уменьшить зависимости компиляции и сократить время компиляции (если заголовочный файл будет изменен, это вызовет несколько перекомпиляций);
  •  Может скрывать детали;
  •  Может уменьшить размер класса (предварительное объявление сообщит о существовании этого класса, не предоставляя всех деталей определения класса);
  •  Сокращение включает, чтобы предотвратить взаимные ссылки между классами для формирования зависимостей, что приводит к сбою компиляции.

 Вот как форвардные объявления описаны в Руководстве по стилю Google C++:

  •  По возможности избегайте предварительных объявлений. Используйте #include для включения необходимых файлов заголовков.
  •  Так называемые предварительные объявления — это чистые объявления классов, функций и шаблонов без сопутствующих определений.

 преимущество:

  •  Упреждающие объявления могут сэкономить время компиляции, а избыточные директивы #include заставят компилятор разворачивать больше файлов и обрабатывать больше входных данных.
  •  Упреждающие объявления могут сэкономить ненужное время перекомпиляции. #include вызывает многократную перекомпиляцию кода для несвязанных изменений в файлах заголовков.

 недостаток:

  •  Форвардные объявления скрывают зависимости, и при изменении файлов заголовков пользовательский код пропускает необходимый процесс перекомпиляции.
  •  Форвардные объявления могут быть нарушены последующими изменениями в библиотеке. Фронтальное объявление функций или шаблонов иногда не позволяет разработчикам файлов заголовков изменить их API, например, расширить тип параметра, добавить параметр шаблона с собственными параметрами по умолчанию и т. д.
  •  Поведение при предварительном объявлении символа из пространства имен std:: не определено.
  •  Трудно сказать, когда использовать предварительные объявления и когда использовать #include. В крайних случаях замена включения предварительными объявлениями может даже неявно изменить смысл кода.

 в заключение:

  •  Старайтесь избегать предварительного объявления сущностей, определенных в других проектах.
  •  Функции: Всегда используйте #include.
  •  Шаблоны классов: сначала используйте #include.

 Ниже приведен отрывок из «Использование неполных (вперед) объявлений»:

 Неполное объявление (неполное объявление часто называют опережающим объявлением) — это ключевое слово class или struct, за которым следует имя класса или типа структуры. Оно сообщает компилятору, что именованный класс или тип структуры существует, но ничего не говорит. вообще о функциях-членах или переменных класса или структуры; это упущение означает, что это (серьезно) неполное объявление типа. Поскольку неполное объявление не сообщает компилятору, что находится в классе или структуре, пока компилятор не получит полное объявление, он не сможет скомпилировать код, который ссылается на члены класса или структуры или требует знания размер объекта класса или структуры (чтобы узнать размер, необходимо знать типы переменных-членов).

 По возможности используйте неполное объявление в заголовочном файле. Используя неполное объявление в заголовочном файле, мы можем устранить необходимость #include заголовочного файла для класса или структуры, что уменьшает связь или зависимости между модулями, что приводит к более быстрой компиляции и упрощению разработки. Если файлу .cpp необходимо получить доступ к членам класса или структуры, он будет включать заголовок, содержащий полное объявление.

 Когда будет работать неполное объявление в заголовочном файле:

 (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) Если вы используете непрозрачный тип X в качестве переменной-члена класса, объявленного в AhThis, это тип, на который ссылаются только через указатель, полное объявление которого не должно быть доступно и не находится ни в одном заголовочном файле. . Таким образом, неполное объявление типа — это единственное объявление, которое ваш код когда-либо сделает или потребует либо в Ah, либо в A.cpp.


Когда неполное объявление не будет работать в заголовочном файле:

 (1) 、 Если ваш заголовочный файл Ah объявляет класс A, в котором не полностью объявленный тип X появляется как тип переменной-члена.

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


 (2) 、 Если ваш заголовочный файл Ah объявляет класс A, в котором не полностью объявленный тип X является базовым

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_


 

Supongo que te gusta

Origin blog.csdn.net/weixin_58045467/article/details/130721604
Recomendado
Clasificación