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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4757565/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 16:23:15  来源:igfitidea点击:

What are forward declarations in C++?

c++declarationforward-declaration

提问by Simplicity

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

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

The following is mentioned:

提到了以下内容:

add.cpp:

添加.cpp:

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

main.cpp:

主.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.

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

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

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

回答by Scott Langham

Why forward-declare is necessary in C++

为什么在 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.

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

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.

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

So, just to keep things explicit and avoid the 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. But 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.

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

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.

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

Eg:

例如:

File Car.h

文件汽车.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

文件轮.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:

嗯...这里需要声明 Car ,因为 Wheel 有一个指向 Car 的指针,但是 Car.h 不能包含在这里,因为它会导致编译器错误。如果包含 Car.h,则将尝试包含 Wheel.h,后者将包含 Car.h,而 Car.h 将包含 Wheel.h,这将永远持续下去,因此编译器会引发错误。解决方案是转发声明 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 include Car.h without causing a cycle.

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

回答by Mahesh

The compiler looks for each symbol being used in the current translation unit is previously declared or not in the current unit. It is just a matter of style providing all method signatures at the beginning of a source file while definitions are provided later. The significant use of it is when you use a pointer to a class as member variable of another class.

编译器会查找当前翻译单元中使用的每个符号之前是否在当前单元中声明过。在源文件的开头提供所有方法签名,而在稍后提供定义,这只是风格问题。它的重要用途是当您使用指向一个类的指针作为另一个类的成员变量时。

//foo.h
class bar;    // This is useful
class foo
{
    bar* obj; // Pointer or even a reference.
};

// foo.cpp
#include "bar.h"
#include "foo.h"

So, use forward-declarations in classes when ever possible. If your program just has functions( with ho header files), then providing prototypes at the beginning is just a matter of style. This would be anyhow the case had if the header file was present in a normal program with header that has only functions.

因此,尽可能在类中使用前向声明。如果你的程序只有函数(带有 ho 头文件),那么在开始时提供原型只是风格问题。无论如何,如果头文件存在于带有仅具有功能的头文件的普通程序中,情况就会如此。

回答by sbi

The term "forward declaration" in C++ is mostly only used for class declarations. See (the end of) this answerfor why a "forward declaration" of a class really is just a simple class declarationwith a fancy name.

C++ 中的术语“前向声明”主要仅用于类声明。请参阅(结尾)这个答案,了解为什么类的“前向声明”实际上只是一个带有花哨名称的简单类声明

In other words, the "forward" just adds ballast to the term, as anydeclaration can be seen as being forward in so far as it declares some identifier beforeit is used.

换句话说,“转发”只是在术语中添加了镇流器,因为任何声明都可以被视为转发,只要它使用之前声明了一些标识符。

(As to what is a declarationas opposed to a definition, again see What is the difference between a definition and a declaration?)

(至于什么是声明而不是定义,请再次参见定义和声明之间的区别是什么?

回答by Nick

Because C++ is parsed from the top down, the compiler needs to know about things before they are used. So, when you reference:

因为 C++ 是自上而下解析的,所以编译器需要在使用之前了解它们。所以,当你参考:

int add( int x, int y )

in the main function the compiler needs to know it exists. To prove this try moving it to below the main function and you'll get a compiler error.

在 main 函数中,编译器需要知道它存在。为了证明这一点,尝试将其移动到主函数下方,您将收到编译器错误。

So a 'Forward Declaration' is just what it says on the tin. It's declaring something in advance of its use.

所以“前向声明”就是它在罐头上所说的。它在使用之前声明了一些东西。

Generally you would include forward declarations in a header file and then include that header file in the same way that iostreamis included.

通常,您会在头文件中包含前向声明,然后以包含iostream的相同方式包含该头文件。

回答by René Nyffenegger

When the compiler sees add(3, 4)it needs to know what that means. With the forward declaration you basically tell the compiler that addis a function that takes two ints and returns an int. This is important information for the compiler becaus it needs to put 4 and 5 in the correct representation onto the stack and needs to know what type the thing returned by add is.

当编译器看到add(3, 4)它需要知道这意味着什么。通过前向声明,您基本上告诉编译器这add是一个接受两个整数并返回一个整数的函数。这对编译器来说是重要的信息,因为它需要将 4 和 5 以正确的表示形式放入堆栈,并且需要知道 add 返回的内容是什么类型。

At that time, the compiler is not worried about the actualimplementation of add, ie where it is (or if there iseven one) and if it compiles. That comes into view later, aftercompiling the source files when the linker is invoked.

当时,编译器不担心实际实现的add,即它在哪里(或者如果有甚至一个),如果它编译。稍后,调用链接器时编译源文件之后,就会看到这一点。

回答by Nawaz

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

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

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

It's same as #include"add.h". If you know,preprocessor expands the file which you mention in #include, in the .cpp file where you write the #includedirective. That means, if you write #include"add.h", you get the same thing, it is as if you doing "forward declaration".

它与#include"add.h". 如果您知道,预处理器会#include在您编写#include指令的 .cpp 文件中展开您在, 中提到的文件。这意味着,如果你写#include"add.h",你会得到同样的东西,就好像你在做“前向声明”一样。

I'm assuming that add.hhas this line:

我假设add.h有这条线:

int add(int x, int y); 

回答by Hyman

one quick addendum regarding: usually you put those forward references into a header file belonging to the .c(pp) file where the function/variable etc. is implemented. in your example it would look like this: add.h:

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

extern int add(int a, int b);

the keyword extern states that the function is actually declared in an external file (could also be a library etc.). your main.c would look like this:

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

#include 
#include "add.h"

int main()
{
.
.
.

回答by Dirk

One problem is, that the compiler does not know, which kind of value is delivered by your function; is assumes, that the function returns an intin this case, but this can be as correct as it can be wrong. Another problem is, that the compiler does not know, which kind of arguments your function expects, and cannot warn you, if you are passing values of the wrong kind. There are special "promotion" rules, which apply when passing, say floating point values to an undeclared function (the compiler has to widen them to type double), which is often not, what the function actually expects, leading to hard to find bugs at run-time.

一个问题是,编译器不知道您的函数传递的是哪种值;is 假设,int在这种情况下函数返回 an ,但这可能是正确的,也可能是错误的。另一个问题是,编译器不知道您的函数期望哪种参数,并且如果您传递的值类型错误,则无法警告您。有一些特殊的“提升”规则,在将浮点值传递给未声明的函数时适用(编译器必须将它们加宽以键入 double),这通常不是函数实际期望的,导致难以发现错误在运行时。