引用
记录自己学习C++的点点滴滴,为了自己更好的复盘和回顾,如果可以帮助到你,我将不胜荣幸!!!
一、引用是什么
C++引用的提出其实就是为了减少C中指针的使用,引用并不是定义一个新的变量,而是给变量取一个别名;形如:
int number = 10;
int & ref = number;
引用的符号就是 & ,和C语言的取地址符号是一样,但是他们的作用并不一样,区分他们的一个关键点就在于&前是否有类型,如果&前有地址那么这便是用于引用而不是用于取地址;
二、引用的使用
引用在使用时,我们需要注意:
引用在定义时必须要初始化,引用一经绑定就不会改变指向。
其实这和上一小节中我们谈到的指针常量是一样的,我们其实可以想到引用的实现的,但是编译器是不会为引用开辟内存空间的,它和它引用的变量是共用同一块内存空间的;并且对引用的操作与对引用的变量操作是等价的;
其实这一点在进行值交换函数时是非常有用的,我们可以对比以下C中实现两个值交换函数与C++中实现两个值交换函数:
//c语言实现值交换函数
void swap(int * number1,int *number2)
{
int tmp = *number1;
*number1 = *number2;
*number2 = tmp;
}
//C++实现值交换函数
void swap(int &ref1, int &ref2)
{
int tmp = ref1;
ref1 = ref2;
ref2 = tmp;
}
我们发现引用的引入相比较用指针的方式更加方便且容易理解,同时这里也引出了我们接下来要讨论的一个话题——当引用作为函数返回值时,如果返回的是全局变量、堆空间以及栈空间分别是怎样的呢?
三、引用作为函数返回值
3.1、全局变量
当引用作为全局变量的函数返回值时,不会发生拷贝操作,并且对引用的操作就相当于对全局变量的实体进行操作;
15 //引用作为全局变量的返回值
16 int arr[10] = {1, 2, 3, 4};
17 int &getIndex(int idx)
18 {
19 return arr[idx];
20 }
21 void test1()
22 {
23 int &ref = getIndex(0);
24 cout << "getIndex(0) = " << getIndex(0) << endl;
25 getIndex(0) = 100;
26 cout << "getIndex(0) = " << getIndex(0) << endl;
27 cout << "arr[0] = " << arr[0] << endl;
28 }
3.2、堆空间
堆空间可以用局部变量进行举列,由于局部变量是有生存周期的,随着函数的结束,堆空间上的局部变量也就自动消失了,因此我们在使用引用作为堆空间的返回值时,编译器会发生报错;
30 // 引用作为堆空间的返回值
31 int &fun1()
32 {
33 int number = 100;
34 return number;
35 }
36 void test2()
37 {
38 int &ref = fun1();
39 cout << "number = " << ref << endl;
40 }
会发生如下的报错:
reference.cpp: In function ‘int& fun1()’:
reference.cpp:33:9: warning: reference to local variable ‘number’ returned [-Wreturn-local-addr]
int number = 100;
^~~~~~
这里也就引出来了函数返回类型是引用的前提条件:返回的引用必须是生命周期大于函数本体的实体;
这时候大家也就会不由得的联想到我们最常见的栈空间上的变量了,按道理说栈空间上的变量不会随着函数的消失而消失,那么他的生命周期肯定也就大于函数的生命周期了,应该是可以用引用作为函数的返回类型;
那么栈空间在将引用作为函数返回类型时会存在哪些问题呢?
3.3、栈空间
直接上代码
43 int &getHeapData()
44 {
45 int *pInt = new int(100);
46 return *pInt;
47 }
48 void test3()
49 {
50 int &ref = getHeapData();
51 cout << "getHeapData() = " << getHeapData() <<endl;
52 cout << "ref = " << ref << endl;
53 }
getHeapData() = 100
ref = 100
可以看到堆空间时可以使用引用作为返回类型的,但是大家有没有发现什么问题呢?
我们在函数中new了一个堆空间,我们有delete这个堆空间吗?,如果没有那会发生一个什么样的问题呢?
显然,内存泄漏了,我们可以通过delete操作解决这里的内存泄露,但是一般我们在拿到这种函数的api时,默认他已经做好了相应的内存管理工作,不会专门再去做delete销毁堆空间,而且你也不能保证别人就用的是new啊!所以不要返回堆空间的引用,除非有完善的自动回收机制;
这里最后完善一下上面发生内存泄漏的问题:
48 void test3()
49 {
50 int &ref = getHeapData();
51 cout << "getHeapData() = " << getHeapData() <<endl;
52 cout << "ref = " << ref << endl;
53
54 delete &ref;
55 }
总结
引用在C++的使用是十分常见的,同时这也是C与C++的一个不同之处