为什么要将函数声明和定义放在单独的文件中

Why put function declarations and definitions in separate files?

本文关键字:单独 文件 定义 函数 声明 为什么      更新时间:2023-10-16

在codeacademy的函数课程中,如果您要调用程序中的函数,它们会教您使用三个文件:

  • 我通过反复试验发现的int main()文件是c++程序部分不可或缺的一部分(我想…),文件扩展名为.cpp

  • DECLARING函数的头文件,文件扩展名为.hpp。

  • 一个单独的文件,具有DEFINITION函数,具有.cpp文件扩展名

在头文件中单独声明和定义函数,并简单地将它们包含在int main()之上,这是否可行?对我来说,有单独的声明和定义文件似乎会混淆更大项目中的问题。

在大型项目中,通常只需要类型和函数声明,而不需要定义。例如,在其他头文件中。如果所有定义都在标头中,那么包含多个其他标头及其可传递包含的组合结果将导致巨大的编译单元。这大大缩短了编译时间,因为编译器需要处理的代码量会激增到超出需要的数量级。这也会影响链接时间,因为链接器将有更多的工作要做,以丢弃更多对象文件中包含的重复项。

除非所有内容都标记为inline,否则您也很容易遇到ODR(一个定义规则)问题。

在大型项目中,许多文件可能需要函数声明,但函数定义应该只编译一次。它在链接时与所有需要它的地方相结合。

这个问题有两个答案

  1. 程序员为什么要把东西分成多个.h/.hpp和.cpp文件?

    我相信这里的答案是,当你的.cpp文件变得很大,其中有很多代码可能与需要提供该文件提供的功能的人无关时,它可以帮助组织。这里有一个例子:

    假设您有一些在屏幕上显示图像的c++代码。作为想要使用该代码的人,您可能对该代码所公开的函数/类感兴趣,这些函数/类可以让您控制该功能。也许代码公开了以下有用的函数:

    • WriteImageToScreen(int position_x, int position_y)
    • ClearScreen()

    查看仅告诉您可以使用什么而不是如何实现所有这些的头文件会容易得多。实现这两个函数以便调用它们很可能需要1000行代码和一堆你不关心的变量和语句。不必阅读这些内容可以帮助您专注于代码的重要部分。你想要与之互动的作品。

    我展示了这个例子,就好像你在调用别人的代码,但这同样适用于你自己的代码。随着项目越来越大,可以方便地对文件中的每个功能进行摘要。

    既然如此,并不是每个人都同意这是正确的做事方式,或者说这是有帮助的。

  2. 为什么编译器需要将事物拆分为多个.h/.hpp和.cpp文件?

    如果你不熟悉这个术语,编译器就是将源代码文本转换为计算机可以执行的程序。

    那么,为什么编译器需要单独的.hpp/.cpp文件呢?其他人已经用这个方法一针见血了,但如果某个东西被多次定义,c++编译器就会感到困惑。如果您将所有内容都放在一个头文件中,那么当您将该头包含在多个文件中时,它将被定义多次。因此,从本质上讲,这又回到了组织问题。

    我见过程序员,他们只有一个主文件,然后在编译时,所有代码都直接包含在该主文件中

#include "SomeFile.cpp"
#include "AnotherFile.cpp"
// ...
#include "SoManyFiles.cpp"
int main()
{
DoStuff();
}

我相信这被称为巨石建筑,不建议这样做。

如果你有一个玩具项目,你可以。

如果您有1000000行代码,那么您的构建时间将非常糟糕。

C++20引入了模块,这应该会使整个问题消失。

其他语言的工具可以从"模块"中提取接口。希望当C++20到来时,工具将变得可用。

从实现中分离接口的唯一好理由是是一个接口的多个实现。例如VHDL,并且将在C++20模块中提供。实用的原因是汇编的速度和易读性。

一个小型C++程序可以(通常由)一个翻译单元,例如几千行C++代码。在这种情况下,您可以有一个myprog.ccC++源文件(里面有几个#include-)。

但是,当你在团队中处理一个更大的程序时,有几个C++源文件是很方便的。

一些C++文件是由另一个程序生成的(这被称为元编程或源代码到源代码编译),可能有一百万行C++行。ANTLR或GNU Bison或TypeScript2Cxx能够生成C++代码。

但是,如果您在像Alice和Bob这样的团队中工作,则可以方便地确定Alice负责alice.cc,Bob正在编写bob.cc,并且两者都在一个公共头文件header.hh上合作,该头文件在alice.ccbob.cc中都是#include-d。header.hh实际上定义了软件项目的API。

阅读更多关于版本控制系统(我更喜欢git)和构建自动化工具(如ninja或make)的信息。

在gitlab、github或其他地方的现有开源项目的C++代码中寻找灵感(特别是Clang和GCC的源代码,这两个编译器都是主要的C++编译器)。

FWIW,在GCC 10.1(2020年5月)中,gcc/go/gofrontend/expressions.cc文件是手写的,有19711行C++代码,因此有近两万行。它们是每天汇编的。我确实认识从事这项工作的人,他们都是才华横溢的专业人士。FTLK 1.4最大的文件是它的src/Fl_Text_Display.cxx,有4175行C++。

根据个人经验,您可能有一个包含几十行C++的C++函数(只有当生成C++代码时,这才有实际意义),但优化编译器的编译时间是不可取的。您可以调整我的manydl.c程序以生成任意大小的c++文件(它目前生成具有"可调"大小函数的"随机"c文件)。但是Fluid或Qt Designer生成的C++代码可能相当大,并且为GUI生成的C++程序通常由长但概念简单的函数组成。

C++11标准中的任何内容(参见n3337)都不需要几个翻译单元。您可能有(参见sqlite的示例)一个一百万行的C++文件foo.cc。您还可以生成一些C++源代码。Qt项目,GCC编译器。雅克·皮特拉特(Jacques Pitrat)关于"人造人:有意识机器的良心"的书ISBN 978-1848211018用很多页解释了为什么这种方法是值得的。