标头和不同的cpp文件

Headers and different cpp files

本文关键字:cpp 文件      更新时间:2023-10-16

我已经知道include guards,但我想解决一些问题:

示例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-在没有include保护的情况下,为什么Foo.h可以在main.cpp中包含两次?

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

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

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

Ex1-为什么Foo.h可以在main.cpp中包含两次,而没有包含保护?

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

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

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

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

跨翻译单元定义类型很好。

Ex4-这和Ex1之间有什么区别?

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

  1. 多次声明函数不是错误。

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

  3. 链接器从未看到enum FooBar。编译器将枚举中的值用作常量。

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

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

一般的答案是,您可以多次声明内容,但只能定义一次(这并不完全正确,很少有内容可以只声明一次,例如,模板上的默认参数)。当在一个翻译单元中看到多个定义时,编译器会抱怨;当在不同的翻译单元中出现多个事物定义,并且不能在不同的转换单元中多次定义时,链接器会抱怨。例如,类型、模板和内联函数可以在每个翻译单元中定义一次,而不会出现问题。正常函数只能在一个翻译单元中定义。

在第一个示例中,您只需在头中声明一个函数。您可以根据需要随时声明函数。不过,您只能定义它们一次。

您的第二个示例包括变量i的定义。编译该报头的每个翻译单元将包括i的定义。当链接器尝试构建东西时,它将检测到i有两个定义,并且它将失败。包括警卫不会防止这个问题,因为你只能在一个翻译单位内工作。

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

您的第四个示例包括两次定义enum的头,即翻译单元看到类型的重新定义并失败。如果你使用了include防护,这个问题就会被发现。

Ex1-它可以,因为它可以。也就是说,这是合法的,语言中没有任何内容可以阻止您多次包含头文件。如果有的话,一开始就不需要包括警卫。这个Foo.h中没有任何东西可以阻止它被包含两次。它所拥有的只是一个函数原型,您可以包含任意数量的函数原型副本,只要它们都相同。

Ex2-您将从链接器获得一条重复的符号消息,因为您将有两个名为i的全局变量。但这是链接器的事情,而不是编译器的事情。如果你真的遇到了编译器错误,我想这可能是因为在Foo.cpp中,在包含Foo.h之后,你有一个全局变量i。但是,在SumOfNums的主体中,您也使用i作为函数参数。因此,在该函数中,您无法访问全局i。不过,并不是所有的c++编译器都会抱怨这一点。有些人只会给你一个警告,然后继续他们愉快的编辑工作。

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

Ex4-本例中的Foo.h定义了一个类型,而例1中的CCD29则没有。因此,在同一个main.cpp文件中包含两次Foo.h,就是两次注入同一类型定义。这是违法的。如果你问我为什么这对类型定义是非法的,但对函数原型是合法的,我只能告诉你语言规范就是这样写的。这背后可能有一个理由,但我不知道它是什么。