什么是 C 中的前向声明?

新手上路,请多包涵

链接中,提到了以下内容:

添加.cpp:

 int add(int x, int y)
{
    return x + y;
}

主.cpp:

 #include <iostream>

int add(int x, int y); // forward declaration using function prototype

int main()
{
    using namespace std;
    cout << "The sum of 3 and 4 is " << add(3, 4) << endl;
    return 0;
}

我们使用了前向声明,以便编译器在编译 main.cpp 时知道“ add ”是什么。如前所述,为要使用的每个位于另一个文件中的函数编写前向声明会很快变得乏味。

你能进一步解释一下“ 前向声明”吗?如果我们在 main 函数中使用它会出现什么问题?

原文由 Simplicity 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.6k
2 个回答

为什么在 C++ 中需要前向声明

编译器希望确保您没有犯拼写错误或将错误数量的参数传递给函数。因此,它坚持在使用它之前首先看到“add”(或任何其他类型、类或函数)的声明。

这实际上只是允许编译器更好地验证代码并允许它整理松散的末端,以便它可以生成一个看起来整洁的目标文件。如果您不必转发声明内容,编译器将生成一个目标文件,该文件必须包含有关函数 add 可能是什么的所有可能猜测的信息。当 add 函数可能存在于链接器加入的不同目标文件中时,链接器必须包含非常聪明的逻辑来尝试 add 您实际打算调用的 —与使用 add 产生 dllexe 的那个。链接器可能会出错 add 。假设您想使用 int add(int a, float b) ,但不小心忘记写它,但链接器发现了一个已经存在的 int add(int a, int b) 并认为这是正确的并使用了它。您的代码会编译,但不会按照您的预期进行。

因此,为了保持明确并避免猜测等,编译器坚持要求您在使用之前声明所有内容。

声明与定义的区别

顺便说一句,了解声明和定义之间的区别很重要。声明只提供了足够的代码来显示某些东西的样子,因此对于函数,这是返回类型、调用约定、方法名称、参数及其类型。但是,不需要该方法的代码。对于定义,您需要声明,然后还需要函数的代码。

前向声明如何显着减少构建时间

您可以将函数声明添加到当前的 .cpp.h 文件中,方法是#includ’ing 已经包含函数声明的标头。但是,这可能会减慢您的编译速度,特别是如果您将程序的 #include 标头放入 .h 而不是 .cpp .h 所有内容 --- 你写的最后会#include’ing你写#includes的所有标题。突然间,编译器有#included 页面和需要编译的代码页面,即使您只想使用一两个函数也是如此。为避免这种情况,您可以使用前向声明并在文件顶部自己键入函数的声明。如果您只使用几个函数,与总是#include 标头相比,这确实可以使您的编译更快。对于非常大的项目,差异可能是一个小时或更长时间的编译时间缩短到几分钟。

中断两个定义都相互使用的循环引用

此外,前向声明可以帮助您打破循环。这是两个函数都试图相互使用的地方。当这种情况发生时(这是一件非常有效的事情),您可能会 #include 一个头文件,但该头文件会尝试 #include 您当前正在编写的头文件。 .. 然后 #includes 另一个标题,其中 #includes 您正在编写的标题。您陷入了先有鸡还是先有蛋的困境,每个头文件都试图重新#include 另一个。要解决此问题,您可以在其中一个文件中前向声明您需要的部分,并将#include 保留在该文件之外。

例如:

文件车.h

 #include "Wheel.h"  // Include Wheel's definition so it can be used in Car.
#include <vector>

class Car
{
    std::vector<Wheel> wheels;
};

文件轮.h

Hmm… the declaration of Car is required here as Wheel has a pointer to a Car , but Car.h can’t be包括在此处,因为它会导致编译器错误。 If Car.h was included, that would then try to include Wheel.h which would include Car.h which would include Wheel.h and this would go on forever ,因此编译器会引发错误。解决方案是转发声明 Car 代替:

 class Car;     // forward declaration

class Wheel
{
    Car* car;
};

If class Wheel had methods which need to call methods of Car , those methods could be defined in Wheel.cpp and Wheel.cpp is now able to包括 Car.h 而不会导致循环。

原文由 Scott Langham 发布,翻译遵循 CC BY-SA 4.0 许可协议

关于以下内容的一个快速附录:通常,您将这些前向引用放入属于 .c(pp) 文件的头文件中,其中实现了函数/变量等。在您的示例中,它看起来像这样:add.h:

 extern int add(int a, int b);

关键字 extern 表明该函数实际上是在外部文件中声明的(也可以是库等)。你的 main.c 看起来像这样:

#包括
#include "add.h"

主函数()
{
.
.
.

原文由 jack 发布,翻译遵循 CC BY-SA 2.5 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题