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/

  • 相关阅读:
    Php ArrayIterator的几个常用方法
    13 子元素和后代元素选择器 1 元素之间的关系 2 后代元素选择器 后代 元素用的是空格 3 子元素选择器 用的是>号 4 ietest的使用
    12常用选择器 1元素选择器 2 id选择器3 class类选择器 4 一个元素可以设置多个class属性值 5 选择器分组又叫并集选择器 6 通配选择器 * 7 复合选择器 交集选择器
    11 1 块元素div的定义 2 常见的块元素 3 块元素的用途 4 内联元素,行内元素,span 5 内联元素和块元素的用途 6 a元素超链接的用法 7 p元素不可以包含任何其他的块元素
    10 1、IDE,dreamweaver,webstorm,hbuilder 2 Hbuilder快捷键
    9 1 css的注释 2 css的语法:选择器,声明块
    8 1 css 2 元素就是标签 2 内联样式 3 css选择器 style ,head ,内部样式表 4 外部样式表 css文件, link标签 ,href,
    7 1 超链接 a标签 href ,_self,_blank 2 center标签 居中显示 3 # 4 回到顶部 5 id定位 6 电子邮件超链接
    5 1 html的语法规范 大小写,注释嵌套,标签成对出现,属性,值,加引号
    4 1meta标签,name ,content,keywords,description,url,refresh
  • 原文地址:https://www.cnblogs.com/yetanghanCpp/p/8879880.html
Copyright © 2011-2022 走看看