为什么有头文件和.cpp文件? 为什么C++有头文件和.cpp文件?

为什么有头文件和.cpp文件? 为什么C++有头文件和.cpp文件?,c++,header-files,C++,Header Files,这是声明接口的预处理器方式。将接口(方法声明)放入头文件,将实现放入cpp。使用库的应用程序只需要知道接口,通过接口可以访问它们。< p>因为在C++中,最终的可执行代码不携带任何符号信息,它或多或少是纯机器代码。 因此,您需要一种与代码本身分离的方法来描述一段代码的接口。这个描述在头文件中。嗯,主要原因是将接口与实现分离。标头声明类(或正在实现的任何类)将执行“什么”,而cpp文件定义它将如何执行这些功能 这减少了依赖性,因此使用头的代码不一定需要知道实现的所有细节,也不需要知道实现所需的任何

这是声明接口的预处理器方式。将接口(方法声明)放入头文件,将实现放入cpp。使用库的应用程序只需要知道接口,通过接口可以访问它们。

< p>因为在C++中,最终的可执行代码不携带任何符号信息,它或多或少是纯机器代码。
因此,您需要一种与代码本身分离的方法来描述一段代码的接口。这个描述在头文件中。

嗯,主要原因是将接口与实现分离。标头声明类(或正在实现的任何类)将执行“什么”,而cpp文件定义它将如何执行这些功能

这减少了依赖性,因此使用头的代码不一定需要知道实现的所有细节,也不需要知道实现所需的任何其他类/头。这将减少编译时间,以及在实现中发生更改时所需的重新编译量


它不是完美的,您通常会使用诸如的技术来正确地分离接口和实现,但这是一个良好的开端。

因为设计库格式的人不想“浪费”空间来获取很少使用的信息,如C预处理器宏和函数声明

因为你需要这些信息来告诉你的编译器“这个函数在链接器工作的时候可以使用”,他们必须拿出第二个文件来存储这些共享信息


C/C++之后的大多数语言都将这些信息存储在输出中(例如Java字节码),或者它们根本不使用预编译格式,总是以源代码形式分发并动态编译(Python、Perl)。

通常,您希望有一个接口定义,而不必发布整个代码。例如,如果您有一个共享库,您将附带一个头文件,它定义了共享库中使用的所有函数和符号。如果没有头文件,则需要发送源文件

在单个项目中,头文件IMHO至少用于两个目的:

  • 清晰性,也就是说,通过将接口与实现分开,更容易阅读代码
  • 编译时。通过尽可能只使用接口,而不是完整的实现,可以缩短编译时间,因为编译器可以简单地引用接口,而不必解析实际代码(理想情况下,只需要一次)
C++编译 < C++编译程序分为2个主要阶段:

  • 第一种是将“源”文本文件编译成二进制“对象”文件:CPP文件是编译后的文件,除非通过原始声明或头包含将其输入,否则编译时不需要了解其他CPP文件(甚至库)。CPP文件通常编译成.OBJ或.O“对象”文件

  • 第二个是将所有“对象”文件链接在一起,从而创建最终的二进制文件(库或可执行文件)

  • 水电站在这一过程中的位置如何

    一个可怜的孤独的CPP文件。。。 每个CPP文件的编译独立于所有其他CPP文件,这意味着如果A.CPP需要B.CPP中定义的符号,如:

    // A.CPP
    void doSomething()
    {
       doSomethingElse(); // Defined in B.CPP
    }
    
    // B.CPP
    void doSomethingElse()
    {
       // Etc.
    }
    
    // A.CPP
    void doSomethingElse() ; // From B.CPP
    
    void doSomething()
    {
       doSomethingElse() ; // Defined in B.CPP
    }
    
    它不会编译,因为A.CPP无法知道“doSomethingElse”的存在。。。除非a.CPP中有声明,如:

    // A.CPP
    void doSomething()
    {
       doSomethingElse(); // Defined in B.CPP
    }
    
    // B.CPP
    void doSomethingElse()
    {
       // Etc.
    }
    
    // A.CPP
    void doSomethingElse() ; // From B.CPP
    
    void doSomething()
    {
       doSomethingElse() ; // Defined in B.CPP
    }
    
    然后,如果您有使用相同符号的C.CPP,则可以复制/粘贴声明

    复制/粘贴警报! 是的,有个问题。复制/粘贴是危险的,并且很难维护。这意味着,如果我们有办法不复制/粘贴,并且仍然声明符号,那就太酷了。。。我们怎么做?包括一些文本文件,通常由h,.h,.h,+,或,我的首选C++文件,.HPP:< /P>后缀。
    // B.HPP (here, we decided to declare every symbol defined in B.CPP)
    void doSomethingElse() ;
    
    // A.CPP
    #include "B.HPP"
    
    void doSomething()
    {
       doSomethingElse() ; // Defined in B.CPP
    }
    
    // B.CPP
    #include "B.HPP"
    
    void doSomethingElse()
    {
       // Etc.
    }
    
    // C.CPP
    #include "B.HPP"
    
    void doSomethingAgain()
    {
       doSomethingElse() ; // Defined in B.CPP
    }
    
    如何包含
    工作? 包括一个文件,本质上,将解析并复制它的内容粘贴到CPP文件中

    例如,在以下代码中,使用A.HPP标头:

    // A.HPP
    void someFunction();
    void someOtherFunction();
    
    。。。来源B.CPP:

    // B.CPP
    #include "A.HPP"
    
    void doSomething()
    {
       // Etc.
    }
    
    。。。纳入后将成为:

    // B.CPP
    void someFunction();
    void someOtherFunction();
    
    void doSomething()
    {
       // Etc.
    }
    
    一件小事——为什么要将B.HPP包括在B.CPP中? 在当前情况下,这是不需要的,B.HPP有
    doSomethingElse
    函数声明,B.CPP有
    doSomethingElse
    函数定义(其本身就是一个声明)。但在更一般的情况下,B.HPP用于声明(和内联代码),可能没有相应的定义(例如,枚举、普通结构等),因此如果B.CPP使用来自B.HPP的声明,则可能需要include。总而言之,默认情况下,源文件包含其标题是“很有品味的”

    结论

    头文件是必要的,因为C++编译器不能单独搜索符号声明,因此,必须通过包含这些声明来帮助它。 最后一句话:您应该在HPP文件的内容周围设置标题保护,以确保多个包含不会破坏任何内容,但总而言之,我相信HPP文件存在的主要原因已在上面解释

    #ifndef B_HPP_
    #define B_HPP_
    
    // The declarations in the B.hpp file
    
    #endif // B_HPP_
    
    甚至更简单(虽然不是标准)


    因为C,这个概念的起源,已经有30年的历史了,而在当时,它是将多个文件中的代码链接在一起的唯一可行的方法


    今天,这是一个可怕的黑客,它完全破坏C++中编译时间,导致无数的不必要的依赖(因为头文件中的类定义暴露了太多关于实现的信息),等等。

    < P>因为C++从C继承了它们。不幸的是,

    < P>响应,

    这减少了依赖性,因此使用标头的代码不会