最近看了c++ primer中的二到四章,每章除了叙述相应主题,还顺带讲解了许多C++中的基本概念,其中好多涉及到了编译器的工作原理,不理解还真的不好记忆,这里做一下总结,以便查阅。大家也一起跟着复习一下吧。
首先要说的就是定义和声明,定义也具有声明的作用,在c++中一个变量必须定义且只定义一次,但可以声明多次,必须在使用变量之前定义或者声明该变量。一处定义,多处声明主要用于源代码分布在多个文件中的时候,即C++编译器所支持的separate compilation(分别编译)。下面就通过例子理解一下吧
Example 1 非const变量的声明和定义
// file_1.cpp
int counter; //定义
// file_2.cpp
extern int counter; //声明
定义在全局作用域(global scope)的非const变量是在整个程序中都可以访问的(可以跨文件),我们可以在一个文件的全局作用域定义变量,通过正确的声明在另外的文件中使用这个变量。
Example 2 const变量的声明和定义
//file_1.cpp
extern const int bufSize = fcn();//定义
//file_2.cpp
extern const int bufSize;//声明
大家都看出区别来了吧,为什么定义const变量时也需要extern呢?是因为const变量默认只能够在当前文件中访问,只有在定义的时候加上extern才能在整个程序中访问。声明时同样需要extern关键字。
接下来说说头文件吧,头文件是用来放声明的而不是定义,头文件经常被很多源文件包含,如果里面放定义,那不是违反了定义只能出现一次的规则(如果定义多次,编译器在链接的时候它怎么知道该用哪个定义?)。对于以上这个规则有三种例外情况大家需要注意,class的定义,在编译时就能确定值的const变量的定义,inline函数的定义,它们都允许在多个源文件中同时定义只要它们在每个文件中的定义完全相同(这不正好放到头文件中吗,你说对吧),下面我们分别解释解释吧
情况1:class的定义
在分别编译每个源文件时,编译器需要知道类的定义,这样它才能知道该类的实例都能调用什么函数,函数的参数都是什么,从而进行语法检查。这个应该很好理解吧,我们经常把一个或者多个类的定义放到一个头文件中,实际我更习惯把这种情况称为类的声明(用来区分类的具体实现)。
情况2:编译时就能确定值的const变量
比如const int magic_number = 512; 前面讲过默认情况下const变量是不能被其他文件访问的,所以可以将它放在头文件中啊,实际上大部分编译器在编译时会用常数表达式(在这里就是512)替换掉const变量。那对于编译时不能确定的const变量,如何定义呢?你应该已经知道了吧!
//源文件中定义const变量时加上extern关键字
extern const int magic_number = 512;
//头文件中声明const变量
extern const int magic_number;
情况3:inline函数,无论是全局的inline函数,还是类实现中的inline函数都可以放到头文件中,这是因为编译器并不把inline函数作为一个函数来编译,而是做类似于宏的处理(比宏好用,例如它可以进行函数参数,返回值的类型检查)将inline函数的实现展开到调用该函数的地方。这就是为什么它也可以定义在头文件中。(如果在类的定义中直接写函数实现,则该函数被直接实现为inline函数,其他情况都需要inline关键字)