复习9:源文件和程序

写这篇文章之前要介绍命名空间和异常的。因为现在不常用它们,暂且略去。

9.1 分别编译

      源文件提交给编译器后,首先进行预处理,即完成宏处理和按照#include指令引进头文件。预处理之后的源文件成为编译单位--编译器真正的工作对象。连接器将分别编译的部分约束在一起。

 

9.2 连接

      如果一个名字可以在定义它的编译单位之外的其他编译单位使用,称其具有外部连接性。否则称其具有内部连接性。

        const,typedef,inline函数都是内部连接的。应该把全局的这些东东放到头文件中。其中可以使用显式声明使const具有内部连接: 在匿名名字空可以使一些名字局部于一个编译单位。匿名名字空间的效果很像内部连接(其实并不是,只是因为在别的编译单位我们无法通过名字空间名访问其中的名字)。

在c/c++程序里,static也被用于表示“使用内部连接”,请不要在函数或类内部外使用static关键字。

9.2.1 头文件

      #include包含文件时,就是使用文件内容替换这个#include语句。

      作为经验,头文件里可以包括:

      命名名在空间namespace N {...},类型定义static Point{int x,y;};,模板声明template<class T>class Z;,模板定义template<class T>class Z {...};,函数声明extern int strlen(const char*);,在线函数定义inline char get(char* p){return *p++};数据声明extern int n;,常量定义const float pi = 3.14;,枚举enum Light{red,yellow,green};名字声明class Matrix;,包含指令#include<algorithm>,宏定义#define V 12,条件编译指令#ifdef _cplusplus,当然还有注释。

      头文件里绝不该有:

      常规的函数定义char get(char *p) {return *p++;},数据定义int a;,聚集量的定义short tbl[] = {1,2,3};匿名名字空间namespace {...},导出的模板定义export template<class T>f(T t){...}。

9.2.2 标准库头文件

9.2.3 单一定义规则

      一个类、模板或者在线函数的两个定义能够被接受为同一个定义的实例,当且仅当:

      1. 它们出现在不同的编译单位。

      2. 它们按一个个单词对应相同。

      3. 这些单词的意义在两个编译单位中也完全相同。

9.2.4 与非c+代码的链接

9.2.5 连接与指向函数的指针

       不同语言间,甚至相同语言不同编译器编译的obj文件,相互连接是有困难的,比如在寄存器保存参数的方式和参数入栈顺序都会不相同。我们可以在extern声明中给出相关的连接约定。例如extern "C" char* strcpy(char*,cosnt char*);说明它按照c连接约定进行连接。这部分不常用,仅仅有个概念,用到时在做研究。

 

9.3 使用头文件

9.3.1 单一的头文件

      使用extern声明头文件中的数据(几个源文件的公用数据),在某个源文件中定义它们,可以避免重复定义。

9.3.2 多个头文件

      程序规模稍大时推荐使用多个头文件,每个复杂的源文件可有两个头文件,一个是调用界面的头文件x.h,一个是实现界面的头文件x_impl.h这样做的好处是逻辑明确,模块依赖度减少。

9.3.3 头文件的使用

9.3.4 包含保护服

      即使用条件编译指令#ifndef避免头文件重复包含。

 

9.4 程序

9.4.1 非局部变量的初始化

      原则上,所有在函数之外定义的变量应该在main调用前完成初始化。一个编译单位内这种非局部变量按照它们定义的顺序初始化。如果这样的变量没有显式的初始式,它将被初始化有关类型的默认值。对于内部类型和枚举,默认值是0。(全局变量为什么一定要初始化呢,显式的初始式和默认值也是有不同的,这好像跟exe文件的结构有关系,下次再搞清楚这些问题)

9.4.1.1 程序终止

      四种主要的终止方式:main返回,调用exit(),调用abort(),抛出未捕捉的异常。其他病态的或未定义的方式也可使程序垮台。

      程序调用exit()终止,所有构造起来的静态对象的析构函数都被调用。而调用abort()则不做这些工作。这意味着exit()并不立即终止程序,在析构函数调用exit()可能导致无穷递归。但不提倡这样终止程序,调用exit()的上游函数的局部变量的析构函数都不会被调用!最好抛出一个异常,让异常处理器决定做些什么。

      c/c++标准库函数atexit()使我们可以让程序终止前执行一些代码,其中的规则和利弊留于用到时再讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值