第九章 内存模型和名称空间
CPP可以选择数据保留在内存中的时间长度以及程序的那一部分可以访问数据(作用域和链接)等。可以使用new来动态地分配内存,而定位new运算符提供了这种技术的一种变种。CPP名称空间是另一种控制访问权的方式。通常,大型程序都由多个源代码文件组成,这些文件可能共享一些数据。这样的程序涉及到程序文件的单独编译。
和C语言一样,CPP也允许程序员将组件函数放在独立的文件中。第1章介绍过, 可以单独编译这些文件, 然后将它们链接成可执行的程序。(通常,C++编译器既编译程序, 也管理链接器。) 如果只修改了一个文件, 则可以只重新编译该文件, 然后将它与其他文件的编译版本链接。
简而言之, 将一个程序放在多个文件中将引出新的问题。因此他们提供了#include来处理这种情况。与其将结构声明加入到每一个文件中,不如将其放在头文件中,然后在每一个源代码文件中包含该头文件。这样, 要修改结构声明时, 只需在头文件中做一次改动即可。另外, 也可以将函数原型放在头文件中。因此, 可以将原来的程序分成三部分。
头文件: 包含结构声明和使用这些结构的函数的原型。
源代码文件: 包含与结构有关的函数的代码。
源代码文件: 包含调用与结构相关的函数的代码。
如果编写另一个程序时, 也需要使用这些函数, 则只需包含头文件,并将函数文件添加到项目列表或make列表中即可。另外,这种组织方式也与OOP方法一致。一个文件(头文件)包含了用户定义类型的定义; 另一个文件包含操纵用户定义类型的函数的代码。这两个文件组成了一个软件包, 可用千各种程序中。
请不要将函数定义或变量声明放到头文件中。这样做对于简单的情况可能是可行的, 但通常会引来麻烦。例如, 如果在头文件包含一个函数定义, 然后在其他两个文件(属于同一个程序) 中包含该头文件,则同一个程序中将包含同一个函数的两个定义, 除非函数是内联的, 否则这将出错。下面列出了头文件中常包含的内容。
函数原型。
使用#define或const定义的符号常量。
结构声明。
类声明。
模板声明。
内联函数。
9.2. 存储持续性、作用域和链接性
C++使用三种不同的方案来存储数据。
-
自动存储持续性: 在函数定义中声明的变量(包括函数参数)的存储持续性为自动的。它们在程序开始执行其所属的函数或代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放。C++有两种存储持续性为自动的变量。
-
静态存储持续性: 在函数定义外定义的变量和使用关键字static定义的变量的存储持续性都为静态。它们在程序整个运行过程中都存在。C扫一有3种存储持续性为静态的变量。
-
线程存储持续性(C++11):当前, 多核处理器很常见, 这些CPU可同时处理多个执行任务。这让程序能够将计算放在可并行处理的不同线程中。如果变量是使用关键字thread_local声明的,则其生命周期与所属的线程一样长。本书不探讨并行编程。
-
动态存储持续性: 用new运算符分配的内存将一直存在, 直到使用delete 运算符将其释放或程序结束为止。这种内存的存储持续性为动态, 有时被称为自由存储(free store) 或堆(heap)。
9.2.1. 作用域和链接
作用域(scope) 描述了名称在文件(翻译单元)的多大范围内可见。
链接性(linkage)描述了名称如何在不同单元间共享。链接性为外部的名称可在文件间共享, 链接性为内部的名称只能由一个文件中的函数共享。自动变量的名称没有链接性, 因为它们不能共享。
C++11中,auto用于自动类型推断。
9.2.7. 说明符和限定符
volatile
假设编译器发现,程序在几条语句中两次使用了某个变量的值,则编译器可能不是让程序查找这个值两次,而是将这个值缓存到寄存器中。这种优化假设变量的值在这两次使用之间不会变化。如果不将变量声明为volatile,则编译器将进行这种优化;将变量声明为volatile,相当千告诉编译器,不要进行这种优化。
mutable
可以用它来指出,即使结构(或类) 变量为const, 其某个成员也可以被修改。