1、引用作为函数的返回值
引用作为返回值,一般不常见,实际在真正的工作过程中也是极少遇见。引用作为返回值有他的有点,也有他
的缺点。看下面的代码
#include <iostream>
using namespace std;
//这种实现方式,在调用的时候,return p,其实还是发生了拷贝动作
char *getmem1(int size)
{
char *p = NULL;
p = (char *)malloc(size);
if (NULL != p)
return p;
return NULL;//拷贝地址 0x11223344
}
//指针的方式
int getmem2(char **pp, int size)
{
char *p = NULL;
p = (char *)malloc(size);
if (NULL != p)
{
*pp = p;
return 0;
}
return -1;
}
//引用作为返回值,不要返回局部变量的引用(别名)、地址
int& getA()
{
int a=10;
return a;//int &temp = a;
}
int& getA1()
{
static int a=10;
return a;//int &temp = a;除非数据在静态区 这样才是可以的
}
int main(void)
{
/*int main_a = 0;
main_a = getA();//main_a = temp 这里赋值的是getA中的临时变量,而临时变量a在return的时候被销毁
cout<<"main_a = "<<main_a<<endl; //返回temp的别名,打印时直接奔溃
int &main_a_re = getA(); //main_a_rew为a别名,一样会出问题
cout<<"main_a_re = "<<main_a_re<<endl; //奔溃报错*/
int &main_a_re = getA1(); //main_a_rew为a别名,a在静态区,或者是在堆上申请的,是可以的。结果打印10
cout<<"main_a_re = "<<main_a_re<<endl;
//同样的,由于getA1返回的是静态变量的别名,所以可以被赋值
getA1() = 100;
cout<<"main_a_re = "<<main_a_re<<endl; //结果为打印100
//结论:当引用如果作为函数返回值时,不可以返回局部变量,可以返回静态变量,并可以被作为左值在进行赋值操作
return 0;
}
getmem1和getmem2,是简单介绍了一下,通过指针可以实现的返回指针的操作。同理,根据上一篇的内容,能用指针实现的。使用引用也可以实现。这里不做过多研究了。
下面开始分析:当引用作为返回值,如getA,实际上返回的是一个别名,我们把getA理解是一个局部的变量temp,在函数返回的时候相当于是 int &temp = a;返回的是局部变量a的别名。
但是局部变量a在栈上,并当函数返回时被销毁。所以下面如果直接使用main_a = getA();会出现垃圾值,甚至会直接程序奔溃。
getA1也是如此,但是getA1返回的是静态变量,当函数返回时不被销毁。所以可以使用。同时由于getA1返回的是静态变量a的别名,我们还可以把它当做左值,进行赋值操作。
结论:(缺点)当引用如果作为函数返回值时,不可以返回局部变量,
(优点)可以返回静态变量,并可以被作为左值在进行赋值操作。但其实引用作为函数返回值,并不被推荐使用。
2、指针引用
指针的引用,就和正常的整形变量、或者结构体变量一样,只不过引用的对象是指针。相当于是给指针做了一个别名,常常会使用在函数的入参当中。看下面这个程序
#include <iostream>
#include <cstring>
using namespace std;
struct teacher
{
int id;
char name[64];
};
//指针的方式
int get_teacher(struct teacher **pp)
{
struct teacher *pt = NULL;
pt = (struct teacher *)malloc(sizeof(struct teacher));
if (NULL != pt)
{
pt->id = 10;
strcpy(pt->name, "zhang3");
*pp = pt;
return 0;
}
return -1;
}
void free_teacher(struct teacher **pp)
{
if (NULL == pp)
return ;
struct teacher *p = *pp;
if (p != NULL)
{
free(p);
*pp = NULL;
}
}
//引用的方式
int get_teacher1(struct teacher *&pre) //相当于struct teacher *是一个整体,pre就是入参的别名
{
pre = (struct teacher *)malloc(sizeof(struct teacher));
if (NULL != pre)
{
pre->id = 11;
strcpy(pre->name, "li4");
return 0;
}
return -1;
}
//通过引用的方式,比较简单,不会像二级指针那样,可能会使用错误。//比如我第一次写的时候就改了很多次,二级指针才用对
void free_teacher1(struct teacher *&pre)
{
if (pre != NULL)
{
free(pre);
pre = NULL;
}
}
int main(void)
{
struct teacher *pt = NULL;
get_teacher(&pt);
cout<<pt->name<<" "<<pt->id<<endl;
free_teacher(&pt);
get_teacher1(pt);
cout<<pt->name<<" "<<pt->id<<endl;
free_teacher1(pt);
return 0;
}
因为入参是一个指针,所以通过get_teacher来申请堆上的空间,就需要传入二级指针。当然也可以用局部变量一级指针来malloc,最后在用一次值拷贝,不过当数据量比较大的时候,值拷贝就不能作为是一个好的方法。
get_teacher入参二级指针,在使用的时候需要对指针进行取地址,然后传入参数。很多人使用二级指针并不是很熟悉,一不小心就会出错。包括后面的free_teacher也是一样的。
通过使用引用作为入参,void free_teacher1(struct teacher *&pre) 就比较简单了,这里的pre就是指针struct teacher *入参的别名,吧struct teacher *当做是和int一样的一种数据结构、当做一个整体就很好使用了。
有点自然是,代码写起来比较简单,不容易出错。
3、const引用
const引用通常是作为形参使用,其实就在常指针的及出厂,再加一个const修饰,起到一个保护作用。
int main(void)
{
const int a = 10;
//int &re = a; 这样的话 在c++编译器中会直接报错,error: binding reference of type ‘int&’ to ‘const int’ discards qualifiers
const int &re = a; //如果想对一个常量进行引用,必须是一个const引用
int b=20;
const int &re2 = b; //这样是可以的,但是re2作为b的别名,却是一个const类型,所以re2也不可以改变。但是b可以修改。
//re2 = 30; 使用引用的时候实际上就是加了一个取值符号 *re2,理解
b = 30;
return 0;
}
a. 常量引用 : 将引用绑定到const对象上(其实就是对const的引用)。
b. 常量引用表示不能通过引用改变绑定对象的值,但是对象的值可以通过别的方式改变。