谈谈C++之多文件编译

前言

C++采用了多文件编译,在逻辑上,整个程序包含所有文件,但编译是针对单个文件生成目标文件,多文件是在链接时才进行整合,所以在编译过程中,我们需要指明当前文件所需要的其他文件的信息。C++编译是区分.h.cpp文件的,cpp会生成目标文件,而.h不会生成目标文件。
一般而言,我们利用 .h作为头文件来传递文件间的信息, .cpp文件用于具体程序实现,在此总结下各个关系。

函数

函数默认是全局的,也就是说,在一个文件内定义了一个函数,另一个文件是可以引用的,但这个文件如何知道怎么调用这个函数,是通过声明来进行传递。头文件内应包含函数的声明,而不应该有定义,因为,include的本质是直接替换,当头文件被include到多个cpp中时,工程中将出现多个同名函数定义,连接时,自然就会因重复定义而失败。
如果函数定义为static,则即便在另一文件中声明,也无法调用,因为static限制了使用范围为当前文件。

变量

全局变量默认也是整个工程的,即全局的,但变量的声明和定义在格式上有些冲突,故若只是声明,应为
extern T a;
定义则可省去extern
当然若有static,则该变量只在单文件中可见。

类与函数和变量,就有点不一样了,因为类的定义和声明,有点模棱两可,类的声明其实就包含其数据成员和函数成员,函数成员的实现可以在声明中实现,也是可以分离出来在cpp中实现,但数据成员的初始化,尽量分离出来,编译器版本不一样,效果有偏差。

再看一下类的作用范围,应该还是全局的,这里存在一些问题:
如果把成员方法实现写在类的声明里面,当不同文件的多次定义同名类,且存在不同的函数实现方式时,编译器会选择编译时,排在前面的文件的实现方式,也就是说,不同的文件,能够定义同名类,同名方法,但在编译后程序其实只存在一个该类,所以这种方式算是一个标准未定义的选项,慎用,最好还是方法实现分离。

上面的实验:
a.cpp 定义类A,方法b()
b.cpp 同样定义类 A,方法b()
二者,有不同的b()的实现,结果有一样的成员函数地址,均为a.cppb(),交换g++的编译顺序,返现可变为b.cpp中的b()

总结一下,若在类的声明文件中实现了函数体,程序只会只使用编译时遇到的第一个文件的该类方法的实现,所以,定义类时若采用在声明中实现方法,则方法的实现应该只存在于一个文件中。当然若方法体分离为两个文件,此时,编译器会检测重复定义,此时必然会保持一致。

关于宏,记住宏的作用域除了某些自身为块作用域,其余作用域均为文件内,所以宏的最广作用域是文件内。

总结

C++采用了头文件和实现文件的方式来进行文件间的引用,但在编译阶段时,采用的是单文件编译,所以当前文件需要引用其余文件中的信息,站在编译器的角度,当然需要预先声明要用的信息,所以按照这个思路,可以很自然的明白各种声明,定义的原理。

本文链接:https://rainlin.top/archives/107
转载请注明转载自:https://rainlin.top
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇