C&C++
发布时间:2022-04-03 发布网站:大佬教程 code.js-code.com
大佬教程收集整理的这篇文章主要介绍了C++如何进行多文件编程?(汇总版),大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
在 C++ 多
文件编程中,
一个完整的 C++ 项目可以包含 2 类
文件,即 .h
文件和 .cpp
文件。通常情况下,.h
文件称为 C++ 头
文件,.cpp
文件称为 C++ 源
文件。
通过 《
用g++命令执行C++多文件项目》一节的学习我们知道,同属
一个 C++
项目中的所有
代码文件是分别进行编译的,只需要在编译成目标
文件后再与其它目标
文件做一次
链接即可。例如,在 a.cpp 源
文件中定义有
一个全局
函数 a(),而在
文件 b.cpp 中需要
调用这个
函数。即
便如此,处于编译阶段的 a.cpp 和 b.cpp 并不需要知道对方的存在,它们各自是独立编译的是,只要最后将编译得到的目标
文件进行
链接,整个程序就可以运行。
那么,整个过程是
如何实现的呢?从写程序的角度来理解,当
文件 b.cpp 中需要
调用 a()
函数时,只需要先声明一下该
函数即可。这
是因为,编译器在编译 b.cpp 时会
生成一个符号表,类似 a() 这样看不到定义的符号就会被存放
在这个表中。在
链接阶段,编译器就会在别的目标
文件中去寻找这个符号的定义,一旦找到了,程序也就可以 顺利地
生成了(反之则出现
链接错误)。
注意,这里提到了两个概念,即“声明”和“定义”。所谓定义,指的是就是将某个符号完整的描述清楚,它是变量还是
函数,变量类型以及变量值是多少,
函数的参数有哪些以及返回值是什么等等;而“声明”的作用仅是告诉编译器该符号的存在,至于该符号的具体的含义,只有等
链接的时候才能知道。
基于声明和定义的不同,才有了 C++ 多
文件编程的出现。试想如果有
一个很常用的
函数 f(),其会被程序中的很多 .cpp
文件调用,那么我们只需要在
一个文件中定义此
函数,然后在需要
调用的这些
文件中声明这个
函数就可以了。
那么问题来了,
一个函数还好对付,声明起来也就一句话,如果有好几百个
函数(比如是一大堆的数学
函数),该
怎么办呢?一种简单的
方法就是将它们的声明全部放在
一个文件中,当需要时直接从
文件中拷贝。这种方式固然可行,但还是太麻烦,而且还显得很笨拙,于是头
文件便可以发挥它的作用了。
所谓的头
文件,其实它的
内容跟 .cpp
文件中的
内容是一样的,都是 C++ 的源
代码,唯一的区别在于头
文件不用被编译。我们把所有的
函数声明全部放进
一个头
文件中,当某
一个 .cpp 源
文件需要时,可以通过 #include 宏命令直接将头
文件中的所有
内容引入到 .cpp
文件中。这样,当 .cpp
文件被编译之前(也就是预处理阶段),使用 #include 引入的 .h
文件就会替换成该
文件中的所有声明。
以《
用g++命令执行C++多文件项目》一节中的 C++ 项目为例,拥有
student.h、
student.cpp 和 main.cpp 这 3 个
文件,其中
student.cpp 和 main.cpp
文件中用 #include 引入了
student.h
文件。在此基础上,
文章中用 g++ 命令分别对
student.cpp 和 main.cpp 进行了预处理操作,
并分别
生成了
student.i 和 main.i
文件。
如下展示了 main.i
文件中的
内容:
class student {
public:
const char *name;
int age;
float score;
void say();
};
int main() {
student *pstu = new student;
pstu->name = "小明";
pstu->age = 15;
pstu->score = 92.5f;
pstu->say();
delete pstu;
return 0;
}
显然和之前的 main.cpp
文件相比,抹去了用 #include 引入
student.h
文件,而是将
student.h
文件中所有的
内容都拷贝了过来。
C++头文件内应该写什么
通过上面的讲解读者应该知道,.h 头
文件的作用就是被其它的 .cpp 包含进去,其本身并不参与编译,但实际上它们的
内容会在多个 .cpp
文件中得到编译。
通过“符号的定义只能有一次”的规则,我们可以很容易地得出,头
文件中应该只放变量和
函数的声明,而不能放它们的定义。因为
一个头
文件的
内容实际上是会被引入到多个不同的 .cpp
文件中的,
并且它们都会被编译。换句话说,如果在头
文件中放了定义,就等同于在多个 .cpp
文件中出现对同
一个符号(变量或
函数)的定义,纵然这些定义的
内容相同,编译器也不认可这种做法(报“重定义”
错误)。
所以读者一定要谨记,.h 头
文件中只能存放变量或者
函数的声明,而不要放定义。例如:
extern int a;
void f();
这些都是声明。反之:
int a;
void f() {}
这些都是定义,如果存放在 .h
文件中,一旦该
文件被 2 个以上的 .cpp
文件引入,编译器就会立马报错。
凡事都有例外,以上 3 种情况也属于定义的范畴,但它们应该放在 .h
文件中:
1) 头文件中可以定义 const 对象
要知道,全局的 const 对象
默认是没有 extern 声明的,所以它只在当前
文件中有效。把这样的对象写进头
文件中,即使它被包含到其他多个 .cpp
文件中,
这个对象也都只在包含它的那个
文件中有效,对其他
文件来说是
不可见的,所以
便不会导致多重定义。
与此同时,由于这些 .cpp
文件中的 const 对象都是从
一个头
文件中包含进去的,也就保证了这些 .cpp
文件中的 const 对象的值是相同的,可谓一举两得。
2) 头文件中可以定义内联函数
内联
函数(用 inline 修饰的
函数)
是需要编译器在编译阶段根据其定义将它内联展开的(类似宏展开),而并非像普通
函数那样先声明再
链接。这就意味着,编译器必须在编译时就找到内联
函数的完整定义。
显然,把内联
函数的定义放进
一个头
文件中是非常明智的做法。
3) 头文件中可以定义类
因为
在程序中创建
一个类的对象时,编译器只有
在这个类的定义
完全可见的情况下,才能知道这个类的对象应该如何布局,所以,关于类的定义的要求,跟内联
函数是基本一样的,即把类的定义放进头
文件,在使用到这个类的.cpp
文件中去包含这个头
文件。
值得一提的是,类的内部通常包含成员变量和成员
函数,成员变量是要等到具体的对象被创建时才会被定义(分配空间),但成员
函数却
是需要在一开始就被定义的,这也就是类的实现。通常的做法是将类的定义放在头
文件中,而把成员
函数的实现
代码放在
一个 .cpp
文件中。
还有另一种办法,就是直接成员
函数的实现
代码写到
类定义的内部。在 C++ 的类中,如果成员
函数直接定义在类体的内部,则编译器会将其视为内联
函数。所以把
函数成员的定义写进类体内,一起放进头
文件中,也是合法的。
注意,如果把成员
函数的定义写在定义类的头
文件中,而没有写进类内部,这是不合法的。这种情况下,此成员
函数不是内联
函数,一旦头
文件被两个或两个以上的 .cpp
文件包含,就可能
会出现重定义的
错误。
有效避免头文件被重复引入
在 C++ 多
文件编程中,如果 .h 头
文件中只包含声明语句的话,即
便被同
一个 .cpp
文件引入多次也
没有问题,因为声明语句是可以重复的,且重复
次数不受限制。然而,刚刚讨论到的 3 种特殊情况也是头
文件很常用的
一个用处。如果
一个头
文件中出现了上面 3 种情况中的任何一种,且被同
一个 .cpp
文件引入多次,就
会发生重定义
错误。
在 C++ 多
文件编程中,为了有效避免“因多次引入头
文件发生重定义”的问题,C++ 提供了 3 种处理机制,
其中最常用的一种方式就是借助条件编译 #ifndef/#define/#endif,初学者一定要学会至少一种方式。
大佬总结
以上是大佬教程为你收集整理的C++如何进行多文件编程?(汇总版)全部内容,希望文章能够帮你解决C++如何进行多文件编程?(汇总版)所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。