1.头文件和源文件
头文件中常常包含的内容:
- 函数原型
- 使用#define或const定义的符号常量
- 结构声明
- 类声明
- 模板声明
- 内联函数
也就是说头文件中常常包含的是一些声明部分,而把这些声明的实现常常放到源文件中,通过一个头文件可以知道哪些函数、常量或者类被声明。
在同一个文件中只能将同一个头文件包含一次。用预处理器编译指令#ifndef可以避免重复包含同一头文件。
2.存储持续性
- 自动存储持续性:在函数定义中声明的变量(包括函数参数)的存储持续性为自动的。它们在程序执行所属的函数或代码块时被创建,执行完后,自动被内存释放。
- 静态存储持续性:在函数定义外定义的变量和使用static定义的变量的存储持续性都为静态。它们在程序整个运行过程中都存在。
- 动态存储持续性:用new操作符分配的内存将一直存在,直到使用delete操作符释放该内存。这种内存的存储持续性为动态,也称为自由存储。
3.内部链接性,外部链接性,无链接性
在函数外部声明的static变量或const常量具有内部链接性,作用于整个文件;在函数外部声明的一般变量具有外部链接性,同样作用于整个文件,并且其他文件也可以使用,在其他文件中使用该变量时,需要extern来进行引用。局部变量(代码块中声明的变量)无链接性。
如果希望在函数外部声明的const常量具有外部链接性,则在声明时加上const关键字可以覆盖原有的内部链接性,但是其他文件使用这个常量时,都需要用extern const来声明。
4.说明符和限定符
说明符
- auto——声明自动存储变量
- register——声明CPU寄存器变量
- static——声明静态变量,内部链接性变量
- extern——引用已声明的外部变量
- mutable——即使结构或类变量为const,用mutable修饰的成员其值也可以被改变。
限定符
- const
- volatile——即使程序代码没有对内存单元进行修改,其值也可能改变。比如某个指针指向某个硬件位置,硬件可能修改其中的内容
5.函数和链接性
函数也有链接性,C++不允许函数嵌套,所有函数的存储持续性都是静态的,即在整个程序执行期间都一直存在。默认情况下,函数的链接性是外部的,即可以在文件间共享。使用static关键字修饰函数可以将函数的链接性设置为内部的,使它只能在这个文件中使用。
6.布局操作符new(placement new)
一般情况下,new负责在堆中找到一个能够满足要求的内存块。(placement) new操作符是new操作符的变体,叫做布局操作符,程序员能够通过它指定使用的内存位置。程序员可能通过这种特性来设置其内存管理规程或处理需要通过特定地址进行访问的硬件。
- 使用new操作符,必须要包含头文件new,然后将new操作符用于提供了所需地址的参数。
- 布局new操作符,使用传递给它的地址,它不跟中那些内存单元已经使用,也不查找未使用的内存块。
- 常规new操作符使用delete来释放内存。但是对于(placement)new操作符,如果指定的内存(传入的地址)属于静态内存,则不能够使用delete来释放内存,如果指定的内存是通过new来分配的,则需要使用delete来释放内存。
- #include
- #include
- const int BUF = 512;
- const int N = 5;
- char buffer[BUF];
- int main()
- {
- using namespace std;
- double *pd1,*pd2;
- //int i ;
- cout<<"Calling new and placement new:/n";
- pd1 = new double[N];
- pd2 = new(buffer)double[N];//布局new操作符将pd2放在buffer数组中,pd2的地址和buffer的地址是相同的
- for(int i = 0;i<N;i++)
- pd2[i] = pd1[i] = 1000 + 20.0*i;
- cout<<"Buffer addresses:/n"<<"heap:"<<pd1
- <<" static:"<<(void *)buffer<<endl;
- for(int i = 0;i<N;i++)
- {
- cout<<pd1[i]<<" at "<<&pd1[i]<<";";
- cout<<pd2[i]<<" at "<<&pd2[i]<<endl;
- }
- cout<<"/nCalling new and placement new a second time:/n";
- double *pd3,*pd4;
- pd3 = new double[N];
- pd4 = new(buffer)double[N];//buffer和pd4地址相同,这说明布局new操作符,使用传递给它的地址,它不跟中那些内存单元已经使用,也不查找未使用的内存块
- for(int i = 0;i<N;i++)
- pd4[i] = pd3[i] = 1000 + 20.0*i;
- cout<<"Buffer contents:/n";
- for(int i = 0;i<N;i++)
- {
- cout<<pd3[i]<<" at "<<&pd3[i]<<";";
- cout<<pd4[i]<<" at "<<&pd4[i]<<endl;
- }
- cout<<"/nCalling new and placement new a third time:/n";
- delete[] pd1;
- pd1 = new double[N];
- pd2 = new(buffer + N*sizeof(double))double[N];//在buufer的内存基础上向下偏移40个字节
- for(int i = 0;i<N;i++)
- pd2[i] = pd1[i] = 1000 + 20.0*i;
- cout<<"Buffer contents:/n";
- for(int i = 0;i<N;i++)
- {
- cout<<pd1[i]<<" at "<<&pd1[i]<<";";
- cout<<pd2[i]<<" at "<<&pd2[i]<<endl;
- }
- delete[] pd1;
- delete[] pd3;
- return 0;
- }
7.名称空间
一些概念:
- 声明区域(declaration region)
在函数外面声明的变量其声明区域为所在文件,在代码块中声明的变量其声明区域为该代码块。 - 潜在作用域(potential scope)
using声明和using编译指令:using std::cout;(using声明);using namespace std;(using编译指令),表示std命名空间下的所有名称都可用。
使用名称空间的一些原则:
- 使用在已命名的名称空间中声明的变量,而不是使用外部全局变量。
- 使用在已命名的名称空间中声明的变量,而不是使用静态全局变量。
- 如果开发了一个函数库或类库,将其放在一个名称空间中。
- 仅将编译指令using作为一种将旧代码转换为使用名称空间的权宜之计。
- 不要在头文件中使用using编译指令。如果非要使用编译指令using,应将其放在所有预处理编译指令#include之后。
- 导入名称时,首选使用作用域解析操作符或using声明的方法。
- 对于using声明,首选将以作用域设置为局部而不是全局。