一、前言
直接使用变量名,对象名,数组名作为实参传递进函数都是按值传递,无法修改原始数据,并且耗费时间;使用指针作为实参则不安全;引用则刚好适合。
引用是一个变量的别名,可以像使用变量一样使用引用。
左值:可以被引用的数据对象,可以通过地址访问到的数据对象。例如变量、数组元素、结构成员。
非左值:字面常量(用引号括起来的字符串除外,因为它们由地址表示)、表达式等。
引用是左值,引用的引用是左值。(一个变量可以有多个别名)
引用和原变量的内容和地址一模一样,通过原变量或者引用改变值之后,另外一个的值也会改变。
int a = 1;
int& b = a;
a = 2
//此时b的值也为2。
使用引用的用途:
1、能够在函数中修改原始数据
2、通过传递引用而不是按值传递,提高运行速度。
二、声明引用
1、引用必须在声明时完成初始化,在初始化时只能绑定左值。
2、引用一旦完成初始化,就不能再成为其他变量的引用。
3、数组、结构不能建立引用,因为是多个数据,无法确定别名
三、引用参数
引用作为函数的形参时,会对实参进行引用。这也使得引用作为参数可以改变原始数据。(使用const修饰引用形参,可以避免通过引用形参修改原始数据,但是可以通过变量本身修改值)
p.s.当使用const修饰的引用作为形参时,如果:
1、实参是左值,但类型不正确,可以被转换成正确的类型。
2、实参不是左值,但是类型正确(例如实参为2+3,形参为int类型)
此时C++允许建立临时变量,把传入的实参转换成正确的左值,赋给该临时变量,然后让形参引用该实参。
为什么一定要const修饰引用形参才能创建临时变量?
因为如果引用形参不是const,一旦创建临时变量,就代表着无法通过引用形参修改函数的原始数据,和引用的设计理念冲突。因此限定只有const修饰的引用参数允许创建临时变量,这样即使无法修改原始数据,也不会和引用的设计理念冲突,因为引用是const,已经被规定不能修改。
四、引用作为返回值
引用作为返回值,则返回值必须是一个左值。
如:
int& test(int a);
使用这些方法赋值都可以:
int b = test(a);
int &c = test(a);
返回的引用不能指向局部变量内存,因为局部内存在函数执行完成之后会被释放掉。
可以采用以下几种方法:
1、返回常量引用:如果函数返回的是一个常量引用,在函数被调用后,返回的引用就不能被修改,所以不会有悬垂引用的问题。但这种方法局限性很大,在实际开发中用途也不是很广泛。
2、返回堆上对象的引用:可以在函数内用new关键字动态分配内存,返回堆上对象的引用,这样就可以保证在函数返回后该对象依然存在。但这样需要手动管理内存,容易出现内存泄漏和内存竞争等问题,所以需要小心使用。
3、返回传入引用实参:传递进来的实参不是函数内部的局部变量,可以在函数结束之后依然存活。
4、不返回引用,返回一个该类型的值。比如
int test(int& a);
这样局部变量就会被拷贝到调用者的栈帧内存中。这样在局部变量的内存被释放后,调用者的内存不会被立刻释放。
五、引用的使用
使用引用就如同使用变量本身那样
如:
int& test(int &a)
{
return a;
}
在函数内部,我们使用引用a就如同使用a代表的整数一样
六、实践分析
例1
#include <iostream>
using namespace std;
int& test(int &a)
{
return a;
}
int main()
{
int b = test(3);
cout<<b<<endl;
}
编译器会报错,因为3是字面常量,不是左值,不能被test函数中的a引用。
例2
#include <iostream>
using namespace std;
int& test(int &a)
{
return a;
}
int main()
{
int a = 3;
int b = test(a);
cout<<b<<endl;
}
相比于例1,我们给整数3取了一个名字叫a,于是这个整数成为了左值,可以被引用。成功输出3
例3
int& test(int a)
{
return a;
}
编译不会通过,编译器会抱怨reference to local variable 'a' returned
。意思是返回的引用指向了一个局部内存。
因为传入的是int类型的变量,是属于按值传递。也就是说,在传入的时候,编译器会创建一个临时变量,把实参的值拷贝到这个临时变量,然后让函数的引用形参指向这个临时变量。这个临时变量属于局部变量,在函数执行完毕的时候,它的内存会被释放。因此编译器会报错。
例4
string test1(const string& s1, const string& s2);
string test2(const string s1, const string s2);
test1的效率更高,因为test1不用按值传递,而是传入引用
注意,test1不能返回string&,但是可以返回 string。因为引用作为返回值,不能指向局部变量。