一.引用概念
引用
不是新定义一个变量,而
是给已存在变量取了一个别名
,编译器不会为引用变量开辟内存空间,它和它 引用的变量共用同一块内存空间。
类型
&
引用变量名
(
对象名
) =
引用实体,比如:
int main()
{
int a = 10;
int& b = a;//引用取别名,定义引用类型
int* p = &b;//取地址
}
二.引用需要注意的问题
1.引用在定义时必须初始化
//1.引用在定义时必须初始化
int a = 10;
int& b = a;
2.一个变量可以有多个引用
//2.一个变量可以有多个引用
int a = 10;
int& b = a;
int& c = a;
int& d = b;
3.引用一旦引用一个实体,再不能引用其他实体,与指针区分开![](https://i-blog.csdnimg.cn/blog_migrate/0e5ab76eac64a350c4f55a5e133959e3.png)
三.引用的应用
1.引用做参数
引用的价值
做参数 -- a、提高效率 b、形参的修改,可以影响实参(输出型参数)
// 1、引用做参数(总结有三种传参方式)
// _Z4swappipi
void swap(int* p1, int* p2) // 传地址
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
// _Z4swapriri
void swap(int& r1, int& r2) // 传引用
{
int tmp = r1;
r1 = r2;
r2 = tmp;
}
// _Z4swapii
void swap(int r1, int r2) // 传值
{
int tmp = r1;
r1 = r2;
r2 = tmp;
}
// 他们三个构成函数重载
// 但是swap(x, y);调用时存在歧义,他不知道调用,传值还是传引用
// 类似以前讲的这个,他们构成重载,但是存在歧义
// void f();
// void f(int x = 0, int y = 0);
2.指针的引用
3.引用作返回值
先说传值返回,传值返回,会产生一个临时变量。然后将返回值拷贝给临时变量,(这个临时变量是常量,不可修改,但可以被读取,都相当于右值)再将临时变量拷贝给调用函数。
再说传引用返回,引用返回的意思就是不会生成c的拷贝返回,直接返回c的引用
注意:如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。比如
在下面这个程序中,使用引用返回,会有非常大的优势
#include <time.h>
struct A{ int a[10000]; };
A a;
// 值返回 -- 每次拷贝40000byte
A TestFunc1() { return a; }
// 引用返回 -- 没有拷贝
A& TestFunc2(){ return a; }
void TestReturnByRefOrValue()
{
// 以值作为函数的返回值类型
size_t begin1 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc1();
size_t end1 = clock();
// 以引用作为函数的返回值类型
size_t begin2 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc2();
size_t end2 = clock();
// 计算两个函数运算完成之后的时间
cout << "TestFunc1 time:" << end1 - begin1 << endl;
cout << "TestFunc2 time:" << end2 - begin2 << endl;
}
引用作返回值有两大优势,一个是前面说的提高效率,还有一个就是可以修改返回变量,如下所示
int& At(int i)
{
static int a[N];
return a[i]; // 返回的是a[i]数组的别名
}
int main()
{
int ret = Add(1, 2);
for (size_t i = 0; i < N; ++i)
{
At(i) = 10+i;
}
for (size_t i = 0; i < N; ++i)
{
cout << At(i) << " ";
}
cout << endl;
return 0;
}
总结一下:
引用的作用主要体现在传参和传返回值
1、引用传参和传返回值,有些场景下面,可以提高性能。(大对象+深拷贝对象)
2、引用传参和传返回值,输出型参数和输出型返回值。通俗点说,有些场景下面,形参的改变可以改变实参。有些场景下面,引用返回,可以改变返回对象。
四.常引用
// 假设x是一个大对象或者是后面学习深拷贝的对象
// 那么尽量用引用传参,减少拷贝。如果f函数中不改变x
// 建议尽量用cosnt引用传参,因为传过来的值相当于权限缩小了
//void f(int& x)
void f(const int& x)
{
cout << x << endl;
}
// 常引用
int main()
{
// 权限放大 不可以
//const int a = 10;
//int& b = a;
// 权限不变 可以
const int a = 10;
const int& b = a;
// 权限的缩小 可以
int c = 10;
const int& d = c;
f(a);
f(c);
f(10);
return 0;
}
六.引用和指针的区别
1.
引用
在定义时
必须初始化
,指针没有要求
2.
引用
在初始化时引用一个实体后,就
不能再引用其他实体
,而指针可以在任何时候指向任何一个同类型
实体
3.
没有
NULL
引用
,但有
NULL
指针
4.
在
sizeof
中含义不同
:
引用
结果为
引用类型的大小
,但
指针
始终是
地址空间所占字节个数
(32
位平台下占
4
个字节
)
5.
引用自加即引用的实体增加
1
,指针自加即指针向后偏移一个类型的大小
6.
有多级指针,但是没有多级引用
7.
访问实体方式不同,
指针需要显式解引用,引用编译器自己处理
8. 引用比指针使用起来相对更安全,指针要考虑空指针,野指针等等问题,指针太灵活了,所以相对而言,没有引用安全