我知道人们建议在头文件中包含标题保护,以防止头文件内容被预处理器多次插入源代码文件。
但请考虑以下情况:
假设我有main.cpp
,stuff.cpp
和commonheader.h
文件,.h
文件有标题保护。
如果.cpp
个文件试图多次包含commonheader.h
,那么预处理器
将阻止这种情况发生,并在编译到目标代码后,我们得到,
main.o
包含commonheader.h 的内容。
stuff.o
包含commonheader.h 的内容。
请注意,commonheader的内容已在文件中重复,但不在同一.o
文件中。
那么在链接步骤中会发生什么?由于.o文件被融合成一个exectuable 我们必须确保第二次以确保不再重复使用commonheader的内容。编译器会处理这个吗?如果没有,当我们处理大量头文件时,这不会成为问题,导致跨文件重复代码并导致大的可执行文件大小。
如果我在问题的任何地方犯了一些概念错误,请纠正我。
答案 0 :(得分:2)
通常,您的标头文件实际上不应定义任何符号,它应该声明它们。所以commonheader.h看起来像这样(省略包含警卫):
void commonFunc1(void);
void commonFunc2(void);
在这种情况下,没有问题。如果您在commonFunc1
和main.cpp
中致电stuff.cpp
,则main.o
和stuff.o
都会知道他们想要与名为commonFunc1
的符号进行关联链接器将尝试找到该符号。如果链接器未找到该符号,则会出现未定义的引用错误。 commonFunc1
的实际定义需要在某个cpp文件中。
如果您确实要在头文件中定义函数,请使用static
,以便链接器不会看到它们。所以你的commonheader.h看起来像:
static void commonFunc1()
{
/* ... do stuff ... */
}
在这种情况下,链接器不知道commonFunc1
并且不会发生错误。这可能会增加可执行文件的大小;你最终可能会得到commonFunc1
的两个代码副本。
答案 1 :(得分:1)
扩展Grayson的答案以涵盖变量。如果要在头文件中声明变量,则应使用extern关键字。这是处理全局变量的一种方法。
在头文件global.h中你写这个:
extern Globals globals;
然后你可以在任何文件中使用foo,包括global.h,而在global.cpp中你可以写
#include "globalstype.h"
Globals globals;
请注意,global.cpp不需要包含global.h,但是您需要确保将global.cpp编译到每个用法中,否则链接器会抱怨。
答案 2 :(得分:0)
头文件通常包含声明性代码而非权威代码。那就是他们宣称存在必须存在一次的东西。允许使用宏和内联函数,无论在何处使用它们都必须重复。
编译器使用声明将未解析的链接(或引用)插入到目标代码中。链接器的工作是通过将引用与单个定义匹配来解析这些链接。
如果省略包含警戒,并且在单个翻译单元中包含多个,则会出现多个声明的编译器错误现有的符号。但是,如果您有一个错误地包含定义的标头,并且标头包含在多个转换单元中,则会有多个具有定义的目标文件 - 这会导致多个链接器错误 定义
所以同时:
extern int b ; // declaration, may occur in multiple translation units
是头文件中的文件,
int b ; // definition, must occur in only object file.
不是。
并非声明未包含在目标代码中,而是编译器使用它们来创建链接器将解析的引用,如果编译器尚未使用该定义并已解决它。
答案 3 :(得分:0)
是的,这可能是个问题。您最终可能会有多个定义或冗余副本。
在这方面,C非常简单。你有静态,外部和内联 - 编译器也定义了几种改变可见性的方法。我认为很多其他答案已经涵盖了这一点。 然而,C ++是完全不同的。有很多信息,也有隐含的定义(例如,编译器可能会发出复制构造函数或RTTI)。
使用C ++,定义出现在标题中的可能性更大 - 考虑模板,类声明中定义的方法等。 C ++默认使用One Definition Rule。您将需要更详细地阅读它,但它基本上表明某些类别的符号可能是多重定义的;根据装饰和声明的位置/范围,在许多情况下,允许链接器假定每个主体(定义)是相同的,并且可以自由地丢弃它遇到的任何副本(在二进制文件中保留一个定义)。所以这确实减少了生成的二进制文件的 size ,除非你指定一个副本应该被生成。
但是,在标题中包含这些定义肯定会增加编译每个文件所需的编译时间,内存和文件,可见的依赖关系,并且会增加编辑定义时必须重新编译的文件数。
当然,该语言仍然允许不良形式,并且如果您反复重复说明并且包含多个翻译定义,并且每个翻译必须必须被复制,则不会抱怨。那么你肯定会有很多膨胀。
这可能是一个很好的介绍: http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=386