zoukankan      html  css  js  c++  java
  • 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/

  • 相关阅读:
    ArcGIS API ForJS 3.25开发总结(持续更新)
    geotools实现将shp导入mysql
    OL4如何以TMS服务调用WMTS服务的缓存切片
    PostGIS type: LINESTRING[2] We have a Multilinestring with 2 parts, can't use -S switch!解决方案
    OL4加载geowebcache 部署的离线切片
    geoserver发布自定义坐标系矢量切片(以北京54投影坐标系为例)
    geoserver使用sld(点通过svg填充)样式
    关于用geoserver进行切图自定义GridSet出现EPSG:2379: 0.0,0.0,-1.0,-1.0
    OL4通过ajax加载geoserver中WMS的GetFeatureInfo实现点击查询(解决跨域问题)
    作为一个GIS开发人员,你必须知道的坐标系那些事
  • 原文地址:https://www.cnblogs.com/yetanghanCpp/p/8879880.html
Copyright © 2011-2022 走看看