[TOC]
# 基本语法
引用是c++对c的重要扩充。在c/c++中指针的作用基本都是一样的,但是c++增加了另外一种给函数传递地址的途径,这就是按引用传递(pass-by-reference),它也存在于其他一些编程语言中,并不是c++的发明。
对一段连续的内存空间只能取一个别名吗?
c++中新增了引用的概念,引用可以作为一个已定义变量的别名
基本语法:
~~~
Type& ref = val;
~~~
注意事项:
* &在此不是求地址运算,而是起标识作用。
* 类型标识符是指目标变量的类型
* 必须在声明引用变量时进行初始化。
* 引用初始化之后不能改变。
* 不能有NULL引用。必须确保引用是和一块合法的存储单元关联。
* 可以建立对数组的引用。
~~~
int main() {
//a,b地址都是相同的,可以有多个引用
int a = 10;
int &b = a;
b = 20; //赋值
cout << "a: " << a << endl;
cout << "b: " << b << endl;
cout << "---------" << endl;
getchar();
return 0;
}
~~~
# 注意事项
**引用必须初始化**
**初始化后就不可以再修改**
~~~
//1) 引用必须初始化
//int& ref; //报错:必须初始化引用
~~~
# 数组引用
**错误的方式**
~~~
//3) 不能对数组建立引用
int arr[10];
//int& ref3[10] = arr;
~~~
**正确的方式**
~~~
int main() {
int arr[10];
for (int i = 0; i < 10; i++)
{
arr[i] = i;
}
//给数组起别名
int(&pArr)[10] = arr;
for (int i = 0; i < 10; i++)
{
cout << pArr[i] << " ";
}
cout << endl;
getchar();
return 0;
}
~~~
**起别名**
~~~
int main() {
int arr[10];
for (int i = 0; i < 10; i++)
{
arr[i] = i;
}
//一个具有10个元素的int类型的数组
typedef int(pArr)[10];
pArr &p1 = arr;
for (int i = 0; i < 10; i++)
{
cout << p1[i] << " ";
}
cout << endl;
getchar();
return 0;
}
~~~
# 不要返回局部变量的引用
~~~
//不要返回局部变量的引用
int& dowork() {
//a是局部变量
int a = 10;
return a;
}
int main() {
int &ret = dowork();
//第一次是编译器优化
cout << ret << endl;
//接下来就可以看到了
cout << ret << endl;
cout << ret << endl;
getchar();
return 0;
}
~~~
# 函数中的引用
**函数当左值,必须返回引用**
最常见看见引用的地方是在函数参数和返回值中。当引用被用作函数参数的时,在函数内对任何引用的修改,将对还函数外的参数产生改变。当然,可以通过传递一个指针来做相同的事情,但引用具有更清晰的语法。
如果从函数中返回一个引用,必须像从函数中返回一个指针一样对待。当函数返回值时,引用关联的内存一定要存在
~~~
void ReferenceSwap(int &m, int &n) {
int temp = m;
m = n;
n = temp;
}
void test() {
int a = 10;
int b = 20;
//值传递
ReferenceSwap(a, b);
cout << "a: " << a << " b: " << b << endl;
}
~~~
通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单:
1. 函数调用时传递的实参不必加“&”符
2. 在被调函数中不必在参数前加“*”符
引用作为其它变量的别名而存在,因此在一些场合可以代替指针。C++主张用引用传递取代地址传递的方式,因为引用语法容易且不易出错
~~~
//返回静态变量的引用
int& show2() {
static int a = 20;
return a;
}
int main() {
int &ref = show2();
show2() = 50; //相当于写a=50
cout << ref << endl;
system("pause");
return EXIT_SUCCESS;
}
~~~
# 引用本质
引用的本质在c++内部实现是一个指针常量
~~~
Type& ref = val; // Type* const ref = &val;
~~~
c++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同,只是这个过程是编译器内部实现,用户不可见
~~~
//发现是引用,转换为 int* const ref = &a;
void testFunc(int& ref){
ref = 100; // ref是引用,转换为*ref = 100
}
int main(){
int a = 10;
int& aRef = a; //自动转换为 int* const aRef = &a;这也能说明引用为什么必须初始化
aRef = 20; //内部发现aRef是引用,自动帮我们转换为: *aRef = 20;
cout << "a:" << a << endl;
cout << "aRef:" << aRef << endl;
testFunc(a);
return EXIT_SUCCESS;
}
~~~
# 常量引用
常量引用的定义格式:
~~~
const Type& ref = val;
~~~
常量引用注意:
* 字面量不能赋给引用,但是可以赋给const引用
* const修饰的引用,不能修改。
~~~
void test01(){
int a = 100;
const int& aRef = a; //此时aRef就是a
//aRef = 200; 不能通过aRef的值
a = 100; //OK
cout << "a:" << a << endl;
cout << "aRef:" << aRef << endl;
}
void test02(){
//不能把一个字面量赋给引用
//int& ref = 100;
//但是可以把一个字面量赋给常引用
const int& ref = 100; //int temp = 200; const int& ret = temp;
}
~~~
# 指针引用
在c语言中如果想改变一个指针的指向而不是它所指向的内容,函数声明可能这样:
~~~
void fun(int**);
~~~
给指针变量取一个别名。
~~~
Type* pointer = NULL;
Type*& = pointer;
Type* pointer = NULL; Type*& = pointer;
~~~
~~~
struct Teacher{
int mAge;
};
//指针间接修改teacher的年龄
void AllocateAndInitByPointer(Teacher** teacher){
*teacher = (Teacher*)malloc(sizeof(Teacher));
(*teacher)->mAge = 200;
}
//引用修改teacher年龄
void AllocateAndInitByReference(Teacher*& teacher){
teacher->mAge = 300;
}
void test(){
//创建Teacher
Teacher* teacher = NULL;
//指针间接赋值
AllocateAndInitByPointer(&teacher);
cout << "AllocateAndInitByPointer:" << teacher->mAge << endl;
//引用赋值,将teacher本身传到ChangeAgeByReference函数中
AllocateAndInitByReference(teacher);
cout << "AllocateAndInitByReference:" << teacher->mAge << endl;
free(teacher);
}
~~~
对于c++中的定义那个,语法清晰多了。函数参数变成指针的引用,用不着取得指针的地址。
# 使用场景
常量引用主要用在函数的形参,尤其是类的拷贝/复制构造函数。
将函数的形参定义为常量引用的好处:
* 引用不产生新的变量,减少形参与实参传递时的开销。
* 由于引用可能导致实参随形参改变而改变,将其定义为常量引用可以消除这种副作用。
如果希望实参随着形参的改变而改变,那么使用一般的引用,如果不希望实参随着形参改变,那么使用常引用。
~~~
//const int& param防止函数中意外修改数据
void ShowVal(const int& param){
cout << "param:" << param << endl;
}
~~~
- c语言
- 基础知识
- 变量和常量
- 宏定义和预处理
- 随机数
- register变量
- errno全局变量
- 静态变量
- 类型
- 数组
- 类型转换
- vs中c4996错误
- 数据类型和长度
- 二进制数,八进制数和十六进制数
- 位域
- typedef定义类型
- 函数和编译
- 函数调用惯例
- 函数进栈和出栈
- 函数
- 编译
- sizeof
- main函数接收参数
- 宏函数
- 目标文件和可执行文件有什么
- 强符号和弱符号
- 什么是链接
- 符号
- 强引用和弱引用
- 字符串处理函数
- sscanf
- 查找子字符串
- 字符串指针
- qt
- MFC
- 指针
- 简介
- 指针详解
- 案例
- 指针数组
- 偏移量
- 间接赋值
- 易错点
- 二级指针
- 结构体指针
- 字节对齐
- 函数指针
- 指针例子
- main接收用户输入
- 内存布局
- 内存分区
- 空间开辟和释放
- 堆空间操作字符串
- 内存处理函数
- 内存分页
- 内存模型
- 栈
- 栈溢出攻击
- 内存泄露
- 大小端存储法
- 寄存器
- 结构体
- 共用体
- 枚举
- 文件操作
- 文件到底是什么
- 文件打开和关闭
- 文件的顺序读写
- 文件的随机读写
- 文件复制
- FILE和缓冲区
- 文件大小
- 插入,删除,更改文件内容
- typeid
- 内部链接和外部链接
- 动态库
- 调试器
- 调试的概念
- vs调试
- 多文件编程
- extern关键字
- 头文件规范
- 标准库以及标准头文件
- 头文件只包含一次
- static
- 多线程
- 简介
- 创建线程threads.h
- 创建线程pthread
- gdb
- 简介
- mac使用gdb
- setjump和longjump
- 零拷贝
- gc
- 调试器原理
- c++
- c++简介
- c++对c的扩展
- ::作用域运算符
- 名字控制
- cpp对c的增强
- const
- 变量定义数组
- 尽量以const替换#define
- 引用
- 内联函数
- 函数默认参数
- 函数占位参数
- 函数重载
- extern "C"
- 类和对象
- 类封装
- 构造和析构
- 深浅拷贝
- explicit关键字
- 动态对象创建
- 静态成员
- 对象模型
- this
- 友元
- 单例
- 继承
- 多态
- 运算符重载
- 赋值重载
- 指针运算符(*,->)重载
- 前置和后置++
- 左移<<运算符重载
- 函数调用符重载
- 总结
- bool重载
- 模板
- 简介
- 普通函数和模板函数调用
- 模板的局限性
- 类模板
- 复数的模板类
- 类模板作为参数
- 类模板继承
- 类模板类内和类外实现
- 类模板和友元函数
- 类模板实现数组
- 类型转换
- 异常
- 异常基本语法
- 异常的接口声明
- 异常的栈解旋
- 异常的多态
- 标准异常库
- 自定义异常
- io
- 流的概念和类库结构
- 标准io流
- 标准输入流
- 标准输出流
- 文件读写
- STL
- 简介
- string容器
- vector容器
- deque容器
- stack容器
- queue容器
- list容器
- set/multiset容器
- map/multimap容器
- pair对组
- 深浅拷贝问题
- 使用时机
- 常用算法
- 函数对象
- 谓词
- 内建函数对象
- 函数对象适配器
- 空间适配器
- 常用遍历算法
- 查找算法
- 排序算法
- 拷贝和替换算法
- 算术生成算法
- 集合算法
- gcc
- GDB
- makefile
- visualstudio
- VisualAssistX
- 各种插件
- utf8编码
- 制作安装项目
- 编译模式
- 内存对齐
- 快捷键
- 自动补全
- 查看c++类内存布局
- FFmpeg
- ffmpeg架构
- 命令的基本格式
- 分解与复用
- 处理原始数据
- 录屏和音
- 滤镜
- 水印
- 音视频的拼接与裁剪
- 视频图片转换
- 直播
- ffplay
- 常见问题
- 多媒体文件处理
- ffmpeg代码结构
- 日志系统
- 处理流数据
- linux
- 系统调用
- 常用IO函数
- 文件操作函数
- 文件描述符复制
- 目录相关操作
- 时间相关函数
- 进程
- valgrind
- 进程通信
- 信号
- 信号产生函数
- 信号集
- 信号捕捉
- SIGCHLD信号
- 不可重入函数和可重入函数
- 进程组
- 会话
- 守护进程
- 线程
- 线程属性
- 互斥锁
- 读写锁
- 条件变量
- 信号量
- 网络
- 分层模型
- 协议格式
- TCP协议
- socket
- socket概念
- 网络字节序
- ip地址转换函数
- sockaddr数据结构
- 网络套接字函数
- socket模型创建流程图
- socket函数
- bind函数
- listen函数
- accept函数
- connect函数
- C/S模型-TCP
- 出错处理封装函数
- 多进程并发服务器
- 多线程并发服务器
- 多路I/O复用服务器
- select
- poll
- epoll
- epoll事件
- epoll例子
- epoll反应堆思想
- udp
- socket IPC(本地套接字domain)
- 其他常用函数
- libevent
- libevent简介