(我的道歉,如果这个问题听起来很熟悉,但我真的很困惑。我无法在现有问题下发表评论。)
我浏览了几个问题,this,答案是:
如果...(在标题中定义了一个函数),然后将标题包含在两个或多个不同的源文件中,则您将拥有相同函数的多个定义。
但我认为这本书教会我们总是在标题内写入定义守护。有了警卫,我们不会有多个定义,对吧?
我试图在书中找到原因,但没有多大帮助:它说的原因(为什么函数在头文件中声明,并在源文件中定义)与变量的原因相同(在前一章中)。当我跳到上一章时,没有明确的解释。
答案 0 :(得分:2)
为什么在头文件中声明一个函数但在源文件中定义它?
调用该函数需要声明。它位于标题中,因此任何想要调用该函数的文件都可以包含标题并具有可用的声明。
只能有一个定义,因此只有一个源文件。
有了警卫,我们不会有多个定义,对吧?
每个源文件中都有一个包含标题的定义。防护措施可防止来自同一源文件的多个包含,但不包含来自不同源文件的包含。
答案 1 :(得分:2)
但我认为这本书教会我们总是在标题内写入定义守护。
定义保护可防止在一个 C文件中多次包含标头。它不会阻止标头包含在多个C文件中。
有了警卫,我们不会有多个定义,对吗?
如果将定义放在标题中,并将该标题包含在多个C文件中,那么最终会得到同一函数的多个定义。
具有函数或变量的多个定义将在链接时导致错误,除非函数或变量是static
。如果是静态函数/变量,您最终会得到该函数/变量的多个副本,这通常不是理想的结果。
答案 2 :(得分:1)
有了警卫,我们不会有多个定义,对吧?
不对。包含警卫确保在编译源文件时,标题不会被处理两次。但是当你有几个源文件时,每个文件都是单独编译的,所以包含守卫不会停止多个定义。
答案 3 :(得分:1)
这不是一回事,使用定义保护并不会阻止一个实现跨越多个源文件,它会阻止同一个声明在同一个文件中被多次包含(这会确实导致编译错误。)
因此,对于直接在头文件中定义的函数,防护是无用的,因为它的实现将包含在多个源文件中,除非编译器选择内联它,否则它将不止一次出现。将实现放置在源文件中将使函数在其自己的转换单元中进行编译,并且将相应地解析对它的任何调用。
实际上,编译器可以内联甚至在源文件中实现的函数,因此不会发生这种情况。
答案 4 :(得分:1)
声明在标题中。 定义在身体中。 标头防护是在编译时防止多个声明,同时编译可能包含多个相同标头的单个单元(通过其他定义进入主体以防止包含它的多个对象中的定义的多个编译版本在链接时。
编辑以回答评论,因为我无法格式化评论:
不确定两次同样的事情。
不确定两次声明相同的事情。
可以预先声明两次相同的事情。
// predeclare:
class thingy;
// declare:
class thingy { int x(); };
// define:
thingy::x() { return 1; }
您在标题中放置的任何内容在编译单个文件时可能会出现两次,因为标题通常会包含在其他标题中,因此它们会被多次包含。标题保护在编译时阻止这种情况。
如果它们都包含标题,那么在定义标题的标题中放置的任何内容都可能最终在多个文件的编译中定义,然后在链接时出现两次。标题保护无法阻止这种情况,因此我们避免在标题中定义内容。
您可以将C ++中的#include视为一个巨大的宏 - 它意味着“在编译之前抓取整个文件并将其推送到我的源代码中”。