第9章 内存模型和命名空间
P300 单独编译
一个程序可分为三部分:
- 头文件:包含结构声明和使用这些结构的函数的原型
- 源代码文件:包含与结构有关的函数的代码
- 源代码文件:包含调用与结构相关的函数的代码
注意不要将函数定义或变量声明放在头文件中
#ifndef COORDIN_H_
#define COORDIN_H_
#endif // !COORDIN_H_
使用这样的编译情况可以防止代码被包含两次
P304 存储持续性、作用域和链接性
C++中使用四种不同的方案来存储数据:
- 自动存储持续性:函数中定义的变量其生存周期仅为函数执行过程中,执行完后其内存将会被释放
- 静态存储持续性:用static修饰的变量,其生存周期为整个程序运行期间
- 线程存储持续性:使用thread_local声明的,其生命周期与所属线程一样长
- 动态存储持续性:用new与delete搭配的变量,有时被称为自由存储或堆。
作用域和链接性
链接性为外部的名称可在文件之间共享,链接性为内部的名称只能由一个文件中的函数共享。自动变量没有链接性,因此不能被共享。
自动存储持续性
栈:在函数运行伊始,函数首先将参数值压入栈中,被调用的函数根据其形参描述来确定每个参数的地址。并将其与变量名相关联,在函数结束后,变量并不会被删除,但栈指针将会下移,在之后的函数运行时被覆盖。
关键字register用来修饰变量类型,将变量放在寄存器中,旨在加快运行速度。如:register int count_fast;
。
静态持续变量
C++为静态持续变量提供了三种链接性:外部链接性、内部链接性、无链接性。请看如下代码示例:
int global = 1000;//外部链接性
static int one_file = 50;//内部链接性
void func1(int n) {
static int count = 0;//无链接性
}
静态持续性、外部链接性
- 单定义规则
在每个使用外部定义的文件中,应在声明之外的使用处使用extern修饰,否则则为二次定义。如:extern int cats
#include<iostream>
using namespace std;
double warming = 0.3;
void update(double dt);
void local();
int main() {
cout << "全局warming是" << warming << endl;
update(0.1);
cout << "全局warming是" << warming << endl;
local();
cout << "全局warming是" << warming << endl;
return 0;
}
#include<iostream>
using namespace std;
extern double warming;
void update(double dt);
void local();
void update(double dt) {
extern double warming;
warming += dt;
cout << "更新全局warming为" << warming << endl;
}
void local() {
double warming = 0.8;
cout << "本地warming为" << warming << endl;
cout << "但是全局warming为" << ::warming << endl;
}
将上方两个文件链接,注意到下面代码倒数第二行的“::”,它是域解析运算符,将该运算符放于变量前,可使用变量的全局版本。
下面再看一段代码(感觉用法比较重要所以记一下)
#include<iostream>
using namespace std;
const int ArSize = 10;
void strcount(const char* str);
int main() {
char input[ArSize];
char next;
cout << "输入一行\n";
cin.get(input, ArSize);
while (cin) {
cin.get(next);
while (next != '\n')
cin.get(next);
strcount(input);
cout << "输入一行,空行时停止\n";
cin.get(input, ArSize);
}
cout << "拜拜" << endl;
return 0;
}
void strcount(const char *str) {
static int total = 0;
int count = 0;
cout << "\"" << str << "\"包含";
while (*str++)
count++;
total += count;
cout << count << "个字符\n";
cout << total << "个字符总共\n";
}
这段代码利用了这样一个事实,即试图使用get(char *,int)读取空行将导致cin为false。
说明符和限定符
- volatile
这个关键字表明,及时程序代码没有对内存单元进行修改,其值也可能发生变化。将变量声明为volatile将会防止程序发生将变量存储于寄存器中这一优化过程。 - mutable
mutable可以指出即使结构或类是const,其某个成员也可以被修改。
struct data {
char name[30];
mutable int accesses;
};
const data veep = { "a",8 };
strcpy(veep.name, "b");//错误
veep.accesses++;//允许
- const
const全局变量的链接性是内部的,但可以通过extern来改变其链接性如:extern const int states = 50 ;
。
使用一个函数的每个文件都应包含函数原型。
存储方案和动态分类
使用new进行动态分配,可以在类型名后面加上初始值,并用括号括起:int *pi = new int (6)
。这也适用于结构或数组。
当使用new失败时,将会返回一个空指针。
定位new运算符:可以将new申请在一个确定的位置,如下所示:
char buffer1[50];
p1 = new (buffer1) chaff;
把chaff放在buffer1的位置上。但这种情况下就不能使用delete来删除内存了,因为buffer存放的位置在静态内存,而不是new运算符常规指向的堆内存。