引用的一般使用场景:
分为:做 性参 和 返回值
做形参:
就好像是我们前面写的 那两个swap函数一样 再来就是:
大家都知道我们的C语言的 scanf("%d", &a ); 那这里的取地址操作就是因为我们的scanf函数回去我们缓冲区里面区提取数据 如果直接给给 a 这样是肯定不行的 这里的 &a 就是scanf函数的参数 我们传 a 的地址进去 在里面对a的地址解引用改变a 这样对a进行的改变才会影响到我们传进去的参数(实参)
那现在有了我们的引用操作还需要这样嘛
我们利用引用传参 的效率是比 我们直接传值 的效率要高的
因为 我们以值作为参数或者返回类型的时候,在开始的传参 和 返回期间 函数不会直接传递实参或者直接把返回值直接返回 而是会传递实参的临时拷贝或者是返回变量的临时拷贝 如果这样的话 那我们的效率可想而知是很低下的 并且如果我们参数或者返回值类型很大时 效率会更低 就好比是 结构体类型
做返回值:
大家看这样的结果是什么
int test() { static int n=0; n++; return n; } int main() { cout<<test()<<endl; cout<<test()<<endl; cout<<test()<<endl; }
答案: 1 2 3 这里大家要注意一个点就是我们的 static 语句是只执行一次的 之所以提这个我们在了解引用做返回值之前 我们要理清楚值返回的特性
那有什么办法可以证明 临时变量 的存在呢? 大家看下面的代码:
int test() { int n=0; n++; return n; } int main() { int& a=test(); }
大家觉得上面这段代码 编译器能编译通过嘛? 大家试过应该知道这里是会报错的 正确的代码应该是这样的:
int test() { int n=0; n++; return n; } int main() { const int& a=test(); }
这样我们的编译器才不会报错 也就暗示我们传返回值回来的时候 产生的临时变量是常性的(也就是被const修饰的)
那如果是这样呢?
int& test() { int n=0; n++; return n; } int main() { int& a=test(); }
我们这里使用传引用返回又是什么意思呢 ?
我们既然返回的是一个引用 那这个引用是谁的引用呢 我们这里写得是 return n 不就是返回n的引用嘛 那既然我返回的是 n 的引用(返回n的别名) 我们用 int& a 来接收 这样我们不就得 a不就是 返回来的别名的别名了嘛 意思就是我们得 a 是n的别名的别名 那这样想我们的a不就是n的别名了嘛
我们来验证一下吧:
这样不就表明我们的 a是n的别名了嘛 那大家现在觉得我们的传引用返回和传值返回的区别是什么?
很显然 我们的传值返回 是会产生一个临时变量的 这个临时变量具有常性 而我们的传引用返回不会 它会直接返回返回的变量的别名
所以: 我们的传引用返回是不需要拷贝的
但是我们上面的代码还是有问题的 如果大家有了解过栈帧的创建与销毁的话 大家就知道我们的一个函数在执行它的功能后就会返回上一层函数 与此同时这个函数里面为临时变量分配的空间就会还给操作系统 那我们这里写的代码肯定也是会那样的 所以当我们返回引用后 a的地址 就不能再去访问了 因为这块空间因为 n 所在函数栈帧的销毁而返回给操作系统了 此时如果在访问 就是越界的问题了
我们第一次去调用 没问题 而后面两次出现了随机数的情况 是因为我们第一次使用 cout 从流里面读取数据时 我们首先是要先传参的 参数就是我们的 a 而这个时候我们去访问那片空间值还是正确的 因为这片空间还没有被清理 修改 或者 覆盖 那么此时访问这片空间得到的值就还时正确的 但是后面我们传完参之后 调用 cout (这个也是一个函数) 也会有一片自己的空间 此时这篇空间就继续使用test函数用完后的空间 这是这里的空间就被覆盖使用了 里面的数据也就跟随着修改了所以后面两次调用也就时随机的值了
总结:如果函数返回时,出了函数的作用域,如果返回对象还在(还没还给我们的操作系统),那么就可以使用引用返回,如果已经返回给系统了,那么就必须使用传值返回