头文件 |
- 包含内容
- 使用#define或const定义的符号常量
- 函数原型
- 结构体声明
- 类声明
- 模板声明
- 内联函数
- 头文件的作用
- 对包含头文件的源代码文件(.cpp)进行单独编译时,预处理器将其与源文件合并
- 从而创建临时文件(.cpp)
使用
- 系统头文件用
#include <XXX.h>
- 用户头文件用
#inllude "XXX.h"
使用条件编译防止多次包含头文件
#ifndef XXX_H #define XXX_H ... #endif
#pragma once ...
- 系统头文件用
简述 |
- 存储持续性
- 表示变量在内存中存在的时间
- 自动存储持续性 – 在执行其属函数或代码块时创建,执行完后其内存被释放
- 在函数中声明的变量 – 包括参数
- 在代码块中被声明的变量 – 局部变量
- 静态存储持续性 – 在程序整个运行过程中都存在
- 在函数和代码块外定义的变量 – 全局变量
- 在函数内使用static定义的变量
- 线程存储持续性(C++11) – 生存周期同所属线程一样长
- 使用local_thread声明
- 动态存储持续性 – 直到使用 delete 释放其内存 或 程序运行结束
- 使用 new 分配内存的变量
- 作用域
- 描述名称在文件(翻译单元)的可见范围
- 局部作用域 – 只在定义它的代码块内可用
- 自动变量
- 全局作用域 – 在定义位置到文件结尾都可用
- 静态变量
- 函数原型作用域 – 在函数原型作用域中使用的名称只在包含参数表的括号内可用 – 可省略
- 类作用域 – 在定义它的类中可用
- 名称空间作用域 – 在定义它的名称空间中可用 – 全局作用域为其一特例
- 链接性
- 描述名称在不同单元之间共享的方式
- 外部链接性 – 可在文件中共享
- 内部链接性 – 只能由其所在文件中的函数共享
- 无链接性 – 不能共享 – 如自动变量
自动变量 |
- 自动变量一般都是 作为局部的变量 且没有链接性
- auto原用于显式地指出变量为自动存储 – C++11已经取消此用法
- 局部变量可隐藏外部的变量(或外层的局部变量)
- 全局变量若不想被隐藏,可使用域解析符(::)
- 可使用任何在声明时已知的表达式来初始化自动变量
- 自动变更使用栈来简化实现
- 寄存器变量
- 使用 register 关键字声明
- 它寻底编译器使用寄存器来存储自动变量 – 旨在提高速度 – 目前已经淘汰
- 目前已经淘汰,register 关键字只显式说明变量是自动的
静态持续变量的链接性 |
- 外部链接性
- 直接在代码块外声明即可
- 有两种声明方式
- 定义声明 – 会给变量分配内存 – 直接声明
- 引用声明 – 不给变量分配内存(引用已有变量 – 其它文件中的)
- 使用 extern 关键字 且 不初始化 – 若初始化了,则还是定义声明
- 一般只在一个文件中使用定义声明,而在其他文件中使用引用声明
- 内部链接性
- 直接在代码块外声明, 并在前面加上 static 关键字即可
- 此时 static 关键字表示为变量的内部链接性 (变量已经是静态的了)
- 名称相同时,内部链接性的静态变量将隐藏常规的外部变量
- 直接在代码块外声明, 并在前面加上 static 关键字即可
- 无链接性
- 在代码块内声明,并加个 static 关键字
- 此时 static 关键字表示为变量的存储持续性为静态 (变量已经是无链接性的了)
- 这又叫做 关键字重载
- 在代码块内声明,并加个 static 关键字
说明符和限定符 |
c-v限定符
const – 不能修改的内存
- const 对全局变量的链接性有影响 – const全局变量的链接性为内部的
- 这也是const变量能写在头文件而没有多重定义的原因
若想使用链接性为外部的const变量,则可以再在前面加extern进行定义,但其他文件使用此变量必须也要用extern关键字进行声明
extern const int maxn = 100;
- volatile – 易变的
- 即使程序代码没有对内存单元进行修改,其值也可能发生变化(可能受硬件影响)
- 如果不使用volatile变量,在再次访问之间编译器可能会认为值没有变化,从而进行优化(如将值放在寄存器中缓存起来)
- 使用volatile变量相当于告诉编译器,不要进行这种优化
存储说明符
- auto – C++11后不再是说明符
- register – C++11后表示为显式说明为自动变量
- static – 内部链接 静态
- extern – 引用声明
- thread_local – (C++11) 线程的静态变量
mutable
- 可用它来指出,即使结构体(或类)变量为const, 其某个成员也是可以被修改的
struct node { int x; mutable int y; }; const node n { 1, 2}; n.x = 3; // 错误, 不可修改 n.y = 4; // 正确, y为mutable变量
函数的链接性 |
- 函数默认为外部静态的 即默认是extern的
- 可使用 static 关键字将其链接性设置为内部的
- 必须同时在原型和定义中使用static
- 内部静态函数将覆盖外部函数
语言链接性
- C语言对函数名的矫正使用的约定与C++不同, 可用函数原型指出其使用的约定
extern "C" void fun(int); // 使用C语言的约定 extern void fun(int); // 使用C++的约定 extern "C++" void fun(int); // 使用C++的约定 -- 显式指出
内联函数不受单定义规则约束 – 所以可以放在头文件中
动态内存分配 |
new 运算符
内置的标量类型 – 可以加括号分配内存并初始化 – 也适用于有合适构造函数的类
int* p = new int(5); // 初始化 *p 的值为5
初始化结构体或数组 – 使用大括号的列表进行初始化(C++11)
node *p = new node {1.0, 2.0}; // node 为结构体 int * p = new int[4] {1, 2, 3, 4}; // 初始化数组
- new 失败时
- 返回空指针 – 旧
- 引发 std::bad-alloc 异常 – 第15章
new 运算符调用如下函数 – 分配函数
void* operator new(std::size_t); // new void* operator new[](std::size_t,); // new[]
- delete相有相应的释放函数
定位 new 运算符
- new 负责在堆上找一块满足要求的内存块
- 定位new 能指定要使用的位置
- 设置内存管理规程,处理需要通过特定地址进行访问的硬件,在特定位置创建对象等
使用
#include <new> char buff[50]; int* p = new (buff) int[20]; // 从buff中分配一个包含20个int的数组
- 不能使用delete来释放使用定位new分配的内存
- 定位new 相当于将分配内存的重任交给了程序员
定位new 的分配函数
void* operator new(std::size_t, void* position);
名称空间 |
一些概念
- 声明区域
- 可以在其中声明的区域
- 全局变量的声明区域为其声明所在的文件,局部变量为其代码块
- 潜在作用域
- 从声明点开始,到其声明区域的结尾
- 作用域
- 变量对程序而言可见的的范围
- 潜在作用域可能会被嵌套的声明区域隐藏 – 如局部变量隐藏全局变量
- 声明区域
新特性 – 可命名的名称空间
- 名称空间可以是全局的, 也可以位于另一个名称空间中,但不能位于代码块中
- 默认在名称空间中声明的名称的链接性为外部的(除了const)
- 全局名称空间 – 对应于文件级声明区域, 即之前的全局变量所在空间
- 名称空间是开放的, 即可在不同的地方声明同一个名称空间,整个名称空间为累计起来的结果
- 访问名称空间中名称的方法 – 使用作用域解析运算符 –
::
namespace CE { int x; double y; } cout << CE::x << endl;
using 声明 和 using 编译指令 – 简化名称的使用
using 声明 – 使特定的标识符可用
using CE::x;
- 其实质是将特定的名称添加到它所属的声明区域中
- 如果该声明区域中已经 存在同名变量,则会报错
using 编译指令 – 使该名称空间的所有名称都可用
using namespace CE;
- 可在全局使用,也可在代码块中使用
- 使用此编译指令时,局部名称会隐藏名称空间名称
- < iostream > 与 < iostream.h > 的区别是前者支持名称空间
using namespace CE; // 如果使用using CE::x; 则会报错 int x = 2; cout << x << endl; // 局部变量x -- 将隐藏CE中的x cout << CE::x << endl; // 名称空间中的x
其它特性
- 名称空间是可嵌套的
- using 编译指令是可传递的
可以给名称空间创建别名 – 常用于简化嵌套名称空间的使用
namespace cloud_engine = CE;
- 可以使用未命名的名称空间
- 其作用域为整个文件 – 使用时不用加域解析运算符
- 相当于内部链接的全局静态变量(使用static关键字)
使用名称空间的指导规则
- 使用在已命名的名称空间中声明的变量,而不是使用外部全局变量
- 使用在已命名的名称空间中声明的变量,而不是使用静态全局变量
- 如果开发了一个函数库或类库,将其放在一个名称空间中
- 仅将编译指令using作为一各路将旧代码转换为使用名称空间的权宜之计
- 不要在头文件中使用using编译指令
- 掩盖了要让哪些名称可用
- 包含头文件的顺序可能影响程序的行为 – 应放在所有的
#include
之后
- 导入名称时,首选使用作用域解析运算符 或 using声明 的方法
- 对于 using声明, 首选将其作用域设置为局部而不是全局