C++ / C include 头文件问题

As a newbie programmer, i sometimes encouter the failure that the header files include each other. This article will illustrate why and how to solve this probleam

Why we need header file

(1)  It speeds up compile time.
(2)  It keeps your code more organized.
(3)  It allows you to separate interface from implementation.

(4) C++ programs are built in a two stage process. First, each source file is compiled on its own. The compiler generates intermediate files for each compiled source file. These intermediate files are often called object files -- but they are not to be confused with objects in your code. Once all the files have been individually compiled, it then links all the object files together, which generates the final binary (the program). 

ultimately The #include statement is basically like a copy/paste operation. The compiler will "replace" the #include line with the actual contents of the file you're including when it compiles the file.

Include guards

C++ compilers do not have brains of their own, and so they will do exactly what you tell them to. If you tell them to include the same file more than once, then that is exactly what they will do. And if you don't handle it properly, you'll get some crazy errors.  Usually it happens when you include two files that each include the same file.


An Include Guard is a technique which uses a unique identifier that you #define at the top of the file.

//example.h

#ifndef __X_H_INCLUDED__   // if x.h hasn't been included yet...
#define __X_H_INCLUDED__   //   #define this so the compiler knows it has been included
       // something you write
    
#endif

 example.h is included -- and if example.h is included a second time, the compiler will skip over the header because the #ifndef check will fail.

The "right way" to include

Classes you create will often have dependencies on other classes. A derived class, for example, will always be dependent on its parent, because in order to be derived from the parent, it must be aware of its parent at compile time.

There are two basic kinds of dependencies you need to be aware of:
1) stuff that can be forward declared
2) stuff that needs to be #included

If, for example, class A uses class B, then class B is one of class A's dependencies. Whether it can be forward declared or needs to be included depends on how B is used within A:

- do nothing if: A makes no references at all to B
- do nothing if: The only reference to B is in a friend declaration
- forward declare B if: A contains a B pointer or reference: B* myb;
- forward declare B if: one or more functions has a B object/pointer/reference
as a parementer, or as a return type: B MyFunction(B myb);
- #include "b.h" if: B is a parent class of A
- #include "b.h" if: A contains a B object: B myb;

Circular Dependencies

// a.h -- assume it's guarded
#include "b.h"

class A { B* b; };


// b.h -- assume it's guarded
#include "a.h"

class B { A* a };


// a.cpp
#include "a.h"

// when compile a.cpp, the complier will do the following:
#include "a.h"

   // start compiling a.h
   #include "b.h"

      // start compiling b.h
      #include "a.h"

         // compilation of a.h skipped because it's guarded

      // resume compiling b.h
      class B { A* a };        // <--- ERROR, A is undeclared

  "the right way" and forward declare when you can instead of #including needlessly, this usually isn't a problem. As long as the circle is broken with a forward declaration at some point, you're fine.

Function inline

class B
{
public:
  void Func(const A& a)   // parameter, so forward declare is okay
  {
    a.DoSomething();      // but now that we've dereferenced it, it
                          //  becomes an #include dependency
               // = we now have a potential circular inclusion
  }
};



// b.h  (assume its guarded)

//------------------
class A;  // forward declared dependency

//------------------
class B
{
public:
  void Func(const A& a);  // okay, A is forward declared
};

//------------------
#include "a.h"        // A is now an include dependency

inline void B::Func(const A& a)
{
  a.DoSomething();    // okay!  a.h has been included
}


// b.h

    // blah blah

class B { /* blah blah */ };

#include "b_inline.h"  // or I sometimes use "b.hpp"


// b_inline.h (or b.hpp -- whatever)

#include "a.h"
#include "b.h"  // not necessary, but harmless
                //  you can do this to make this "feel" like a source
                //  file, even though it isn't

inline void B::Func(const A& a)
{
  a.DoSomething();
}

Forward declaring templates

the wrong way:

// a.h

// included dependencies
#include "b.h"

// the class template
template <typename T>
class Tem
{
 /*...*/
  B b;
};

// class most commonly used with 'int'
typedef Tem<int> A;  // typedef'd as 'A'

// b.h

// forward declared dependencies
class A;  // error!

// the class
class B
{
 /* ... */
  A* ptr;
};

Reason :While this seems perfectly logical, it doesn't work! (Although, logically you really think it should. This is an irritation of the language). Because 'A' isn't really a class, but rather a typedef, the compiler will bark at you. Also notice that we can't just #include "a.h" here because of a circular dependency problem.

A practical solution to this problem is to create an alternative header which has the forward declarations of your templated classes and their typedefs.

//a.h

#include "b.h"

template <typename T>
class Tem
{
 /*...*/
  B b;
};


//a_fwd.h

template <typename T> class Tem;
typedef Tem<int> A;


//b.h

#include "a_fwd.h"

class B
{
 /*...*/
  A* ptr;
};

 // the orignal article website:  http://www.cplusplus.com/articles/Gw6AC542/

原文地址:https://www.cnblogs.com/yetanghanCpp/p/8879880.html