目录
一、单独编译
C++和C语言一样,也允许将程序进行模块化设计,即将设计为一个.h文件(头文件)和.c文件。头文件常包含如下内容:
①函数原型
②使用#define 或const定义的符号常量
③结构声明
④类声明
⑤模板声明
⑥内联函数
在包含头文件时,使用""而不是<>,如果头文件包含在尖括号中,编译器将在存储标准头文件的主机文件系统中查找;如果头文件包含在双引号中,编译器将首先查找当前的工作目录或者源代码目录,如果没有找到头文件,将在标准位置找。因而使用自己的头文件应该使用""而不是<>。
在同一个文件中只能将同一头文件包含因此。预处理器编译指令#ifndef避免头文件多次包含。如下,当编译器首次遇到该文件时,COORDIN_H_未定义,则执行#ifndef和#endif之间的内容,若在同一个文件1中重复包含这个头文件,编译器再次遇到时,由于COORDIN_H_已定义,则直接跳到#endif。
#ifndef COORDIN_H_
#define COORDIN_H_
statement
#endif
在IDE中,不要将头文件加入到项目列表中,也不要在源代码文件中#include另一个源代码文件 另外,还需要注意多个库链接:不同的编译器可能会将同一个函数生成不同的修饰名词,因此创建的二进制模块大多数情况下都不能正确的链接。在链接编译模块时,需确保对象文件和库都是同一个编译器生成的。
二、存储持续性、作用域和链接性
C++使用三种(C++11中是四种)不同的方案来存储数据,这些方案的区别在于数据保留在内存中的时间。
①自动存储持续性:在函数定义中声明的变量(包括函数形参)的存储连续性为自动。它们在程序开始执行其所属的函数或者代码块是被创建,在执行完函数或代码块时,它们使用的内存被释放。C++有两种存储持续性为自动的变量。
②静态存储持续性:在函数定义外定义的变量和使用关键字static定义的变量。它们在程序运行过程中都存在。C++有三种这样的变量。
③动态存储持续性:用new运算符分配的内存,在使用dekete释放之前一致存在。同时也可以被称为自由存储(free store)或堆(heap)。
④线性存储持续性(C++11):当前,多核处理器很常见,这些 CPU 可同时处理多个执行任务。这让程序能够将计算放在可并行处理的不同线程中。如果变量是使用关键字 thread local 声明的,其生命周期与所属的线程一样长。
1、作用域和链接
作用域:分为局部和全部,局部仅在当前函数中或代码块(使用{}括起来的一段代码)里,全局在单前文件中定义的位置至结尾。
链接性:分为外部和内部,外部可在文件间共享,内部只能由一个文件中的函数共享。C++变量的作用域有很多种:
①静态变量的作用域是全局还是局部取决于是如何被定义的。
②函数原型作用域中使用的名称只在包含参数列表的括号内使用。
③类中声明的成员的作用域是整个类。
④在名称空间声明的变量作用域是整个名称空间(全局域是名称空间域的特例)。
⑤C++函数的作用域可以是整个类或者整个名称空间,但不能是局部的(即不能在代码块中定义函数)。C++存储方式是通过存储持续性、作用域和链接性描述。
2、自动存储持续性
在默认情况下,在函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性。即不同函数间的变量只有在定义他们的函数中才可以使用。但如果在一个代码块中包含另外一个代码块,内部代码块定义了一个变量与外部代码块的变量名字相同,在程序执行内部代码块时,新的定义将会隐藏以前的定义,等到内部代码块执行完毕,外部的定义才可以重新可见。
(1)自动变量和栈
首先了解一下栈,栈 (stack) 是一种串列形式的 数据结构。这种数据结构的特点是 后入先出 (LIFO, Last In First Out),数据只能在串列的一端 (称为:栈顶 top) 进行 推入 (push) 和 弹出 (pop) 操作。自动变量保存在栈中,函数调用时自动变量入栈(开辟内存存储自动变量),函数结束出栈(释放使用的内存)。程序使用两个指针来跟踪栈,栈底指针指向栈的开始位置,栈顶置针指向下一个可用的内存单元。下图简化描述了函数调用时的栈变化。
(2)寄存器变量
关键字register 最初由C语言引入,建议编译器使用CPU寄存器来存储自动变量。如:
register int count_fast; //
这样可提高访问变量的速度。在C++11中该关键字已经失去了它的用途,现在和以前auto的用途一样显式的指出变量是自动的。
3、静态持续变量
与C语言一样,C++为静态存储持续性变量提供了3种链接性:
①外部连接性:可在其他文件种访问
②内部链接性:只能在当前文件中访问
③无链接性:只能在当前函数或代码块中访问
但是这些变量在程序运行的整个过程中存在。在程序运行时,这些变量被存储在固定的内存块中,另外,若没有显示的初始化静态变量,编译器将设置为0。
下表展示了不同自动变量以及静态变量的区别
可以看到,静态变量的连接性由关键字static以及声明的位置取决
4、静态持续性、外部链接性
链接性为外部的变量通常简称为外部变量,它们的存储持续性为静态,作用域为整个文件。外部变量是在函数外部定义的,因此对所有函数而言都是外部的。例如,可以在 main()前面或头文件中定义它们。可以在文件中位于外部变量定义后面的任何函数中使用它,因此外部变量也称全局变量(相对于局部的自动变量)。
(1)单定义规则
在每一个使用外部变量的文件中都必须声明它;同时C++有单定义规则,即变量只能有一次定义。因此C++提供了两种变量声明:
①定义声明,简称为定义,它给变量分配存储空间;
②引用声明,简称为声明,不分配存储空间,它引用已有变量。
引用声明使用关键字extern,且不进行初始化;否则声明为定义,导致分配存储空间
double up;
extern int blem;
extern char blem;
在多个文件中使用外部变量时,只需要在一个文件中定义,其他文件使用extern声明。
(2)全局变量和局部变量
全局变量与局部变量各有好处:
局部变量:能够数据隔离
全局变量:所有函数都能访问,无需传递变量参数。但是程序不可靠,无法保持数据完整性。适用于常量数据,例如月份,星期等数据块。
这种不需要修改的常量数据,还可以使用const来防止数据被修改。
5、静态持续性、内部链接性
将static限定符用于作用域为整个文件的变量时,该变量的链接性将为内部的。只能在所属的文件中使用。
另外,当一个文件定义了一个静态外部变量,其名称与另外一个文件中的常规外部变量相同,则在该文件中,静态变量将隐藏常规外部变量。
6、静态存储连续性、无链接性
使用static修饰在代码块中定义的局部变量,存储连续性是静态的。改变了只能在代码块中使用,但是代码块不活跃状态时变量依然存在。这种变量只会在启动时进行一次初始化,以后再调用函数时,将不会像自动变量那样重新初始化。
7、说明符和限定符
存储说明符:
①auto(C++11中已不是说明符)
②register,声明寄存器存储,C++11中功能变成和以前的auto一样显示指出变量是自动的。
③static
④extern
⑤thread_local(C++新增),thread_local与static或extern结合使用。指出变量的持续性与其所属线程的持续性相同。thread_local之于线程犹如静态变量之于程序。
⑥mutable,mutable指出const修饰的结构或类,其中某个成员也是可以被修改的
cv-限定符:
①const :在C++中,默认情况下全局变量的链接性是外部的,但const限定符全局变量的链接性是内部的,就像使用了static说明符一样,不过可以添加extern关键字来覆盖内部链接性。
②volatile:volatile表明 即使程序未对内存单元进行修改,其值也可能发生变化。比如说将指针指向某个硬件位置,可能是某个端口的信息,在这种情况下可能是硬件修改了数据。或者两个程序共享程序,互相影响。volatile作用是为了改善编译器的优化能力。
8、函数链接性
在C++中,所有的函数存储持续性都自动是静态的,在程序执行的整个过程都存在,默认情况下,函数的链接性为外部, 可以在函数原型使用extern指出函数是在另外一个文件定义的。也可以使用static将函数链接性设置为内部。
程序寻找函数时,若该文件中函数原型指出函数是静态的,编译器只会在该文件中查找,否则将在所有的程序文件中查找。若存在两个定义,则报错;若找不到定义,则会去库中寻找。
9、语言链接性
C++具有多态性,所以同一个函数名可对应多个函数。因此C++编译器执行会进行名词矫正或者名词修饰,为重载函数生成不同的符号名称。例如,spiff(int)转换为 _spiff_i ,spiff(double , double) 转换为 _spiff_d_d。这种修饰被称为C++语言链接。
C语言由于一个名称仅对应一个函数,则函数没有链接性,。
若C++程序链接C语言库,函数原型要指出约定。
extern "C" void count(int);
extern void count(int);
extern "C++" void count(int);
10、存储方案和动态分配
通常编译器使用三块独立的内存:(1)静态变量;(2)自动变量;(3)动态存储
动态内存由运算符new和delete控制,而非作用域和链接性规则控制。new分配内存,delete释放内存。
(1)使用new运算符初始化
与前面文章讲述的初始化方式相同
int *pi = new int (6);
double *pd = new double (99.99);
(2)new失败
new可能找不到请求的内存量,之前会返回一个空指针,现在会引发异常std::bad_alloc。
(3)new:运算符、函数和替换函数
可以使用new、new[]、delete、delete[]函数(被称为分配函数),进行内存空间的开辟与释放。
void * operator new (std::size_t);
void * operator new[] (std::size_t);
void operator delete (std::size_t);
void operator delete[] (std::size_t);
//右边为左边的转换
int * pi = new int; //int * pi = new(sizeof(int));
int *pa = new int[40]; //int *pa = new(40 * sizeof(int));
delete pi; // delete (pi);
(4)定位new运算符
原本new和delete操作是在堆(heap)中寻找满足需求的内存块。new还有一个变体,被称为定位new运算符,能够指定要使用的位置,可用这个特性来设置内存管理规范、处理需要通过特定地址进行访问的硬件或者在特定的位置创建对象。使用定位new运算符有如下要点:①包含头文件new
②将new运算符用于提供了所需地址的参数
③变量后面可以后方括号也可以没有
#include <new>
struct chaff
{
char dross[20];
int slag;
};
char buffer1[50];
char buffer2[500]; /使用静态数组为定位new运算符提供内存空间
int main()
{
chaff *p1, *p2;
int *p3, *p4;
p1 = new chaff;
p3 = new int[20];
p2 = new (buffer1)chaff; //从buffer1中分配空间给chaff;
p4 = new (buffer2)int[20]; //从buffer2中分配空间给一个包含20个元素的int数组。
p5 = new (buffer2)int[20]; //overwrite p4所用内存
p6 = new (buffer + 20 * sizeof(int))int[20] ;//使用偏移量,紧跟着p5所占内存后面接着分配20个元素的int数组
}
C++的学习笔记持续更新中~
要是文章有帮助的话
就点赞收藏关注一下啦!
感谢大家的观看
欢迎大家提出问题并指正~