简体   繁体   English

在C ++中包含双头?

[英]Double header inclusion in C++?

I have what seems a relatively simple question, but one that keeps defying my efforts to understand it. 我有一个看起来相对简单的问题,但却一直在努力去理解它。

I apologise if it is a simple question, but like many simple questions, I can't seem to find a solid explanation anywhere. 如果这是一个简单的问题我很抱歉,但就像许多简单的问题一样,我似乎无法在任何地方找到可靠的解释。

With the below code: 使用以下代码:

/*foo.c*/

#include "bar.h"

int main() {
   return(my_function(1,2));
}

/*bar.h*/

int my_function(int,int);

/*bar.c*/

#include "bar.h" /*is this necessary!?*/

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

Simply, is the second inclusion necessary? 简单来说,第二个包含是否必要? I don't understand why I keep seeing headers included in both source files. 我不明白为什么我一直看到两个源文件中都包含标题。 Surely if the function is declared in "foo.c" by including "bar.h," it does not need to be declared a second time in another linked source file (especially the one which actually defines it)??? 当然,如果通过包含“bar.h”在“foo.c”中声明该函数,则不需要在另一个链接的源文件中第二次声明它(特别是实际定义它的那个)? A friend tried to explain to me that it didn't really matter for functions, but it did for structs, something which still eludes me! 一位朋友试图向我解释它对于功能并不重要,但它确实对结构有用,这仍然是我的事! Help! 救命!

Is it simply for clarity, so that programmers can see which functions are being used externally? 它只是为了清晰起见,以便程序员可以看到外部使用哪些功能?

I just don't get it! 我只是不明白!

Thanks! 谢谢!

In this particular case, it's unnecessary for the reason you described. 在这种特殊情况下,您所描述的原因是不必要的。 It might be useful in situations where you have a more complex set of functions that might all depend on each other. 在您拥有可能完全相互依赖的更复杂功能集的情况下,它可能很有用。 If you include the header at the top of the .cpp file, you have effectively forward-declared every single function and so you don't have to worry about making sure your function definitions are in a certain order. 如果在.cpp文件的顶部包含标题,则可以有效地向前声明每个函数,因此您不必担心确保函数定义按特定顺序排列。

I also find that it clearly shows that these function definitions correspond to those declarations. 我还发现它清楚地表明这些函数定义对应于那些声明。 This makes it easier for the reader to find how translation units depend on each other. 这使读者更容易找到翻译单元如何相互依赖。 Of course, the names of the files might be sufficient, but some more complex projects do not have one-to-one relationship between .cpp files and .h files. 当然,文件的名称可能就足够了,但是一些更复杂的项目在.cpp文件和.h文件之间没有一对一的关系。 Sometimes headers are broken up into multiple parts, or many implementation files will have their external functions declared in a single header (common for large modules). 有时标题被分解为多个部分,或者许多实现文件将在单个标题中声明其外部函数(对于大型模块是常见的)。

Really, all inclusions are unnecessary. 真的,所有夹杂物都是不必要的。 You can always, after all, just duplicate the declarations (or definitions, in the case of classes) across all of the files that require them. 毕竟,您可以在所有需要它们的文件中复制声明(或类的情况下的定义)。 We use the preprocessor to simplify this task and reduce the amount of redundant code. 我们使用预处理器来简化此任务并减少冗余代码的数量。 It's easier to stick to a pattern of always including the corresponding header because it will always work, rather than have to check each file every time you edit them and determine if the inclusion is necessary or not. 更容易坚持总是包含相应标题的模式,因为它始终有效,而不是每次编辑它们时都必须检查每个文件,并确定是否需要包含。

The way the C language (and C++) is designed is that the compiler processes each .c file in isolation. C语言(和C ++)的设计方式是编译器单独处理每个.c文件。

You typically launch your compiler (cl.exe or gcc, for example) for one of your c files, and this produces one object file (.o or .obj). 您通常会为其中一个c文件启动编译器(例如cl.exe或gcc),这会生成一个目标文件(.o或.obj)。

Once all your object files have been generated, you run the linker, passing it all the object files, and it will tie them together into an executable. 生成所有目标文件后,运行链接器,将所有目标文件传递给它,并将它们绑定到一个可执行文件中。

That's why every .c file needs to include the headers it depends on. 这就是为什么每个.c文件都需要包含它依赖的头文件。 When the compiler is processing it, it knows nothing about which other .c files you may have. 当编译器正在处理它时,它对您可能拥有的其他 .c文件一无所知。 All it knows is the contents of the .c file you point it to, as well as the headers it includes. 它只知道你指向它的.c文件的内容,以及它包含的头文件。

Simple rule is this(Considering foo is a member function of some class):- 简单的规则就是这个(考虑到foo是某个类的成员函数): -

So, if some header file is declaring a function say:= 所以,如果某个头文件声明一个函数说:=

//foo.h
void foo (int x);

Compiler would need to see this declaration anywhere you have defined this function ( to make sure your definition is in line with declaration) and you are calling this function ( to make sure you have called the function with correct number and type of arguments). 编译器需要在您定义此函数的任何地方看到此声明(以确保您的定义符合声明)并且您正在调用此函数(以确保您使用正确的参数数量和类型调用该函数)。

That means you have to include foo.h everywhere you are making call to that function and where you are providing definition for that function. 这意味着你必须在调用该函数的任何地方包含foo.h ,并在那里为该函数提供定义。

Also if foo is a global function ( not inside any namespace ) then there is no need to include that foo.h in implementation file. 此外,如果foo是一个全局函数(不在任何命名空间内),则不需要在实现文件中包含该foo.h

In your simplified example inclusion of "bar.h" in "bar.c" is not necessary. 在您的简化示例中,不需要在“bar.c”中包含“bar.h”。 But in real world in most cases it would be. 但在现实世界中,大多数情况下都是如此。 If you have a class declaration in "bar.h", and "bar.c" has functions of this class, the inclusion is needed. 如果在“bar.h”中有一个类声明,而“bar.c”具有该类的函数,则需要包含它。 If you have any other declaration which is used in "bar.c" - being it a constant, enum, etc. - again include is needed. 如果您在“bar.c”中使用了任何其他声明 - 它是常量,枚举等 - 需要再次包含。 Because in real world it is nearly always needed, the easy rule is - include the header file in the corresponding source file always. 因为在现实世界中几乎总是需要它,所以简单的规则是 - 始终在相应的源文件中包含头文件。

If the header only declares global functions, and the source file only implements them (without calling any of them) then it's not strictly necessary. 如果头只声明全局函数,并且源文件只实现它们(不调用它们中的任何一个)那么它并不是绝对必要的。 But that's not usually the case; 但通常情况并非如此; in a large program, you rarely want global functions. 在大型程序中,您很少需要全局功能。

If the header defines a class, then you'll need to include it in the source file in order to define member functions: 如果标头定义了一个类,那么您需要将它包含在源文件中以定义成员函数:

void Thing::function(int x) {
   //^^^^^^^ needs class definition
}

If the header declares functions in a namespace, then it's a good idea to put the definitions outside the namespace: 如果头部在命名空间中声明函数,那么将定义放在命名空间之外是个好主意:

void ns::function(int x) {
   //^^^^ needs previous declaration
}

This will give a nice compile-time error if the parameter types don't match a previous declaration - for which you'd need to include the header. 如果参数类型与之前的声明不匹配,那么这将产生一个很好的编译时错误 - 您需要为其包含标题。 Defining the function inside its namespace 在其命名空间内定义函数

namespace ns {
    void function(int x) {
        // ...
    }
}

will silently declare a new overload if you get the parameter types wrong. 如果参数类型错误,将默默地声明一个新的重载。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM