标头和不同的cpp文件

时间:2012-10-03 23:21:59

标签: c++

我已经知道包括警卫,但是我想知道一些问题:

示例1

foo.h中

int SumOfNums(int i, int j);

Foo.cpp中

#include "Foo.h"
int SumOfNums(int i, int j){
   return i+j;
}

的main.cpp

#include "Foo.h"
#include "Foo.h"

int main(){
    SumOfNumbs(5,10);
}

这将编译并运行正常。

示例2

foo.h中

int SumOfNums(int i, int j);
int i;

Foo.cpp中

#include "Foo.h"
int SumOfNums(int i, int j){
   return i+j;
}

的main.cpp

#include "Foo.h"

int main(){
    SumOfNumbs(5,10);
}

根据compier重新定义'i'。

示例3

foo.h中

int SumOfNums(int i, int j);
enum FooBar{FOO, BAR};

Foo.cpp中

#include "Foo.h"
int SumOfNums(int i, int j){
   return i+j;
}

的main.cpp

#include "Foo.h"

int main(){
    SumOfNumbs(5,10);
}

这将编译并运行正常。

示例4

foo.h中

int SumOfNums(int i, int j);
enum FooBar{FOO, BAR};

Foo.cpp中

#include "Foo.h"
int SumOfNums(int i, int j){
   return i+j;
}

的main.cpp

#include "Foo.h"
#include "Foo.h"
int main(){
    SumOfNumbs(5,10);
}

根据编译器重新定义FooBar。

总结一下:

示例1 - 当没有包含警卫时,为什么Foo.h可以在main.cpp中包含两次?

示例2 - int变量与函数头有何不同?

示例3 - 当Foo.cpp中有一个FooBar定义而main.cpp中有一个定义时,为什么链接器不会抱怨?

示例4 - 这与示例1 有什么区别?

5 个答案:

答案 0 :(得分:4)

  1. 多次声明功能不是错误。

  2. SumOfNums的函数声明只告诉编译器SumOfNums存在于某处(但不在此处)。 i的定义在全局区域中分配存储并为其命名。您有i的两个定义,每个.cpp文件中有一个定义,因为包含定义的标题不止一次。

  3. 链接器永远不会看到enum FooBar。枚举中的值由编译器用作常量。

  4. 此示例包含enum FooBar的声明,而示例1则不包含。编译器只希望看到给定enum的声明一次。

答案 1 :(得分:4)

  

Ex1 - 当没有包含警卫时,为什么Foo.h可以在main.cpp中包含两次?

因为在这种情况下,只有声明一个函数。你可以拥有任意数量的声明。

  

Ex2 - int变量与函数头有何不同?

这也是定义,因此打破了一个定义规则。

  

Ex3 - 当Foo.cpp中有一个FooBar定义而main.cpp中有一个定义时,为什么链接器不会抱怨?

在翻译单元中定义类型很好。

  

Ex4 - 这与Ex1有什么区别?

您在同一个翻译单元中定义了两次相同的类型 - 不允许。

答案 2 :(得分:3)

  1. 因为函数声明可以重复,但类型定义等不能重复。
  2. 您在每个包含标题的翻译单元中定义变量i,但“一个定义规则”意味着您只能在程序中为变量定义一个。
  3. enum FooBar的定义纯粹是类型信息;标题既不定义也不声明该类型的变量。您可以使用<iostream>等标准标题获得类似的行为。
  4. 这里的区别在于您尝试重复enum FooBar的类型定义(通过包括Foo.h两次)并且不允许类型重定义,这是您应该使用标头保护的主要原因。< / LI>

答案 3 :(得分:2)

一般的答案是你可以多次声明事物,但你只能定义一次(它不完全正确,你只能声明一次很少的东西,例如模板上的默认参数)。当在翻译单元中看到多个定义时,编译器会抱怨,当在不同的翻译单元中看到多个事物定义并且不能在不同的翻译单元中多次定义时,链接器会抱怨。例如,类型,模板和内联函数可以在每个翻译单元中定义一次而没有问题。普通功能只能在一个翻译单元中定义。

在第一个示例中,您只需在标题中声明一个函数。您可以根据需要随时声明函数。但是,您只能定义一次。

您的第二个示例包含变量i的定义。编译此标题的每个翻译单元都将包含i的定义。当链接器尝试构建东西时,它将检测到i有两个定义,它将失败。包含警卫不会阻止此问题,因为它们只能在一个翻译单元内工作。

您的第三个示例只是在标头中声明了一个函数,并定义了enum。类型只能在每个翻译单元中定义一次,但多个翻译单元都可以具有类型的定义。原因很简单:类型不会创建任何代码,也不会为变量分配任何空间。

您的第四个示例包括定义enum两次的标题,即翻译单元看到类型的重新定义并失败。如果您使用了包含警卫,则会发现此问题。

答案 4 :(得分:0)

Ex1 - 可以,因为它可以。即,这是合法的,语言中没有任何内容阻止您多次包含头文件。如果有的话,首先不需要包括警卫。这Foo.h中没有任何内容阻止它被包含两次。它只是一个函数原型,你可以根据需要包含尽可能多的函数原型副本,只要它们都是相同的。

Ex2 - 您将从链接器中获得重复的符号消息,因为您将有两个名为i的全局变量。但这是一个链接器,而不是编译器。如果您实际上遇到编译器错误,我想这可能是因为,在Foo.cpp中,在包含Foo.h之后,您有一个全局变量i。但是,在SumOfNums的正文中,您还使用i作为函数参数。因此,在该函数内部,您无权访问全局i。但是,并非所有的c ++编译器都会抱怨这一点。有些人会给你一个警告,继续他们的快乐编译业务。

Ex3 - 因为FooBar是一种类型,而不是变量。链接器符号仅为全局变量和函数实现生成(意味着具有主体的函数,而不仅仅是原型)。仅通过包含Foo.h就不会生成链接符号,因此没有链接器问题。

Ex4 - 此示例中的Foo.h定义了一个类型,而示例1中的Foo.h则没有。因此,通过在同一main.cpp文件中包含{{1}}两次,您将两次注入相同的类型定义。那是违法的。如果你问我为什么这对于类型定义是非法的,但对于函数原型来说是合法的,我可以告诉你的是语言规范的编写方式。它背后可能有一个基本原理,但我不知道它是什么。