目录
在C语言的学习中,我们学习了对指针的使用。而在C++中我们引用另一个有效的工具--引用。本篇文章将为问题很多的小明详细介绍“引用”。
引用的概念
概念:
引用并不是新建一个变量,而是对已有变量取一个别名。由于这个特性,引用并不会新开辟空间,而是同已有的变量公用同一块空间。
例如:每位同学都有自己的名字,而问题很多的小明就是给大家取得别名。但是指向的都是每一个人自己本身。
使用:
引用符号:&
类型+&+变量 = 引用实体
举个例子:
int a = 1;
int&b = a;
printf("%p %p",&a,&b);
我们打印出地址可以发现,a和b指向的地址相同。所以,我们可以对之前的概念,引用变量和实体指向的地址相同。
既然二者的地址指向同一块空间,那么对变量的修改就会使实体的值也改变。
int a =10;
int&b = a;
std::cout<<a<<' '<<b;
b =20;
std::cout<<a' '<<b;
打印的最终结果如下:
所以我们可以发现,一旦引用后,对变量的修改也会对实体造成影响。就好比,我打了孙悟空和我打了孙行者造成的效果是一样的,因为“孙悟空”和“孙行者”都指向同一个人。
此外,在引用的时候,我们的引用变量的类型必须与原来的实体类型相同。实体类型是什么,引用的变量就要是什么类型。
引用的特性
1.特性
1.引用必须在定义时就要初始化。
2.一个变量可以有多个引用。
3.引用一旦引用一个实体,就不能应用其他实体。
2.讲解
我们通过代码来对引用的特性进行讲解。
int a = 10;
int& ra; // 没初始化,会报错
int& ra = a;
int& rra = a;
int& rrra = a;
//这三个都是a的引用,只要其中一个
//发生改变,其他三个也会改变
printf("%p %p %p %p\n", &a, &ra, &rra, &rrra);
根据第一部分的讲解,我们可以知道ra,rra,rrra的地址都和实体a的地址相同,因此修改任一一个其他的几个也会改变。 通俗来讲,就是一个人可以有多个别称。
常引用
引用也可以用于常量的类型,根据引用概念中变量类型必须与实体类型一致可以知道,在进行常引用时,类型需要加上const。
const int a = 10;
//int& ra = a; // 该语句编译时会出错,a为常量
const int& ra = a;
// int& b = 10; // 该语句编译时会出错,b为常量
const int& b = 10;
double d = 12.34;
//const int& rd = d; // 该语句编译时会出错,类型不同
const double& rd = d;
通过这一板块的讲解,相信大家对引用中类型的设置有了更深的理解。
引用的使用
1.引用做函数的参数
与C语言不同的是,在C语言中,实参传递的形参只是一份数据的拷贝,对实参无法产生影响,只有传输地址才能对实参产生影响。但在C++中,有了引用,我们就不需要传输地址!
void Swap(int& left, int& right)//交换函数
{
int temp = left;
left = right;
right = temp;
}
int main{
int a = 10;
int b = 20;
Swap(a,b);//因为形参为引用,所以这传的是a,b的引用
}
从上述代码可以看到,如果我们想要改变实参,只需在外部函数中进行引用。
这里需要注意的是,引用做函数参数的时候,依旧需要带类型,若不带类型则为取地址。
2.引用做函数返回值
引用在用作返回值时,若出了函数变量未被销毁,则可以直接在函数外部对函数内部进行修改。
static int n = 0;
int Count()
{
int n =0;
n++;
return n;
}
int main()
{
int ret = Count();
cout<<n<<endl;
return 0;
}
大家可以思考上述代码的输出结果。
大家打印的结果可能是1,也可能是随机值。为什么会出现这种情况呢?
因为这里的返回并不是返回n,因为n在这里是局部变量,当n出了Count函数,n便被销毁了,返回值是通过已经在main函数里创建好的临时变量存下n的值返回的。
static int n = 0;
int& Count()
{
n++;
n++;
return n;
}
int& tmp = Count();
tmp = 20;
cout << tmp << ' ' << n;
我们将n定义为静态变量,并通过引用在外部尝试修改n的值 。
得到的输出结果如下:
我们再来分析下面这段代码
int&count()
{
int n = 0;
n++;
return n;
}
int main()
{
int&ret = count();
count<<ret<<endl;
count<<ret<<endl;
return 0;
}
很多同学会认为两次输出值相同,实际上是不同的,第二次的输出会是一个随机值。
原因是,由于这里使用了引用,所以这里的ret就和n公用一块空间。在第二次调用时会将原空间进行覆盖,所以打印的出的是一个随机值。所以这里我们可以知道引用是对原空间的直接覆盖。
传值和传引用返回对比
在前面我们介绍到,传值返回是通过中间的临时变量进行返回,如图。
而对于传引用返回则不需要建立临时变量,而是直接修改返回。
所以,我们可以得到如下结论:用值作为参数或者返回值类型效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低 。
引用和指针的区别
1.引用概念上定义一个变量的别名,指针存储一个变量地址。
2.引用在定义时必须初始化,指针则没有要求。
3.引用在初始化时引用一个实体后,就不能引用其他实体,而指针可以在任何时候指向任何一个同类型实体。
4.NULL没有引用,但有NULL指针
5.在sizeof种,引用的结果时引用类型的大小,而指针始终是地址空间所占字节数。
6.引用++是对引用的实体+1,而指针++是向后移动一个类型大小。
7.有多级指针,但无多级引用。
8.访问实体方式不同,指针需要显式解引用,引用编译器自己处理。
9.引用比指针使用更加安全。