什么是 C++ 中的前向声明?

2022-01-30 00:00:00 forward-declaration c++ declaration

在:http://www.learncpp.com/cpp-tutorial/19-头文件/

以下提到:

添加.cpp:

int add(int x, int y){返回 x + y;}

main.cpp:

#include <iostream>int add(int x, int y);//使用函数原型的前向声明主函数(){使用命名空间标准;cout <<3 和 4 之和是" <<添加(3, 4) <<结束;返回0;}

<块引用>

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

您能进一步解释前向声明"吗?如果我们在 main() 函数中使用它会出现什么问题?

解决方案

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

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

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

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

声明与定义的区别

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

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

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

打破两个定义相互使用的循环引用

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

例如:

文件 Car.h

#include Wheel.h"//包含 Wheel 的定义,以便它可以在 Car 中使用.#include <向量>类车{std::vector<轮子>轮子;};

文件 Wheel.h

嗯... Car 的声明在这里是必需的,因为 Wheel 有一个指向 Car 的指针,但是 Car.h 不能包含在此处,因为它会导致编译器错误.如果包含 Car.h,则将尝试包含 Wheel.h,其中包含 Car.h,其中包含 Wheel.h 并且这将永远持续下去,因此编译器会引发错误.解决方案是转发声明 Car :

类汽车;//前向声明类轮{车*车;};

如果类Wheel有方法需要调用Car的方法,这些方法可以在Wheel.cpp中定义Wheel.cpp 现在可以包含 Car.h 而不会导致循环.

At: http://www.learncpp.com/cpp-tutorial/19-header-files/

The following is mentioned:

add.cpp:

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

main.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;
}

We used a forward declaration so that the compiler would know what "add" was when compiling main.cpp. As previously mentioned, writing forward declarations for every function you want to use that lives in another file can get tedious quickly.

Can you explain "forward declaration" further? What is the problem if we use it in the main() function?

解决方案

Why forward-declare is necessary in C++

The compiler wants to ensure you haven't made spelling mistakes or passed the wrong number of arguments to the function. So, it insists that it first sees a declaration of 'add' (or any other types, classes, or functions) before it is used.

This really just allows the compiler to do a better job of validating the code and allows it to tidy up loose ends so it can produce a neat-looking object file. If you didn't have to forward declare things, the compiler would produce an object file that would have to contain information about all the possible guesses as to what the function add might be. And the linker would have to contain very clever logic to try and work out which add you actually intended to call, when the add function may live in a different object file the linker is joining with the one that uses add to produce a dll or exe. It's possible that the linker may get the wrong add. Say you wanted to use int add(int a, float b), but accidentally forgot to write it, but the linker found an already existing int add(int a, int b) and thought that was the right one and used that instead. Your code would compile, but wouldn't be doing what you expected.

So, just to keep things explicit and avoid guessing, etc, the compiler insists you declare everything before it is used.

Difference between declaration and definition

As an aside, it's important to know the difference between a declaration and a definition. A declaration just gives enough code to show what something looks like, so for a function, this is the return type, calling convention, method name, arguments, and their types. However, the code for the method isn't required. For a definition, you need the declaration and then also the code for the function too.

How forward-declarations can significantly reduce build times

You can get the declaration of a function into your current .cpp or .h file by #includ'ing the header that already contains a declaration of the function. However, this can slow down your compile, especially if you #include a header into a .h instead of .cpp of your program, as everything that #includes the .h you're writing would end up #include'ing all the headers you wrote #includes for too. Suddenly, the compiler has #included pages and pages of code that it needs to compile even when you only wanted to use one or two functions. To avoid this, you can use a forward-declaration and just type the declaration of the function yourself at the top of the file. If you're only using a few functions, this can really make your compiles quicker compared to always #including the header. For really large projects, the difference could be an hour or more of compile time bought down to a few minutes.

Break cyclic references where two definitions both use each other

Additionally, forward-declarations can help you break cycles. This is where two functions both try to use each other. When this happens (and it is a perfectly valid thing to do), you may #include one header file, but that header file tries to #include the header file you're currently writing... which then #includes the other header, which #includes the one you're writing. You're stuck in a chicken and egg situation with each header file trying to re #include the other. To solve this, you can forward-declare the parts you need in one of the files and leave the #include out of that file.

Eg:

File Car.h

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

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

File Wheel.h

Hmm... the declaration of Car is required here as Wheel has a pointer to a Car, but Car.h can't be included here as it would result in a compiler error. 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, so instead the compiler raises an error. The solution is to forward declare Car instead:

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 include Car.h without causing a cycle.

相关文章