[TOC]

文章参考:https://www.zhihu.com/question/353263548

概述

通过使用引用来替代指针,会使 C++ 程序更容易阅读和维护。C++ 函数可以返回一个引用,方式与返回一个指针类似。当函数返回一个引用时,则返回一个指向返回值的隐式指针。用引用作函数的返回值的最大的好处是在内存中不产生返回值的副本

使用方法

引用作为返回值,须遵守以下规则:

  • 不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了”无所指”的引用,类似于野指针,程序会进入未知状态。
  • 不能返回函数内部new分配的内存的引用。虽然不存在局部变量的被动销毁问题,可对于这种情况又面临其它尴尬局面。例如,被函数返回的引用只是作为一 个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
  • 可以返回类成员的引用,但最好是 const,这样可以避免在无意的情况下破坏该类的成员。

下面通过一段程序详细说明其用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<iostream>
using namespace std;

float temp;

float func1(float r)
{
temp = r * r * 3.14;
return temp;
}
float &func2(float r){ //&说明返回的是temp的引用,换句话说就是返回temp本身
temp = r * r * 3.14;
return temp;
}

int main()
{
float a = func1(5.0); //case 1:返回值
//float &b = func1(5.0); //case 2:用函数的返回值作为引用的初始化值 [Error] invalid initialization of non-const reference of type 'float&' from an rvalue of type 'float'
//(有些编译器可以成功编译该语句,但会给出一个warning)
float c = func2(5.0); //case 3:使用返回的指向temp的引用给变量c赋值
float &d = func2(5.0); //case 4:用函数返回的引用作为新引用的初始化值

cout<<a<<endl;//78.5
//cout<<b<<endl;//78.5
cout<<c<<endl;//78.5
cout<<d<<endl;//78.5
return 0;
}

/****************运行结果****************/
78.5
78.5
78.5

分别分析一下上面4种情况具体的运行原理:

  • case1:返回全局变量temp的值时,C++会在内存中创建临时变量并将temp的值拷贝给该临时变量。当返回到主函数main后,赋值语句a = func1(5.0)会把此临时变量的值再拷贝给变量a。

  • case2:函数func1()是以值方式返回,返回时首先拷贝temp的值给临时变量。返回到主函数后,用临时变量来初始化引用b,使得b成为该临时变量到的别名。由于临时变量的作用域短暂(在C++标准中,临时变量或对象的生命周期在一个完整的语句表达式结束后便宣告结束,也就是在语句float &b = func1(5.0);之后),所以引用b很有可能以后”指向”一个无法确定的值。

  • case3:函数func2()的返回值不产生副本,而是返回一个”指向”变量temp的引用给主函数,即主函数的赋值语句中的左值是直接通过变量temp的引用拷贝而来(c是变量temp的一个拷贝而非别名),这样就避免了临时变量的产生。尤其当变量temp是一个用户自定义的类的对象时,这样还避免了调用类中的拷贝构造函数在内存中创建临时对象的过程,提高了程序的效率。

  • case4:用函数返回的“指向”temp的引用作为引用d的初始值,此时d为temp的别名,这种方法同样不产生副本。

另外,通过函数返回引用这种机制,函数就可以放在赋值语句的左边。请看下面这个demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//代码来源:RUNOOB
#include <iostream>
using namespace std;

double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};

double& setValues( int i )
{
return vals[i]; // 返回第 i 个元素的引用
}

int main ()
{

cout << "改变前的值" << endl;
for ( int i = 0; i < 5; i++ )
{
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}

setValues(1) = 20.23; // 改变第 2 个元素
setValues(3) = 70.8; // 改变第 4 个元素

cout << "改变后的值" << endl;
for ( int i = 0; i < 5; i++ )
{
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}
return 0;
}

/****************运行结果****************/
//改变前的值
vals[0] = 10.1
vals[1] = 12.6
vals[2] = 33.1
vals[3] = 24.1
vals[4] = 50
//改变后的值
vals[0] = 10.1
vals[1] = 20.23
vals[2] = 33.1
vals[3] = 70.8
vals[4] = 50