引用实际上是指针的一个扩展,引用最主要的特点是:它是一个虚拟的变量,不产生新的变量,只是引用其它已存在的变量,它只存在于我们的源码之中,不占有内存,引用实际上是变量的别名,所以对引用的改动实际上就是对目标的改动。
首先来举一个引用的例子:
#include<iostream>
#define LOG(x) std::cout << x << std::endl
int main()
{
int var = 6;
int& ref = var;
ref = 2;
LOG(var);
std::cin.get();
}
引用的格式:int& ref = var;
。这里&读作引用,引用不能空引用,引用的变量前面也不用加什么奇奇怪怪的符号,但是后面必须要加上要引用的东西,如果是int& ref;
这种形式而后面不接引用的变量,这样是错误的。
在这里,我们就创建了一个新的“别名”ref,因为这个ref不能称作是一个变量,如果你现在编译,它不会产生两个变量ref和var,只会产生一个var。现在我们可以做的是,由于ref已经引用了var,我们就可以通过ref来改变var的值。运行之后,输出结果为2,也就是var的值被改为了2。
ref就是a,我们只是给a起了一个别名。
来点高级的,假如我们现在想写一个函数,使得这个函数能够让输入整数递增。
#include<iostream>
#define LOG(x) std::cout << x << std::endl
void add(int value)
{
value++;
}
int main()
{
int var = 6;
add(var);
LOG(var);
std::cin.get();
}
运行之后的结果,var依然为6,没有发生加一递增,why?
传值操作,这里产生了一个新变量value,相当于开始先把var的值传给了value,然后通过add函数后value的值变为了7,而var的值并没有改变。
既然变量var的值实际上并没有改变,那要想变量传递进函数并改变变量var的值,可以怎么做呢?
我们可以不把值传递进函数,而是把地址传递进函数。这里就要用到指针。
#include<iostream>
#define LOG(x) std::cout << x << std::endl
void add(int* value)
{
(*value)++;
}
int main()
{
int var = 6;
add(&var);
LOG(var);
std::cin.get();
}
注意:(*value)++;
一句中,由于自增操作符的优先级要比解引用的优先级要高,我们要加括号来保证先进行解引用在对其值进行自增,否则的话是地址先加一然后再解引用,而var地址内的值并没有改变。
上面是用指针的方法改变var的值,我们还可以用引用的方式将代码变得更为简洁一点:
#include<iostream>
#define LOG(x) std::cout << x << std::endl
void add(int& value)
{
value++;
}
int main()
{
int var = 6;
add(var);
LOG(var);
std::cin.get();
}
传值操作时,由于产生了一个新的变量value,所以进行加一操作的实际上是value而不是var。但是现在进行了引用,正如最开始时的说明,引用实际上不产生新的变量,只是对已存在的变量进行引用,所以这里value就是var,我们在函数中改变了value的值,实际上也就是改变了var的值。
同时再注意一下格式:把函数中的形参定义由指针改成引用的格式,这样做的好处就是:引用之后,我们就是对var这个值进行操作了,所以函数中的value++前也不用再加上*解引用了;同时引用时时也不用再用var的地址进行传递了,直接用var进行传递。
运行结果如图所示:
var=7,我们成功对var的值进行了修改,和用指针对地址进行传递是相同的。
还需要注意的一点是,一旦你声明了一个引用就不能更改它引用的对象。举例:
int a = 2;
int b = 6;
int& ref = a;
ref = b;
这样操作是完全不行的,这里发生的是把b的值赋给了a的引用,输出为a=8,b=8。所以如果我们真的想改变ref所指代的是什么的时候,该怎么做呢?
我们需要创建一些变量使得它先指向a,再指向b,我们创建一个指针来完成。
#include<iostream>
#define LOG(x) std::cout << x << std::endl
void add(int& value)
{
value++;
}
int main()
{
int a = 5;
int b = 8;
int* ref = &a;
*ref = 2;
ref = &b;
*ref = 3;
LOG(a);
LOG(b);
std::cin.get();
}
输出结果为:
a的值为2,b的值为3,这样我们就使用指针使得ref先指向a又指向了b,并且通过解引用修改了变量的值。
指针和引用的区别
由上面我们可以看出,指针和引用有相似之处,他们的区别到底是什么呢?
- 在任何情况下,都不能使用指向空值的引用。也就是说存在空指针,但是不存在空引用。
- 使用引用之前不需要测试它的合法性,相反,指针应该总是被测试,以防止它为空,这是因为引用在声明时就已经指定了对象,且会一直指向对象,因此无需做合法性测试。
- 指针可以被重新赋值以指向另一个不同的对象,但是引用总是指向在初始化被指定的对象,以后不能改变。
与按值传递不同,引用传递不再生成参数的一个数值副本,取而代之的是被调函数收到了一个引用,这个引用指向了主调函数的真实参数。