内存模型与名称空间
9.1 单独编译
C++ 允许鼓励将组件函数放在独立的文件中,可以单独编译这些文件,然后将他们链接成可执行程序;
例程:
头文件: coordin.h
1 #ifndef COORDIN_H_ 2 #define COORDIN_H_ 3 using namespace std; 4 5 struct polar { 6 double distance; 7 double angle; 8 }; 9 10 struct rect { 11 double x; 12 double y; 13 }; 14 polar rect_to_polar(rect xypos); 15 void show_polar(polar dapos); 16 17 #endif
源文件: file1.cpp
1 #include <iostream> 2 #include "coordin.h" 3 4 int main() 5 { 6 rect rplace; 7 polar pplace; 8 9 cout << "Enter the x and y values: "; 10 while (cin >> rplace.x >> rplace.y) 11 { 12 pplace = rect_to_polar(rplace); 13 show_polar(pplace); 14 cout << "Next two number (q to quit): "; 15 } 16 cout << "Bye!\n"; 17 return 0; 18 }
源文件: file2.cpp
1 #include<iostream> 2 #include<cmath> 3 4 #include "coordin.h" 5 6 polar rect_to_polar(rect xypos) 7 { 8 polar answer; 9 answer.distance = sqrt(xypos.x * xypos.x + xypos.y * xypos.y); 10 answer.angle = atan2(xypos.y, xypos.x); 11 return answer; 12 } 13 14 void show_polar(polar dapos) 15 { 16 const double Rad_to_deg = 57.29577951; 17 18 cout << "distance = " << dapos.distance; 19 cout << ", angle = " << dapos.angle * Rad_to_deg; 20 cout << " degrees\n"; 21 }
将这两个源代码文件和新的头文件一起进行编译和链接,将生成一个可执行程序
程序文件:
1. 头文件: 包含结构声明和使用这些结构的函数原型;
2. 源代码文件: 包含与结构有关的函数的代码;(函数定义)
3. 源代码文件: 包含调用与结构相关的函数代码(main 和其他使用这些函数的函数)
头文件管理:
在同一个文件中只能将同一个头文件包含一次,为避免多次包含同一个头文件:
使用基于预处理器编译指令 #ifndef (即 if not defined):
#ifndef COORDIN_H_
. . .
#endif
#define COORDIN_H_: 完成名称定义
头文件中常包含的内容:
1. 函数原型
2. 使用#define 或 const 定义的符号常量
3. 结构声明
4. 类声明
5. 模板声明
6. 内联函数
9.2 存储持续性、作用域和链接性
C++ 使用3种不同的方案来存储数据,区别在于数据保留在内存中的时间:
1. 自动存储持续性:
在函数定义中声明的变量(包括函数参数)的存储持续性为自动的;
在程序开始执行所属的函数代码块时被创建,在执行完函数或代码块时,被释放;
C++ 有两种存储持续性为自动的变量
2. 静态存储持续性:
在函数定义外定义的变量和使用的关键字 static 定义的变量的存储持续性都为静态;
静态变量在程序整个运行过程都存在;
C++有3种存储持续性为静态的变量
3. 动态存储持续性:
用 new 操作符分配的内存将一直存在,知道使用 delete 操作符将其释放或程序结束为止;
这种内存的存储持续性为动态,有时又称自由存储
9.2.1 作用域和链接
作用域描述了名称在文件的多大范围内可见;
链接性描述了名称如何在不同单元间共享;
9.2.2 自动存储持续性
默认情况下,在函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性;
函数开始执行变量所属的代码块时,将为其分配内存,当函数结束时,变量消失(作用域起点为声明的位置);
可以使用C++(和C)关键字 auto 来显示地指出存储类别:
由于关键字 auto 用于默认状态下为自动的变量,因此程序员几乎不使用它
可以使用任何在声明时其值为已知的表达式来初始化自动变量;
自动变量的管理采用堆栈,自动变量的数目随函数的开始和结束而增减(后进先出);
C++也支持使用 register 关键字来声明局部变量,寄存器变量是另一种形式的自动变量:
CPU 访问寄存器中的值的速度比访问堆栈中内存快;
声明寄存器变量:register int count_fast // request for a register variable
一般 register 变量说明符指出变量将被频繁的使用
9.2.3 静态持续变量
C++ 也为静态存储持续性变量提供了 3 种链接性:外部链接性、内部链接性和无链接性;
静态变量在整个程序执行期间一直存在;
如果没有显示的初始化静态变量,编译器将它设置为 0;
要创建链接性为外部的静态持续变量,必须在代码块的外面声明它;
要创建链接性为内部的静态持续变量,必须在代码块的外面声明它,并使用 static 限定符;
要创建没有链接性的静态持续变量,必须在代码块内声明它,并使用 static 限定符
五种变量存储方式
存储描述 | 持续性 | 作用域 | 链接性 | 如何声明 |
自动 | 自动 | 代码块 | 无 | 在代码块中(可以使用关键字 auto) |
寄存器 | 自动 | 代码块 | 无 | 在代码块中,使用关键字 register |
静态,无链接性 | 静态 | 代码块 | 无 | 在代码块中,使用关键字 static |
静态,外部链接性 | 静态 | 文件 | 外部 | 在函数外 |
静态,内部链接性 | 静态 | 文件 | 内部 | 在函数外,使用关键字 static |
1. 静态持续性、外部链接性
double warming = 0.3; // 定义声明,给变量分配存储空间,并初始化
extren double warming; // 引用声明,不给变量分配存储空间,引用已有变量,声明指出使用外部定义的变量 warming
定义与全局变量同名的局部变量后,局部变量将隐藏全局变量;
C++ 提供了作用域解析操作符(::),当放在变量名称前面时,操作符表示使用变量的全局版本(:: warming)
2. 静态持续性、内部链接性
将 static 限定符用于作用域为整个文件的变量时,该变量的链接性是为内部的;
链接性为内部的变量只能在其所属的文件中使用;
对于外部链接性变量,有且只有一个文件包含了该变量的外部定义,其他文件使用该变量,必须在引用声明中使用关键字 extern;
应使用外部变量在多文件程序的不同部分之间共享数据;
应使用链接性为内部的静态变量在同一个文件中的多个函数之间共享(名称空间提供了另外一种共享数据的方法)
3. 静态存储持续性、无链接性
将 static 限定符用于在代码块中定义的变量,将导致局部变量的存储持续性为静态的;
该变量只在改代码块中可用,但它在该代码块不处于活动状态时任然存在:
两次函数调用之间,静态变量的值将保持不变;
如果初始化了静态局部变量,程序只在启动时进行一次初始化(初始化一次)
例程:
1 #include<iostream> 2 using namespace std; 3 const int ArSize = 10; 4 void strcount(const char * str); 5 6 int main() 7 { 8 char input[ArSize]; 9 char next; 10 cout << "Enter a line: \n"; 11 cin.get(input, ArSize); 12 13 while (cin) 14 { 15 /*cin.get(next); 16 while (next != '\n') 17 cin.get(next); */ 18 while (cin.get() != '\n') 19 continue; 20 strcount(input); 21 cout << "Enter next line(empty line to quit): \n"; 22 cin.get(input, ArSize); 23 } 24 cout << "Bye\n"; 25 return 0; 26 } 27 28 void strcount(const char * str) 29 { 30 static int total = 0; 31 int count = 0; 32 33 cout << "\"" << str << "\" contains "; 34 while (*str++) 35 count++; 36 total += count; 37 cout << count << " characters\n"; 38 cout << total << " characters total\n"; 39 }
自动变量 count 被重置为0;
静态变量 total 只在程序运行时被设置为0,以后调用函数时不再进行初始化,且两次调用期间其值保持不变
9.2.4 说明符和限定符
存储说明符:
1. auto 自动变量
2. register 寄存器存储类型
3. static 被用在整个文件的声明中,表示内部链接性;被用于局部声明中,表示局部变量的存储类型为静态的
4. extern 引用声明,声明引用在其他地方定义的变量
5. mutable 当结构变量为 const ,用 mutable 说明的成员也可以被修改
再谈 const:
const 全局变量的链接性为内部的:
const int fingers = 10 // same as static const int fingers
可以把常量放在头文件中,同一工程下的多个文件使用该头文件后,可以得到常量定义
9.2.5 函数链接性
所有函数的存储持续性都自动为静态的,即在整个程序执行期间都一直存在;
默认情况下,函数链接性为外部的,可以在文件间共享;
可以使用 static 将函数链接性设置为内部的,使之只能在一个文件中使用;
C++ 在哪里查找函数:
如果函数原型指出函数是静态的,则编译器只在文件中查找函数定义;
否则,编译器将在所有文件中查找
9.4 名称空间
C++ 提供了名称空间工具,以便更好地控制名称的作用域:
namespace Tack {
double pail;
void fetch ( );
int pal;
struct Well { . . . };
}
除了用户定义的名称空间外,还存在另一个名称空间——全局名称空间,对应于文件级声明区域(全局变量);
名称空间是开放的(open):
可以把名称加入到已有名称空间中;
可以在文件后面再次引用 Tack 名称空间来提供函数代码:
namespace Tack {
void fetch ()
{
. . .
}
}
包含名称空间的名称(如 Tack :: pail)被称为限定的名称;
using 声明和 using 编译指令
using 声明使特定的标识符可用:
using Jill :: fetch;
在函数内部声明将名称添加到局部声明区域;
在函数外面声明将名称添加到全局名称空间中
using 编译指令使整个名称空间可用:
using namespace Jill;
使得所有名称空间的名称全局可用
名称空间可以进行嵌套;
未命名的名称空间相当于链接性为内部的静态变量(C++不赞成在名称空间和全局作用域中使用关键字 static)
9.4.4 名称空间及其前途
1. 使用已命名的名称空间中声明的变量,而不是使用外部全局变量;
2. 使用已命名的名称空间中声明的变量,而不是使用静态全局变量;
3. 如果开发了一个函数库或者类库,将其放在一个名称空间中;
4. 不要在函数头使用 using 编译指令;
5. 导入名称时首先使用作用域解析操作符或 using 声明的方法;