内存模型和名称空间(单独编译、存储持续性、作用域、链接性、说明符、限定符、布局new操作符、名称空间)
1.模板声明由于不是将被编译的代码,只是指示编译器如何生成与源代码中的函数调用相匹配的函数定义,因此放在头文件中是可以的
2.在链接编译模块时,要确保所有的对象文件或库都是由同一个编译器生成的。如果有源代码可以用自己的编译器重新编译源代码来消除链接错误。
3.C++采用三种方案来存储数据,区别在于数据在内存中保留的时间,分别是:自动存储持续性、静态存储持续性和动态存储持续性。
自动存储持续性:函数定义中声明的变量,程序执行这部分函数或代码块时被创建,执行完这部分时释放
静态存储持续性:函数定义外定义的变量和使用static定义的变量,在程序整个运行中存在。
动态存储持续性:使用new分配的内存,知道被delete时释放内存。
4.链接性:名称在不同单元之间的共享。链接性为外部的名称可在文件间共享,为内部的名称只能由一个文件中的函数共享。自动变量没有链接性,因为它们不能共享。
5.在类中声明的成员作用域为整个类。名称空间中声明的变量作用域为整个名称空间。函数的作用域不能是局部的,可以是整个类或整个名称空间(包括全局的)。
6.堆栈:LIFO后进先出。一段用于管理变量的内存。新的数据被放在原有数据的相邻内存单元中,程序使用完变量后从堆栈中删除它。堆栈的默认长度取决于实现,但编译器通常提供改变堆栈长度的选项。使用指向栈底(堆栈开始)和指向变量后的下一个可用内存单元的指针来管理。
7.自动存储持续性
(1)自动变量:可以用关键字auto指出,不过多数是默认给定,因此可以省略auto。
自动变量可以使用任何已知值的表达式进行初始化。
自动变量用堆栈进行管理,当函数被调用时,自动变量被加入到堆栈,指向栈顶的指针向后移动,函数结束栈顶指针重置为调用函数前的值,从而释放新变量使用的内存。
(2)寄存器变量:使用register关键字指出 形如: register int count_fast;
寄存器变量与自动变量类似,存储持续性为自动、作用域为局部、无链接性,但区别在于不用堆栈管理,而是用寄存器管理,因此访问速度比放在堆栈内存块中的变量速度要快。
注意:寄存器变量由于没有被放置在内存中,因此没有内存地址,因此不能对它进行取地址操作。
9.静态持续变量:静态持续变量有三种链接性——外部链接性、内部链接性、无链接性
(1)静态变量在程序运行期间不变,因此可以直接放在内存中,不需要用堆栈进行管理。
(2)如果静态变量没有初始化,则默认它值为0,若为数组则每个元素为0。
(3)三种声明:
int global = 1000; //外部链接性 在代码块外声明 (也称全局变量)
作用域为整个文件,从声明到文件结尾,可以在程序的其他文件中使用
static int onefile=50; //内部链接性 代码块外声明,用static标识
作用域为整个文件,从声明到文件结尾,只能在包含它声明的代码文件中使用
int main()
{
...
}
void fun1(int n)
{
static int count =0; //无链接 代码块内声明,用static限定
作用域为代码块内,函数不被执行时也存在于内存中
int linma=0;
}
(4)初始化: (一)未被初始化,则默认初始化为0;(二)只能用常量表达式来初始化静态变量(字面常量、const、enum 或sizeof)
(5)外部存储时为防止数据被修改,可以使用常量数据,用const进行修饰,如:
const char * const month[12]= //第一个const防止字符串被修改,
//第二个const确保数组中的每个指针始终指向它最初指向的字符串
{
"January","February",.....
}
(6)对于外部链接性变量,如果只有一个文件中包含了该变量,其他文件中要引用时需要在引用声明中加extern。
(7)如果两个文件中,A文件的静态外部变量和当前常规外部变量(static声明的)重名,则隐藏A中声明的静态变量。
10.cv限定符: const 与 volatile
const:内存被初始化后,程序将不能对内存单元进行修改(链接性为内部)
volatile:即使程序代码没有对内存进行修改,其值也可能发生改变(如硬件修改或两个程序相互影响)。作用是改善编译器的 优化性能。将变量声明为volatile相当于告诉编译器不要进行优化
mutable:即使结构或类变量为const,某个成员也可以被修改,如:
struct data
{
char name[30]; //不可以被修改
mutable int accesses; //可以被修改
...
};
const data veep = {"ddddd",0,...};
11.函数存储持续性为静态,链接性为外部的。但如果在函数的声明和定义前加上static关键字,则函数变为内部链接函数,只能在当前文件中使用。且这个静态函数会覆盖外部定义。
12.C++中引用C库文件中的函数spiff可以利用extern :
extern "C" void spiff(int);
同理有用法extern void spoff(int); 和 extern "C++" void spaff(int);
13.编译器中使用三块独立的内存用于存放静态变量、自动变量和动态存储。
其中,动态存储的内存分配和指向内存指针分配方式不同。指向该地址的指针可以是自动或静态指针变量。如:
float *p_fees = new float[20]; //new的空间将一直在内存中,直到被delete。指针p_fees会包含在该声明的函数结束时消失。
14.new在堆(heap)中为被定义的变量分配内存 ,如果要指定new出的内存分配的地址(如在一个自定义的buffer中)可以使用布局new,使用时需要包含头文件<new>,除需要指定参数外,其他与new的使用相同。具体如下:
double *pd1,*pd2;
char buffer[512];
pd1=new double[5]; //常规new,在堆中分配内存
pd2= new (buffer)double[5]; //布局new,在buffer中分配内存
15.名称空间:可以是全局的或位于另一个名称空间中,但不能位于代码块中。如果没有引用的常量,则链接性是外部的。
全局名称空间是文件级声明区域。
作用域解析符:: 可以限定名称。如pail为未限定名称,Jack::pail为限定名称。
16.未命名的名称空间由于作用域仅在该文件中,因此链接性为内部,可以用来替代static,如下:
static int counts; //合法,但C++不建议、
namespace
{
int counts; //合法,作用形同于上,C++建议
}