为什么在 C++ 中需要前向声明
编译器希望确保您没有犯拼写错误或将错误数量的参数传递给函数。因此,它坚持在使用它之前首先看到“add”(或任何其他类型、类或函数)的声明。
这实际上只是允许编译器更好地验证代码并允许它整理松散的末端,以便它可以生成一个看起来整洁的目标文件。如果您不必转发声明内容,编译器将生成一个目标文件,该文件必须包含有关函数add 可能是什么的所有可能猜测的信息。并且链接器必须包含非常聪明的逻辑来尝试找出您实际打算调用的add,当add 函数可能存在于不同的目标文件中时,链接器将与使用 add 生成的目标文件连接dll 或 exe。链接器可能会得到错误的add。假设您想使用int add(int a, float b),但不小心忘记了写它,但链接器发现了一个已经存在的int add(int a, int b),并认为这是正确的并使用了它。您的代码可以编译,但不会按照您的预期进行。
因此,为了保持明确并避免猜测等,编译器坚持在使用之前声明所有内容。
声明与定义的区别
顺便说一句,了解声明和定义之间的区别很重要。声明只提供了足够的代码来显示某些东西的样子,因此对于函数,这是返回类型、调用约定、方法名称、参数及其类型。但是,不需要该方法的代码。对于定义,您需要声明,然后还需要函数的代码。
前向声明如何显着减少构建时间
您可以通过#includ'ing 已包含函数声明的标头将函数声明放入当前的.cpp 或.h 文件中。但是,这可能会减慢您的编译速度,尤其是如果您将标头 #include 放入程序的 .h 而不是 .cpp 时,因为所有 #includes .h 您正在编写的内容最终都会 #include'也为您编写#includes 的所有标题添加。突然间,编译器有#included 页面和需要编译的代码页面,即使您只想使用一两个函数也是如此。为避免这种情况,您可以使用前向声明,只需在文件顶部自己键入函数的声明。如果您只使用几个函数,与总是#include 标头相比,这确实可以使您的编译更快。对于非常大的项目,差异可能是一个小时或更长时间的编译时间缩短到几分钟。
打破两个定义相互使用的循环引用
此外,前向声明可以帮助您打破循环。这是两个函数都试图相互使用的地方。当这种情况发生时(这是一件非常有效的事情),您可以#include 一个头文件,但该头文件会尝试#include 您当前正在编写的头文件...然后#includes 另一个标头,其中 #include 包含您正在编写的标头。您陷入了鸡与蛋的局面,每个头文件都试图重新#include 另一个。要解决此问题,您可以在其中一个文件中前向声明您需要的部分,并将 #include 保留在该文件之外。
例如:
文件 Car.h
#include "Wheel.h" // Include Wheel's definition so it can be used in Car.
#include <vector>
class Car
{
std::vector<Wheel> wheels;
};
文件 Wheel.h
嗯...这里需要声明Car,因为Wheel 有一个指向Car 的指针,但这里不能包含Car.h,因为它会导致编译器错误。如果包含Car.h,则将尝试包含Wheel.h,其中将包含Car.h,其中将包含Wheel.h,这将永远持续下去,因此编译器会引发错误。解决方案是转发声明Car:
class Car; // forward declaration
class Wheel
{
Car* car;
};
如果Wheel 类有方法需要调用Car 的方法,这些方法可以在Wheel.cpp 中定义,Wheel.cpp 现在可以包含Car.h 而不会导致循环。