指针和引用
2023年11月21日 2023年12月31日
绑定
将某个名字绑定到给定对象, 使用该名字即使用绑定对象
对象也称作实体
1int i = 5, &ri = i; 2cout << &i << endl << &ri << endl; // 二者地址相同
变量
变量是具名的, 可通过变量读写对象
变量不一定拥有内存空间, 但一定对应一个明确对象:
- 指针本身是对象; 指针还指向另一个对象
- 引用是变量但不是对象; 引用绑定一个对象
变量不一定是具体类型: 引用是变量, 不是一个具体类型
指针和引用
通过指针和引用可以间接访问对象
-
使用类型修饰符声明引用和指针,使用基本类型描述间接访问对象的类型
类型修饰符 *
指针 &
左值引用 &&
右值引用 1T var; 2 3T *p = &var, &r = var;
-
用来间接访问对象
- 指针对象保存所指对象的地址; 形容为指向该对象
- 引用绑定对象, 是对象的别名; 形容为绑定该对象
- 指针对象保存所指对象的地址; 形容为指向该对象
-
指针是具体类型,引用不是
-
指针本身也是一个对象, 引用是一个变量
指针
是一个对象
类型修饰符 *
的前半部分描述指向的对象,后半部分描述指针
固定大小,和机器字长一致
1T var, *p = &var; // 类型修饰符*指示p是一个指针, T为指向对象类型 2*p; // 返回指向对象的引用
定义空指针
空指针不指向任何对象
不允许对空指针解引用, 不允许通过空指针访问成员: 编译器会报错
1#include <cstdlib> // NULL是预处理变量,不属于命名空间std;建议避免使用 2 3int *p5 = NULL; 4int *p4 = 0; 5int *p3 = nullptr; // 推荐使用
定义多个指针
要求基本类型一致,需要对每个指针使用类型修饰符 *
1int *ip1, *ip2;
*ip1构成一个声明符, *ip2构成另一个声明符
int是ip1和ip2共同的基本类型
取地址运算符
&
单目运算符
获取某个对象的地址
解引用运算符
*
单目运算符
获取所指对象的引用
成员访问运算符
->
双目运算符
访问所指对象的数据成员
void *指针
可以存放任意类型对象的地址
不能直接操作void *指针指向的对象,对象类型未知
1void *p;
引用
对象的别名
有两种,左值引用和右值引用
是变量, 不是对象: 不拥有内存空间
对引用读写,实则读写其绑定的对象
声明时必须显式初始化,将标识符与对象进行绑定;绑定关系不可更改
使用引用可以避免拷贝带来的开销: 调用函数时,如果 Pass-by-reference-to-const
, 可以避免拷贝和修改绑定对象; 而 Pass-by-value
,会调用拷贝/移动构造函数创建实参的副本
左值引用
lvalue reference
绑定一个左值表达式
左值引用的基本类型和被绑定对象类型必须严格匹配
1T var, &refVar = var;
示例
1#include <iostream> 2 3using std::cout; 4using std::endl; 5 6int &fcn(int &a) 7{ 8 return ++a; 9} 10 11int main() 12{ 13 int a = 5; 14 int &r = ++fcn(a); 15 cout << a << endl; 16 return 0; 17}
输出
7
右值引用
rvalue reference
绑定右值表达式
接管了引用对象的资源,之后可通过右值引用对该对象进行读写
示例
1#include <iostream> 2 3int fcn(int a) { return a + 1; } 4 5int main() 6{ 7 int a = 5; 8 int &&r = fcn(a); 9 const int &cr = r; // 具有底层const的左值引用也可以绑定右值 10 r = 20; 11 std::cout << cr << std::endl; 12 return 0; 13}