开发者

When are includes not needed?

I admit I'm a bit naive when it comes to includes. My understanding is that if you use it in your class you either need to include it or forward declare it. But then I was going through some code and I saw this:

// 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;
// ...

Can someone explain to me why B and C does not need to include Helper? Also, w开发者_Python百科hat are the advantages/disadvantages to organizing includes this way? (Besides the obvious less typing.)

Thanks.


When you #include some header (or other) file into a .cpp file, that #include statement is simply replaced by the content of the header file. For example:

//header.h
int i;
float f;

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

After preprocessing stage, file.cpp will look like,

int i;
float f;

int main()
{}

Can see this in g++ using g++ -E file.cpp > TEMP, which shows you just the preprocessed files.

In your present question context, you must have #include helper.h in/before B.h and C.h as they appear before and you declare an object of those types.

Also, it's not good practice to rely on the arrangement of header files to get the code working, because once you alter arrangement little, the whole hierarchy collapses with several compilation errors.

Instead #include everything in the file if you are using it and you can use the #ifndef guards to avoid multiple inclusion:

//helper.h
#ifndef HELPER_H
#define HELPER_H

// content of helper file

#endif


If the class definitions for B and C don't actually refer to any of the members of the Helper class, then the compiler doesn't need to see the full definition of the Helper class in their header files. A forward declaration of the Helper class is sufficient.

For example, if the definition of the B class only uses pointers or references to Helper, then a forward reference is recommended :

class Helper;

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

If the definition of the B class uses an instance of the Helper class (ie. it needs to know the size of the Helper instance), or otherwise refers to the definition of the Helper class (in template functions eg.), then you need to make the full definition of the Helper class visible (most likely by including the header file that defines the Helper class) :

#include "helper.h"

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

The rule of when to use includes versus forward declarations is relatively straightforward : use forward declarations when you can, and includes when you have to.

The advantage of this is clear : the less includes you have, the less dependencies there are between header files (and the more you'll speed up compilation).


Think of #include as literally including the text of the other file in this one - it's exactly the same as if you had copied it in. So in this case, the reason B and C don't need to include Helper is because you've included it in the same "compilation unit", which is what the combination of a .cpp file and all its includes is called.


With templates, a forward declaration might be enough even if the members of the forward declared type are referred to in the header file.

For example, the boost::shared_ptr<T> implementation only forward declares boost::weak_ptr<T> even though it is used in two constructors. Here is a code snipped from 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;
        }
    }

In this case, a forward declaration of boost::weak_ptr<T> is enough since the two constructors do not get instantiated unless the definition for the forward declared boost::weak_ptr<T> has been included in the compilation unit that is using those constructors.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜