1.单独编译
和C语言一样,C++允许甚至鼓励将组件函数放在独立的文件中,可以单独编译这些文件,然后把它们链接成可执行的程序。如果只修改了一个文件,则可以只重新编译该文件,然后与其他文件的编译版本进行链接,这会使得大程序的管理更加方便。
例如我们要将如下程序分解,将支持函数放在一个独立的文件中,我们需要怎么做呢?
#include<iostream>
using namespace std;
struct fruit
{
int apple;
int banana;
};
fruit fruitSum (fruit f1, fruit f2);
void showFruit (fruit f);
int main()
{
fruit f1 = {
15, 20};
fruit f2 = {
20, 30};
showFruit(f1);
showFruit(f2);
showFruit(fruitSum(f1, f2));
}
fruit fruitSum (fruit f1, fruit f2)
{
fruit fsum;
fsum.apple = f1.apple + f2.apple;
fsum.banana = f1.banana + f2.banana;
return fsum;
}
void showFruit (fruit f)
{
cout << f.apple << endl;
cout << f.banana << endl;
}
该程序是一个将结构内容相加,并显示结果,算上main()一共有三个函数,不能简单地以main()函数为界,简单的将原来的文件分为两个,因为main()和其他两个函数都使用了同一个结构声明,因此两个文件都应该包括结构声明,简单地将他们输入进去无疑会增加麻烦,因为需要修改结构的时候,需要对两个文件中的结构都进行修改,这简直比每次使用new的时候都使用delete都让人困扰。所以C++的开发人员提供了#include的方式。我们将函数原型,结构声明放入到头文件中,然后在每个文件中引入头文件的内容即可,所以原来的程序被分为了三个部分。
- 头文件:包含结构声明和使用这些结构的函数的原型
- 源代码文件:包含与结构有关函数的代码
- 源代码文件:包含调用与结构相关的函数的代码
注意:不要将函数定义或变量声明放到头文件中,这回导致一个程序中有两个相同变量或函数的定义,除非是内联函数,否则这种行为会报错。
- 头文件中可以包含的内容
1.函数原型
2.使用#define或const定义的符号常量
3.结构声明
4.类声明
5.模板声明
6.内敛函数
注意:#include语句中" "以及 < >之间的区别。
- #include"iostream" 文件名包含在双引号中,则编译器将首先查找当前的工作目录或源代码目录,如果在那里没有找到头文件,则在标准位置查找。所以包含自己的头文件时,应该用" "号。
- #include<iostream>文件名包含在尖括号中,则编译器将在存储标准头文件的主机系统的文件系统中查找,所以引用标准库的头文件时,应该使用<>号。
注意:在IDE中,不要把头文件加入到项目列表中,也不要在源代码文件中使用#include来包含其他源代码文件。
下面展示将上述代码分割为三个文件的文件内容:
//文件名:coordin.h
//头文件
#ifndef COORDIN_H_
#define COORDIN_H_
struct fruit
{
int apple;
int banana;
};
fruit fruitSum (fruit f1, fruit f2);
void showFruit (fruit f);
#endif
//文件1 file1.cpp
#include<iostream>
#include"coordin.h"
using namespace std;
int main()
{
fruit f1 = {
15, 20};
fruit f2 = {
20, 30};
showFruit(f1);
showFruit(f2);
showFruit(fruitSum(f1, f2));
return 0;
}
//文件2 file2.cpp
#include<iostream>
#include<cmath>
#include"coordin.h"
fruit fruitSum (fruit f1, fruit f2)
{
fruit fsum;
fsum.apple = f1.apple + f2.apple;
fsum.banana = f1.banana + f2.banana;
return fsum;
}
void showFruit (fruit f)
{
cout << f.apple << endl;
cout << f.banana << endl;
}
- 在linux中编译两个源代码文件命令:g++ file1.cpp file2.cpp -o fruit
输入命令之后,编译器会将两个文件进行编译,首先将#include的内容转化成对应的文本内容,然后生成一个新的cpp文件来存放他们,之后编译器创建每一个源代码文件的目标代码文件,这种文件格式可以放入整个的大程序之中,生成这种目标代码文件之后,链接程序将目标代码文件,库代码和启动代码进行合并,生成可执行文件。 - 头文件管理
C++中规定,在同一个文件中只能将同一个头文件包含一次,因为很有可能在不知情的情况下包含同一个头文件多次,比如使用了包含另一个头文件的头文件,为了防止这种错误的产生,C++有一种技术可以避免多次包含同一个头文件。它是基于预处理器编译指令#ifndef(if not defind)的。
//本代码块意味着,仅当以前没有使用预处理器编译指令#define定义名称COORDIN_H_,才处理其中的语句
#ifndef COORDIN_H_
#define COORDIN_H_
...
#endif
//当首次编译器遇到该文件时,名称COORDIN_H_未定义
//在这种情况下,编译器将查看#ifndef 与 #endif之间的内容
//并且会读取#define这一行
//如果在同文件中再遇到其他包含该头文件的代码
//系统就知道COORDIN_H_已经被定义过了,直接回调到#endif
//注意:这种方法并不能防止编译器将文件包含两次,但是会让他忽略第一次以外所包含的内容
注意:在C++标准中使用术语"翻译单元",而不是文件。也就是说我们上述的文件,标准称呼为翻译单元。
- 多个库的链接问题
C++标准允许每个编译器设计人员以他认为合适的方式实现名称修饰,也就是说不同编译器创建的二进制模块很可能没有办法正确的链接,也就是说两个编译器很可能为同一个函数时候生成不同的修饰名称。名称的不同将使链接器无法将一个编译器生成的函数调用与另一个编译器生成的函数定义匹配,为了防止出现这种问题,如果我们拥有源代码,我们可以自己的编译器重新编译源代码以防止链接错误。
2.存储持续性
- 四种存储方式的存储持续性
1.自动存储:在函数定义中声明的变量(包括函数参数)的存储持续性是自动的。他们在程序开始执行其所属函数或代码块时被创建,在执行完函数或者代码块后被销毁
2.静态存储:在函数定义外定义的变量和使用关键字static定义的变量的存储持续性都为静态。他们在程序整个运行过程中都存在
3.线程存储:C++11中出现了并行多线程编程,这在多核处理器普及的今天很常见,程序能将计算放在可并行处理的不同线程中,生命周期和线程一样长。
4.动态存储:用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或程序结束为止。这种内存的存储持续性为动态,有时被成为自由存储区或者堆。 - 作用域和链接
作用域:作用域描述了名称在文件的多大范围内可见,例如,函数中定义的变量可在该函数中使用,但是不能在其他函数中使用;而在文件中的函数定义之前定义的变量则可以在所有函数中使用。
链接性描述了名称如何在不同单元间共享。链接性为外部的名称可以在文件间共享,链接性为内部的名称只能由一个文件中的函数共享。自动变量没有链接性,所以他们不能共享。 - 作用域的分类
1.局部变量作用域:只在定义他的代码块中可用。
2.全局变量作用域:从定义位置一直到文件结束可用。
3.类成员的作用域:整个类。
4.名称空间变量的作用域:整个名称空间。
5.函数原型参数的作用域:只在包含参数列表的括号中可用
6.函数的作用域可以是整个类或者整个名称空间ÿ