引用
引用不是新定义一个变量,而是给已存在的变量取一个别名,译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
在语法上,一定要认为引用没有开空间,目前先不用考虑底层上
引用的语法为:类型& 引用变量名(对象名) = 引用实体;
引用类型必须和引用实体是同种类型的
int main() { int a = 10; int& b = a;//定义引用类型 }
b是a的引用,a和b本质上是一个变量,它们公用一个空间,改变任意一个另一个都会改变
引用时必须进行初始化
int main() { int a = 10; int& b;//引用时需要初始化,这样写是错误的 }
一个变量可以有多个引用
int main() { int a = 10; int& b = a; int& c = b; }
b是a的·引用,c又是b的引用,所以b和c都是a的引用,它们三个共用一个空间
引用一旦引用了一个实体,不能再引用另一个实体
int main() { int a = 10; int x = 5; int& b = a;//b已经是a的引用了 b = x;//这里不会改变b引用的实体,只是把x的值拷贝给了b,b还是a的引用 }
引用的使用
引用做参数
1.引用做参数,常用于输出型参数
输出型参数就是在函数内部改变,并且在函数外部还能拿到的参数,其实也可以成为“形参的改变可以影响实参”
例如交换两个变量值,这2个变量在后序程序中还会用到,所以是输出型变量,在一些关于数组的OJ题中,一般函数参数都有一个表示数组大小的变量numSize,这个形参也是输出型参数
下面通过引用实现一个swap函数
void swap(int& x, int& y) { int tmp = x; x = y; y = tmp; }
再对比一下原先在C语言中的写的swap:
void swap(int* x, int* y) { int tmp = *x; *x = *y; *y = tmp; }
可以看到用引用最直接的一个点就是不需要再传指针了
以往的swap函数中,为了在函数中能够改变要交换的2个数的值,只能传指针,否则就是形参的改变不会影响实参
在C++中,传引用,形参就是实参的别名,这样形参的改变就可以改变实参
2.引用做参数,可以提高效率,尤其是对于深拷贝的类对象和大对象
以值作为函数传参,在传参过程中,函数不会直接传递实参,而是传递实参的一份临时拷贝,以这个临时拷贝作为函数的形参,在值传递的过程中有拷贝操作,效率低下
在这个过程中,如果实参是深拷贝的类对象或者是大对象,那么它们的拷贝就会大大降低效率
所以,可以用引用做参数,在传参的过程中,没有拷贝的操作,形参就是实参的一个别名,这样能大大地提高效率
需要注意的一点:引用传参和指针传参效率一样,不存在较大的效率差异
下面我们看一下值传参和引用传参的效率差异:
struct A { int a[10000]; }; void func1(A a){}//值传参 void func2(A&a){}//引用传参 int main() { A a;//大对象 int BeginTime1 = clock(); for (size_t i = 0; i < 10000; ++i) func1(a); int EndTime1 = clock(); int BeginTime2 = clock(); for (size_t i = 0; i < 10000; ++i) func2(a); int EndTime2 = clock(); cout << "值传参:" << EndTime1 - BeginTime1 << endl; cout << "引用传参:" << EndTime2 - BeginTime2 << endl; }
可以看到,引用传参消耗的时间明显少于值传参
引用做返回值
在学习引用做返回值之前,要了解一下返回return的作用机制:
在函数中,return了一个值或变量后,并不会直接返回,在中间会生成临时变量,最后返回的值其实是临时变量
在这个过程中,会有拷贝操作,拷贝会导致效率低
为什么要设置临时变量? 如果返回值的作用域是这个函数,函数栈帧销毁后,返回值也被销毁了
所以为了解决这个问题,我们可以引用做返回值:
int& func() { static int n = 0; n++; return n; } int main() { int ret = func(); return 0; }
传引用返回,中间就不会生成临时变量了,可以减少拷贝,提高效率
从下图就可以看出,用引用返回可以减少一次拷贝
下面是用一个引用去接受引用返回:
int& func() { static int n = 0; n++; return n; } int main() { int& ret = func(); return 0; }
这里我们用一个引用接收了引用返回,ret其实就是n的别名
这样可以再减少一次拷贝,还能提高效率
这里需要注意的是,想要使用引用返回,那么要返回的值不应该随着函数栈帧的销毁而销毁,换而言之,返回的变量应该是储存在静态区或者是动态开辟出来的,从上面的代码可以看出,里面返回的n都是用static修饰过的,如果是局部对象的话,会有问题