(1)以引用返回函数值,定义函数时需要在函数名前加 &
(2)用引用返回一个函数值的最大好处是,在内存中不产生被返回值的副本。
引用作为返回值,必须遵守以下规则:
- (1)不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。
#include <iostream>
using namespace std;
int& test1()
{
int n = 5;
return n;
}
int main()
{
int i = test1();
cout << i << endl;
return 0;
}
g++ 5.4.0 编译报警告,运行报段错误!
VS2015编译报:warning C4172: 返回局部变量或临时变量的地址: n
但可正常运行输出
因为局部变量n赋值给i时还没有被新数据覆盖。
由此可看出g++更加严谨,不能返回局部变量的引用!
补充:
#include <iostream>
using namespace std;
int& test1()
{
int n = 5;
return n;
}
int main()
{
// int i = test1(); // 因为有拷贝操作,g++5.4.0运行将会报段错误
int &i = test1(); // 因为无拷贝操作,g++5.4.0运行将不会报错
// cout << i << endl; // g++5.4.0运行将会报段错误
return 0;
}
再举1例:
#include <iostream>
using namespace std;
int& test1()
{
int n = 5;
return n;
}
void test2()
{
int b = 8;
}
int main()
{
int &i = test1();
cout << i << endl; // 输出 5
int &b = test1();
cout << b << endl; // 输出 5
test2();
cout << b << endl; // 输出 8
int c = test1();
test2();
cout << c << endl; // 输出 5
return 0;
}
VS2015编译报警告:warning C4172: 返回局部变量或临时变量的地址: n
但可正常运行输出:
i实际为test1 中局部变量的别名, 当test1退出时,i 指向的内存中值未被覆盖,直接输出为5;
输出第二个b 之前,因为调用了test2, 实际指向的内存空间已被覆盖为8,故输出为8;
c是一个新的变量,其值为5, 故输出为5;
g++ 5.4.0编译警告,运行报段错误:
- (2)不能返回函数内部new分配的内存的引用。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一 个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
- (3)可以返回类成员的引用,但最好是const。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常 量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。
1. 用返回值方式调用函数
#include <iostream>
using namespace std;
int g_a; // 定义全局变量g_a
int test() {
g_a = 168;
return g_a;
}
int main()
{
int i = test();
cout << i << endl;
return 0;
}
返回全局变量g_a的值时,C++会在内存中创建临时变量并将g_a的值拷贝给该临时变量。当返回到主函数main后,赋值语句 i = test()会把临时变量的值再拷贝给变量i 。
2. 用函数的返回值初始化引用变量
#include <iostream>
using namespace std;
int g_a; // 定义全局变量g_a
int test() {
g_a = 168;
return g_a;
}
int main()
{
int &i = test();
cout << i << endl;
return 0;
}
编译报错:error C2440: “初始化”: 无法从“int”转换为“int &”
注:(有些编译器可以成功编译,但会给出一个warning)
分析:这种情况下,函数test()是以值方式返回,返回时,首先拷贝g_a的值给临时变量。返回到主函数后,用临时变量来初始化引用变量i,使得i成为该临时变量的别名。由于临时变量的作用域短暂(在C++标准中,临时变量或对象的生命周期在一个完整的语句表达式结束后便宣告结束,也就是在语句int &i = test();之后) ,所以i面临无效的危险,很有可能以后的值是个无法确定的值。哪怕修改为如下代码:
#include <iostream>
using namespace std;
int g_a; // 定义全局变量g_a
int test() {
g_a = 168;
return g_a;
}
int main()
{
const int &i = test();
cout << i << endl;
return 0;
}
虽然编译运行通过,但该问题依然存在!
如果真的希望用函数的返回值来初始化一个引用变量,应当先创建一个变量,将函数的返回值赋给这个变量,然后再用该变量来初始化引用:
int b = test();
int &i = b;
3. 用返回引用的方式调用函数
#include <iostream>
using namespace std;
int g_a; // 定义全局变量g_a
int& test() {
g_a = 168;
return g_a;
}
int main()
{
int i = test();
cout << i << endl;
return 0;
}
这种情况下,函数test()的返回值不产生副本,而是直接将变量g_a返回给主函数,即主函数的赋值语句中的左值是直接从变量g_a中拷贝而来(也就是说i只是变量g_a的一个拷贝而非别名) ,这样就避免了临时变量的产生。尤其当变量g_a是一个用户自定义的类的对象时,这样还避免了调用类中的拷贝构造函数在内存中创建临时对象的过程,提高了程序的时间和空间的使用效率。
4. 用函数返回的引用作为新引用变量的初始化值
#include <iostream>
using namespace std;
int g_a; // 定义全局变量g_a
int& test() {
g_a = 168;
return g_a;
}
int main()
{
int &i = test();
cout << i << endl;
return 0;
}
这种情况下,函数test()的返回值不产生副本,而是直接将变量g_a返回给主函数。在主函数中,引用变量i用该返回值初始化,也就是说此时i成为变量g_a的别名。由于g_a是全局变量,所以在i的有效期内g_a始终保持有效,故这种做法是安全的。
5. 可以用函数返回的引用作为赋值表达式中的左值
当函数返回一个引用时,则返回一个指向返回值的隐式指针。这样,函数就可以放在赋值语句的左边。例如,请看下面这个简单的程序:
#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;
}
输出:
有兴趣可以看下:为什么 C++ 有指针了还要引用?
参考链接:http://www.runoob.com/cplusplus/returning-values-by-reference.html