[TOC]
7.1 命名空间
工程中会出现同名重定义问题,为避免多个 .cpp
链接时报错,引入命名空间的概念。
命名空间:利用命名空间,在C++程序中划分出来的一块比较大的程序区段。在该程序区段内部,可以定义类型,函数,模版,变量。
1 | namespace A{ |
在不同命名空间中的变量会有各自的程序区段,故不会发生编译错误
7.1.1 匿名命名空间
- 在匿名空间中声明的变量和函数,都不会暴露在其他编译单元中
1 | namespace{ |
7.1.2 命名空间使用
全局作用域符:::
命名空间作用域符:命名空间::name
类作用域符:类名::name
同一命名空间的成员会进行合并
7.1.3 命名空间中变量的声明
1 | namespace A{ |
using namespace 命名空间标识符;
将命名空间A 中的全部内容引入到所在作用域中
1 | void fun(){ |
namespace::成员标识符
1 | void fun(){ |
7.2 标识符的作用域与可见性
作用域:规定标识符的有效范围
可见性:标识符是否可被使用
7.2.1 作用域
函数原型作用域
程序中最小的作用域
生存周期最短:从形参定义处到原型声明结束
1 | int fun(int i); |
i为参数,其作用域开始于 (
,结束于 )
,i
不可用于程序的其他地方
函数原型声明并不关心形参名,等价于
1 | int fun(int); |
局部作用域
从定义开始,到
}
为止或者遇到return
为止
块指的是用一对花括号括起来的代码区域,花括号括起来就是块
1 | void fun(int arg){ |
块作用域的范围是从变量的定义处到包含该定义所在的块末尾
- arg:作用域为函数块
- x:作用域为函数体
- i:作用域为for循环块
- p:作用域为函数体内
局部作用域中新声明的变量会覆盖全局作用域中的同名变量
类作用域
每个类都有自己作用域,一个类就是一个作用域
在类外定义的成员函数必须同时提供类名和函数名(指定特定的类作用域)
1 | class A{ |
在类作用域外,普通数据成员和函数成员只能通过对象、引用或指针访问
访问控制权限为
public
的成员,在类外访问1
2
3
4成员:
对象.公有成员;
对象::公有成员;
ptr->公有成员
类的编译顺序
- 从上到下一次编译变量,类型别名,函数声明及返回值和形参
- 当上一步完成才编译函数成员的函数体
所以,
- 类中重命名尽量放在类的最开始处
- 函数成员可以访问所有的数据成员
类中变量名的查找顺序
1 | typedef int Height; |
变量名的查找顺序:从包含该变量的最小作用域开始查找,找不到则迭代向上层查找,直至全局作用域
- 成员函数中的局部变量
- 类变量
- 全局变量
命名空间作用域
- 命名空间作用域可以跨越多个
*.cpp
文件而存在。 - 命名空间作用域是可以互相嵌套的
全局作用域
作用域范围:从定义开始到源文件结束,在整个函数执行期间保持有效
生命周期:程序一启动就会分配存储空间,直到程序退出,由系统回收内存空间
存储位置:静态存储区
1 |
|
全局变量只能被定义一次
全局变量的内存分配是在编译过程中完成的,在程序的整个执行过程都要占用存储空间,而不是在需要的时候才开辟内存空间
7.2.2 可见性
程序运行到某一点,能够引用的标识符
标识符声明在前,引用在后
在同一作用域中,不能声明同名标识符
两个或多个具有包含关系的作用域中声明了同名标识符,外层标识符在内层不可见
可见性 :局部作用域 > 类作用域 > 命名空间作用域
7.3 对象的生存周期
7.3.1 静态生存周期
对象的生存期与程序的生存周期相同
加 static
使局部变量具有静态生存周期
当一个函数返回后,下次调用时,该变量的值保持不变
- 默认初值为
0
类的静态成员
静态成员是解决同一类的不同对象之间的成员(数据和函数)的共享问题
静态数据成员——类属性
静态成员在每个类中只有一个副本,由本类的所有对象共同维护
可通过 类名::标识符
访问
累的静态数据成员在类外定义和初始化,需要为其分配单独的内存空间
1 | class Point{ |
静态函数成员
静态函数成员可以直接访问该类的静态数据成员和函数成员
对非静态成员的访问需要通过对象
可通过类名和对象名调用,一般约定用类名调用
1 | class Point{ |
动态生存周期
从声明开始,到所在块执行完成时结束
1 |
|
7.4 类的友元
友元:提供了不同类的成员函数之间,类的成员函数与一般函数之间进行数据共享的机制
7.4.1 友元函数
类中通过 friend
关键字修饰非成员函数
在类外可以通过对象名访问类的私有和保护成员
1 | class Point{ |
7.4.2 友元类
若A类为B类的友元类,则A类中的所有成员函数都是B类的友元函数,都可以访问B类的私有和保护成员
- 友元关系是单向的
- 友元关系不能传递
- 友元关系不能被继承
7.5 共享数据的保护
7.5.1 常对象
常对象必须被初始化,不能被更新
常量对象只能调用类的常量成员函数,不能调用非常量成员函数
1 | class A{ |
7.5.2 常对象成员
常成员函数
类型说明符 函数名(参数列表) const;
1 | class R{ |
常成员函数可以引用 const
数据成员,也可以引用非 const
的数据成员,但不能修改数据成员的值
- 常成员函数不能调用另一个非常成员函数
1 |
|
被 mutable
修饰的数据成员可以被常成员函数修改
mutable
修饰的常成员对象不会被视为常成员
1 |
|
7.5.3 常数据成员
常数据成员只能通过构造函数赋值
- 所有构造函数都需初始化常成员函数
类成员的常量和静态变量都应在类作用域外定义
- 特例:类的静态常量如果是整型或枚举,则可以直接在类中定义
1 | class A{ |
7.5.4 常引用
const 类型说明符 &引用名
- 被引用的对象不能被更新
一个常引用,无论绑定普通对象还是常对象,通过该引用访问该对象时都将该对象看做常对象
- 对于基本数据类型的常引用,不能修改值
- 对于类的常引用,不能修改类的数据成员,也不能调用非
const
成员函数
7.6 多文件结构和编译预处理指令
7.6.1 C++程序的一般组织结构
- 类定义文件
*.h
- 类实现文件
*.cpp
- 工程文件
*.cpp
决定一个声明放在源文件还是头文件的原则:
需要分配内存的定义在源文件中
不需要分配内存的放在头文件
内联函数放在头文件——使多个源文件可见
文件的引用
- 自定义文件
""
- 库函数
<>
7.6.2 外部变量和外部函数
外部变量
外部变量是多个源文件可见的全局变量
1 | //源文件1 |
定义性声明和引用性声明
定义性声明:在声明的同时定义的外部变量为定义性声明
在命名空间中不使用 extern
声明的变量,都是定义性声明,即不引用外部变量
用 extern
声明,同时定义了初值,为定义性声明,否则为引用性声明
外部函数
所有在类外声明的非成员函数,都具有全局命名空间作用域,只要在调用前进行引用性声明即可
7.6.3 编译预处理
#include
指令
将指定源文件嵌入到当前源文件的该点
# include<文件名>
:待引用源文件位于 include 子目录中
# include "文件名"
:先在当前目录搜索,如果没有,再按照标准搜索
条件编译指令
1 |
|
1 |
|
1 |
|
1 |
|
#define
和 #undef
用 #undef
删除由 #define
定义的宏,使之不再起作用
1 |
|
1 | //main.cpp |
7.6.4 标准C++库
- 输入输出类
- 容器类和ADT
- 存储管理类
- 算法
- 错误管理
- 运行环境支持