Sim*_*ity 202 c++ declaration forward-declaration
在:http://www.learncpp.com/cpp-tutorial/19-header-files/
提到以下内容:
add.cpp:
int add(int x, int y)
{
return x + y;
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
我们使用了前向声明,以便编译器在编译时知道"
add"是什么main.cpp.如前所述,为要在其他文件中使用的每个函数编写前向声明可能会很快变得乏味.
你能进一步解释" 前瞻性宣言 "吗?如果我们在main()函数中使用它会有什么问题?
Sco*_*ham 364
为什么在C++中需要前向声明
编译器希望确保您没有拼写错误或将错误数量的参数传递给函数.因此,它坚持在使用之前首先看到"添加"(或任何其他类型,类或函数)的声明.
这实际上只是允许编译器更好地验证代码,并允许它整理松散的末端,以便它可以生成一个整洁的目标文件.如果您不必转发声明内容,编译器将生成一个目标文件,该文件必须包含有关函数"add"可能的所有可能猜测的信息.链接器必须包含非常聪明的逻辑来尝试找出你实际打算调用的"添加",当'add'函数可能存在于不同的目标文件中时,链接器与使用add生成的链接器连接一个DLL或exe.链接器可能会得到错误的添加.假设你想使用int add(int a,float b),但是不小心忘了写它,但是链接器发现了一个已经存在的int add(int a,int b)并且认为它是正确的并且使用了它.您的代码将编译,但不会按预期执行.
因此,只是为了保持显式并避免猜测等,编译器坚持要在使用之前声明所有内容.
声明和定义之间的区别
另外,了解声明和定义之间的区别非常重要.声明只提供足够的代码来显示某些内容,因此对于函数,这是返回类型,调用约定,方法名称,参数及其类型.但是不需要该方法的代码.对于定义,您还需要声明,然后还需要函数的代码.
前向声明如何显着减少构建时间
您可以通过#include包含已包含函数声明的标头,将函数声明输入到当前的.cpp或.h文件中.但是,这会降低编译速度,特别是如果你将#include包含在.h而不是程序的.cpp中,因为#includes你正在编写的.h的所有内容都会结束#include所有的标题你也写了#includes.突然,编译器有#included页面和代码页面,即使你只想使用一个或两个函数,它也需要编译.为了避免这种情况,您可以使用前向声明,只需在文件顶部键入函数声明即可.如果你只使用一些函数,那么与总是#include标题相比,这可以真正使你的编译更快.对于非常大的项目,差异可能是一小时或更长的编译时间,只需几分钟.
中断两个定义互为使用的循环引用
此外,前向声明可以帮助您打破周期.这是两个函数都试图互相使用的地方.当发生这种情况时(并且这是完全有效的事情),您可以#include一个头文件,但该头文件尝试#include您当前正在编写的头文件....然后#includes其他头,#包括你正在写的那个.你陷入鸡蛋和鸡蛋的境地,每个头文件试图重新#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;
};
Run Code Online (Sandbox Code Playgroud)
File Wheel.h
嗯......这里需要Car的声明,因为Wheel有一个指向Car的指针,但Car.h不能包含在这里,因为它会导致编译器错误.如果包含Car.h,那么将尝试包括Wheel.h,其中包括Car.h,其中包括Wheel.h,这将永远持续下去,因此编译器会引发错误.解决方案是转发声明Car:
class Car; // forward declaration
class Wheel
{
Car* car;
};
Run Code Online (Sandbox Code Playgroud)
如果类Wheel有需要调用car方法的方法,那些方法可以在Wheel.cpp中定义,而Wheel.cpp现在能够包含Car.h而不会引起循环.
Mah*_*esh 25
编译器查找先前在当前单元中声明的当前转换单元中使用的每个符号.只是在源文件开头提供所有方法签名的样式问题,而后面提供定义.它的重要用途是当您使用指向类的指针作为另一个类的成员变量时.
//foo.h
class bar; // This is useful
class foo
{
bar* obj; // Pointer or even a reference.
};
// foo.cpp
#include "bar.h"
#include "foo.h"
Run Code Online (Sandbox Code Playgroud)
因此,尽可能在类中使用forward-declarations.如果你的程序只有函数(带有ho头文件),那么在开头提供原型只是风格问题.无论如何,如果头文件存在于具有仅具有函数的头的普通程序中,则无论如何都是如此.
Nic*_*ick 12
因为C++是从上到下解析的,所以编译器需要在使用之前了解它们.所以,当你参考:
int add( int x, int y )
Run Code Online (Sandbox Code Playgroud)
在main函数中,编译器需要知道它存在.要证明这一点,请尝试将其移至主函数下方,否则会出现编译错误.
因此," 前瞻宣言 "正如它所说的那样.它在使用之前声明了一些东西.
通常,您将在头文件中包含前向声明,然后以与包含iostream相同的方式包含该头文件.
sbi*_*sbi 11
C++中的术语" 前向声明 "主要仅用于类声明.请参阅(结束)这个答案为什么类的"前向声明"实际上只是一个带有花哨名称的简单类声明.
换句话说,"前进"只是为该术语添加了镇流器,因为任何声明都可以看作是前进的,只要它在使用之前声明了一些标识符.
(关于什么是声明而不是定义,再看看定义和声明之间有什么区别?)