引用可以实现指针的功能,而且更加方面,那么,为什么还要指针呢?这是因为指针可以为空,但引用不能为空,指针可以被赋值,但是引用只能被初始化,不可以被赋为另一对象的别名,如果你想一个变量记录不同对象的地址,那么这时就必须使用指针。具体可以看下面的例子:
#include <iostream>
using namespace std;
int main()
{
int a = 1 ;
int *p ;
p = &a ; //存放变量a的地址
int b = 2 ;
p = &b ; //存放变量b的地址
return 0 ;
}
我们可以看到指针p一开始存放了变量a的地址,后面有存放了变量b的地址。既可以存放不同变量的地址。
#include <iostream>
using namespace std;
int main()
{
int a = 1 ;
int &p = a ;
int b = 2 ;
&p = b ; //错误:不能将p作为变量b的别名
return 0 ;
}
由于引用只能被初始化,不能被赋值,所以我们不能讲p作为变量b的别名。
另外一点,在堆中创建的一块内存区域,必须要用指针来指向它,否则该区域将会变成无法访问的内存区域。当然我们也可以使用引用来引用指向内存区域的指针。请看下面的例子:
#include <iostream>
using namespace std;
int main()
{
int *p = new int ;
int &rp = *p ; //rp作为p指向的地址处的值的别名
rp = 4 ; //我们可以通过别名rp来修改p指向的地址处的值
cout << "ra:\t" << rp << endl ;
cout << "*p:\t" << *p << endl ;
return 0 ;
}
但是我们要明白一点:我们不可以直接使用引用来指向堆中新建的空间,因为引用只是一个别名,不能当做指针来使用。例如:
#include <iostream>
using namespace std;
int main()
{
int &p = new int ;
return 0 ;
}
但是如果我们作如下修改,则可以:
#include <iostream>
using namespace std;
int main()
{
int *&rp = new int ;
return 0 ;
}
即在&rp前面加了“*”,该语句的意思是:我们在堆中新建了一个空间,返回了该空间的地址,该地址有一个指针来保存。而该指针又定义了一个别名rp。这样rp可以看做是一个指针,可以像操作指针那样操作rp。
#include <iostream>
using namespace std;
int main()
{
int *&rp = new int ;
*rp = 2 ;
cout << "*rp:\t" << *rp << endl ;
return 0 ;
}
我们可以看到结果输出了2。
但是一般情况下,我们不会这样使用引用。我直接写上指针名即可,因为这样使用引用用一定的危险:因为在机器运行不正常的情况下,就是当在机器虚拟内存太小时,无法创建新空间的情况下,new int 会返回一个空指针,我们知道引用不能为空,因此这种情况下使用该语句:int *&rp = new int ;就会导致一个没有用的别名,而使(*)读取一个无用的别名将导致系统崩溃。解决的办法是:不要将引用初始化为新建内存区域的别名,而要将rp初始化为指向该区域指针的别名,前提是首先判断该指针不为空。
#include <iostream>
using namespace std;
int main()
{
int *&p = new int ;
if (p != NULL)
{
int &rp = *p ;//一般是p指向的数据的别名
rp = 2 ;
cout << "rp:\t" << rp << endl ;
}
return 0 ;
}
下面再具体总结下引用和指针的区别:
1、指针可以为空,引用不能为空
2、指针可以被赋值,引用不能被赋值
3、指针可以指向堆中的空间,引用不能指向堆中的空间
这样,我们就可以有选择的使用指针和引用了。