1. 单独编译
和C语言一样,C++也允许甚至鼓励程序员将组件函数放在独立的文件中。
可以单独编译这些文件,然后将它们链接成可执行的程序。
通常C++编译器既编译程序,也管理链接器。
如果只修改了一个文件,则可以只重新编译该文件,然后将它与其他文件的编译版本链接。
这使得大程序的管理更便捷。
头文件中常包含以下内容:
函数原型,使用#define
或const
定义的符号常量,结构声明,类声明,模板声明,内联函数。
注意在包含头文件时,我们使用coordin.h
而不是
,
如果文件名包含在尖括号中,则C++编译器将在存储标准头文件的主机系统的文件系统中查找,
但如果文件名包含在双引号中,则C++编译器首先查找当前工作目录或源代码目录(或其他目录,这取决于编译器),
如果没有在那里找到头文件,则将在标准位置查找。
因此,在包含自己的头文件时,应使用引号而不是尖括号。
2. 存储持续性
C++使用三种(在C++11中是四种)不同的方案来存储数据,这些方案的区别就在于数据保留在内存中的时间。
(1)自动存储持续性
在函数定义中声明的变量(包括函数参数)的存储持续性为自动的,
它们在程序开始执行其所属的函数或代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放。
(2)静态存储持续性
在函数外定义的变量,和使用关键字static
定义的变量的存储持续性都为静态。
它们在程序整个运行过程中都存在。
(3)线程存储持续性(C++11)
当前多核处理器很常见,这些CPU可同时处理多个执行任务。
这让程序能够将计算放在可并行处理的不同线程中。
如果变量是使用关键字thread_local
声明的,则其生命周期与所属的线程一样长。
(4)动态存储持续性
用new
运算符分配的内存将一直存在,直到使用delete
运算符将其释放或程序结束为止。
这种内存的存储持续性为动态,有时被称为自由存储(free store)或堆(heap)。
注:
在C++11中,关键字auto
用于自动类型推断,
但在C语言和以前的C++版本中,auto
的含义截然不同,它用于显式的指出变量为自动存储。
3. 作用域
作用域(scope)描述了名称在文件(编译单元)的多大范围内可见。
例如,函数中定义的变量可在该函数中使用,但不能在其他函数中使用。
而在文件中的函数定义之前定义的变量则可在所有函数中使用。
C++变量的作用域有多种,作用域为局部的变量只在定义它的代码块中可用。
代码块是由花括号括起的一系列语句。
作用域为全局(也叫文件作用域)的变量,在定义位置到文件结尾之间都可用。
自动变量的作用域为局部,
静态变量的作用域是全局还是局部取决于它是如何被定义的。
在函数原型作用域中使用的名称只能在包含参数列表的括号内可用(这就是为什么这些名称是什么以及是否出现都不重要的原因)。
在类中声明的成员的作用域为整个类,
在名称空间中声明的变量的作用域为整个名称空间(由于名称空间已经引入到C++语言中,因此全局作用域是名称空间作用域的特例)。
4. 变量的链接性
链接性(linkage)描述了名称如何在不同单元间共享。
链接性为外部的名称,可在文件间共享,链接性为内部的名称,只能由一个文件中的函数共享。
自动变量的名称没有链接性,因为它不能共享。
链接性为外部的变量,通常简称为外部变量,它们的存储持续性为静态,作用域为整个文件。
4.1 extern
一方面,在每个使用外部变量的文件中,都必须声明它,
另一方面,变量只能有一次定义。
C++提供了两种操作,一种称为定义,它给变量分配存储空间。
另一种是引用声明,它不给变量分配存储空间,而是引用已有的变量。
引用声明使用关键字extern
,且不进行初始化,否则将变成定义,导致分配空间。
double up; // definition, up is 0
extern int blem; // blem defined elsewhere
extern char gr = 'z'; // definition because initialized
extern char gr = 'z';
中的extern
是可以省略的。
如果在多个文件中使用外部变量,只需要在一个文件中包含该变量的定义,
但在使用该变量的其他所有文件中,都必须使用关键字extern
声明它。
注:
省略extern
将使变量具有外部链接属性。
4.2 static
(1)用于作用域为整个文件的变量时
将static
限定符用于作用域为整个文件的变量时,该变量的链接性将为内部的。
链接性为内部的变量只能在其所属的文件中使用。
如果文件定义了一个静态变量,其名称与另一个文件中声明的常规外部变量相同,
则在该文件中,静态变量将隐藏常规外部变量。
// file1
int errors = 20; // external declaration
- - -
// file2
static int errors = 5; // known to file2 only
void froobish(){
cout << errors; // uses errors defined in file2
}
可使用外部变量在多文件程序的不同部分之间共享数据,
可使用链接性为内部的静态变量在同一个文件中的多个函数之间共享数据。
(名称空间提供了另外一种共享数据的方法)
如果将作用域为整个文件的变量变为静态的,就不必担心其名称与其他文件中的作用域为整个文件的变量发生冲突。
(2)用于作用域为整个文件的变量时
将static
限定符用于在代码块中定义的变量时,将导致局部变量的存储持续性为静态的。
这意味着虽然该变量只在该代码块中可用,但它在该代码块不处于活动状态时仍然存在。
因此,在两次函数调用之间,静态局部变量的值将保持不变。
另外,如果初始化了静态局部变量,则程序只在启动时进行一次初始化。
以后再调用函数时,将不会像自动变量那样再次被初始化。
注:
关键字static
被用在作用域为整个文件的声明中时,表示内部链接性,
被用于局部声明中,表示局部变量的存储持续性为静态的。
关键字extern
表示引用声明,即声明引用在其他地方定义的变量。
关键字thread_local
指出变量的持续性与其所属线程的持续性相同。
thread_local
变量之于线程,犹如常规静态变量之于整个程序。
5. 函数的链接性
在默认情况下,函数的链接性为外部的,即可以在文件间共享。
可以在函数原型中使用关键字extern
来指出函数是在另一个文件中定义的,不过这是可选的。
可以使用关键字static
将函数的链接性设置为内部的,使之只能在一个文件中使用。
必须同时在原型和函数定义中使用static
关键字。
static int fn(double x);
...
static int fn(double x){
...
}
这意味着该函数只在这个文件中可见,还意味着可以在其他文件中定义同名的函数。
和变量一样,在定义静态函数的文件中,静态函数将覆盖外部定义,
因此,即使在外部定义了同名的函数,该文件扔将使用静态函数。
单定义规则也适用于非内联函数,因此对于每个非内联函数,程序只能包含一个定义。
对于链接性味外部的函数来说,这意味着在多文件程序中,只能有一个文件包含该函数的定义,
但使用该函数的每个文件都应包含其函数原型。
内联函数不受这种规则的约束,这允许程序员能够将内联函数的定义放在头文件中,
这样包含了头文件的每个文件都有内联函数的定义。
然而,C++要求同一个函数的素有内联定义都必须相同。
注:
在默认情况下,函数的链接性为外部的,即可以在文件间共享。
6. 小结
所有声明,都具有外部连接性。
6.1 具有内部连接性的定义
(1)名字空间(包括全局名字空间)中的静态自由函数,静态友元函数、静态变量定义,const常量定义
(2)enum定义,类的定义,union的定义
(3)inline函数定义(包括自由函数和非自由函数)
6.2 具有外部连接性的定义
(1)非inline的类成员函数,非inline的类静态函数
(2)类静态成员变量
(3)名字空间(包括全局名字空间)中非静态自由函数,非静态友元函数,非静态变量
注:
把一个带有外部连接的定义放在 .h 文件中都会引起错误,
由于类的声明和定义都是内部连接的,一般都放在 .h 文件中。
参考
C++ Primer Plus, 6th - P300~P319
C++ 概念两则:声明和定义,内部连接和外部连接