Когда включены не нужны? - PullRequest
2 голосов
/ 12 июля 2011

Признаюсь, я немного наивен, когда дело касается включений. Насколько я понимаю, если вы используете его в своем классе, вам нужно либо включить его, либо объявить его заранее Но потом я просматривал некоторый код и увидел это:

// file: A.cpp
#include "Helper.h"
#include "B.h"
#include "C.h"
#include "A.h"
// ...

// file: A.h
B b;
C c;
// ...

// file: B.h
Helper h;
// ...

// file: C.h
Helper h;
// ...

Может кто-нибудь объяснить мне, почему B и C не должны включать Helper? Кроме того, какие преимущества / недостатки организации включает в себя этот способ? (Помимо очевидного меньше печатать.)

Спасибо.

Ответы [ 4 ]

5 голосов
/ 12 июля 2011

Когда вы #include добавляете какой-либо заголовочный (или другой) файл в файл .cpp, этот оператор #include просто заменяется содержимым файла заголовка.Например:

//header.h
int i;
float f;

// file.cpp
#include"header.h"
int main()
{}

После этапа предварительной обработки файл file.cpp будет выглядеть следующим образом:

int i;
float f;

int main()
{}

Это можно увидеть в g ++, используя g++ -E file.cpp > TEMP, который показывает только предварительно обработанные файлы.

В вашем текущем контексте вопроса вы должны иметь #include helper.h в / до B.h и C.h, как они появляются раньше, и вы объявляете объект этих типов.

Кроме того, не рекомендуется полагаться на расположение заголовочных файлов для обеспечения работоспособности кода, поскольку после небольшого изменения расположения вся иерархия разрушается с несколькими ошибками компиляции.

Вместо #include все в файлеесли вы используете его, и вы можете использовать #ifndef охранники, чтобы избежать многократного включения:

//helper.h
#ifndef HELPER_H
#define HELPER_H

// content of helper file

#endif
2 голосов
/ 12 июля 2011

Если определения классов для B и C фактически не ссылаются ни на один из членов класса Helper, то компилятору не нужно видеть полное определение класса Helper в их заголовочных файлах. Предварительного объявления класса Helper достаточно.

Например, если определение класса B использует только указатели или ссылки на Helper, то рекомендуется прямая ссылка :

class Helper;

class B {
    // <SNIP>
    Helper* helper;
    // <SNIP>
    void help(const Helper& helper);
    // <SNIP>
};

Если определение класса B использует экземпляр класса Helper (т. Е. Ему нужно знать размер экземпляра Helper), или иначе относится к определению класса Helper (в функциях шаблона, например.), тогда вам нужно сделать видимым полное определение класса Helper (скорее всего, включив заголовочный файл, который определяет класс Helper):

#include "helper.h"

class B {
    // <SNIP>
    Helper helper;
    // <SNIP>
    void help(Helper helper);
    // <SNIP>
};

Правило того, когда использовать включения, а не предварительные объявления, относительно простое: использовать предварительные объявления, когда вы можете, и включает, когда вам нужно .

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

1 голос
/ 12 июля 2011

Думайте о #include как о буквально включающем текст другого файла в этот файл - он точно такой же, как если бы вы его скопировали. Так что в этом случае причина B и C не должна включать Helper в том, что потому что вы включили его в одну и ту же «единицу компиляции», которая называется комбинацией файла .cpp и всех включенных в него файлов.

0 голосов
/ 12 июля 2011

При использовании шаблонов может быть достаточно предварительного объявления, даже если в заголовочном файле упоминаются члены объявленного в прямом направлении типа.

Например, реализация boost::shared_ptr<T> только для прямого объявления объявляет boost::weak_ptr<T> дажехотя он используется в двух конструкторах.Вот код, полученный из http://www.boost.org/doc/libs/1_47_0/boost/smart_ptr/shared_ptr.hpp:

namespace boost
{
// ...
template<class T> class weak_ptr;
// ...
template<class T> class shared_ptr
{
// ...
public:
// ...
    template<class Y>
    explicit shared_ptr(weak_ptr<Y> const & r): pn(r.pn) // may throw
    {
        // it is now safe to copy r.px, as pn(r.pn) did not throw
        px = r.px;
    }

    template<class Y>
    shared_ptr( weak_ptr<Y> const & r, boost::detail::sp_nothrow_tag ): px( 0 ), pn( r.pn, boost::detail::sp_nothrow_tag() ) // never throws
    {
        if( !pn.empty() )
        {
            px = r.px;
        }
    }

. В этом случае достаточно предварительного объявления boost::weak_ptr<T>, так как два конструктора не создаются, если только определение для объявленного форварда boost::weak_ptr<T> не имеетбыл включен в модуль компиляции, который использует эти конструкторы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...