C++中引用和指针的不同

来源:互联网 发布:热血传奇 怪物数据 编辑:程序博客网 时间:2024/04/29 04:34

C++中引用和指针的不同

分类: C++ 281人阅读 评论(0)收藏 举报

  引用,其本质就是指针,将它用在一些特别的场合,会比指针更简洁,更方便。具体说来,指针多用于动态内存管理和对数组的操作等,C风格的代码接收和返回指针;引用则往往用在接收和返回类类型的名字空间域函数或类域函数,以避免类对象的复制开销。但是请注意,引用毕竟不完全等同于指针,它们有一些差异:
  1、引用必须初始化,指针则不然。
int *pi;//可以
int &ri;//不行,未初始化
int i;int &ri=i;//可以
  2、引用不能为空引用,指针则不然。
int *pi=NULL;//可以
int *pi=NULL;int &ri=*pi;//会引发问题。
  3、引用在初始化后,不能再引用其他对象,指针则不然。
void f(int &ri)
{
    int i=3;
    ri=4;//改变所引用的实参
    ri=&i;//错误,类型不符
    ri=i;//ri并未转为引用局部变量i
}
  虽然每次调用f时,都有可能会使形参ri,引用不同的实参,但是在f函数内,无法在调用处,引用初始化后,再改变其所引用的对象。显然包括引用在内,在很多时候,初始化与赋值运算符的语义有很大区别。
  4、引用在必要时实现为指针,但是常规语法取不到这个内部指针的地址。
int i=3;int *pi=&i;//i的值为3,地址为&i;pi的值为&i,地址为&pi
int i=3;int &ri=i;//i的值为3,地址为&i;ri的值为3,地址为&i,初始化后ri和i基本为同义语。
  实际上,当引用ri初始化为i后,语言实现可能只将ri优化处理成i的别名,而不将其实现为指针;也可能使用一个内部指针来完成工作。比如第3条中的f函数的形参ri,每次具体调用函数时,都可能会传入不同地址的实参。因为设计引用就是要避免对象复制的,因此它会压入堆栈一个指向被引用类型的指针。有可能这样来实现:
int i=3;
int &ri=i;//可实现为int *temp_pi=&i;以后每次源程序中使用ri时,就会被相应替换成*temp_pi
  即使实现为这样的内部指针,也无法用正常的语法来取得它的地址。因为按照上边的分析,初始化后,取地址操作&ri将等价于&*temp_pi。那么这对用户来说,有什么会造成影响的不同吗?来看看一种不太常见的情况:可变个数参数表的省略号...前一个参数,如果是引用类型,将无法正常工作;如果是指针类型或简单类型等,则不会有问题。如有以下声明:
void f(int i,...);
  则在实现代码中,可以使用C标准库的stdarg.h来取得可变个数部分的参数。有兴趣的话可以去看看stdarg.h的具体代码,它是用宏来完成的,针对不同的环境,采用了相应的手段。我用一段功能类似的伪代码来说明一下:

void f(int i,...)//此函数用i来表示可变个数参数的个数
{
    int ix;
    void *base=&i;
    for(ix=0;ix<i;ix++)
    {
        base+=sizeof(int);//或者依实现为base-=sizeof(int)
        cout<<"第"<<ix+1<<"个int参数的值为:";
        cout<<*((int *)base)<<endl;
    }
}
 
  如果有调用f(5,1,2,3,4,5);则5,4,3,2,1,5将从右到左依次被压入堆栈,然后调用f函数。在f函数内部来看,那几个未命名的可变参数,是每个占一个sizeof(int)大小,一个接一个挨着放在堆栈上的,并且局部变量i指向了堆栈顶的位置。这时可以用相对于堆栈顶位置的位移量来取得每一个局部变量。当函数返回时,由调用点来清理堆栈。这样,f(5,1,2,3,4,5);和f(3,10,11,12);均可正确调用和返回,因为每处调用均知道自己用了几个参数,所以它可以在函数返回后,正确地在堆栈上弹出参数(其缺点就是每处调用都要自己清理,这样方式的程序长度,要比清理只出现在被调函数返回处的方式,要长一些)。这就是所谓的cdecl调用约定。然而,如果...前的参数为引用类型,如:
void f(const int &ri,...);//如果使用非左值进行调用,引用必须为const
调用f(5,1,2,3,4,5)将会发生什么呢?
因为int &ri是个引用参数,所以实际上会是:
push 5;
push 4;
push 3;
push 2;
push 1;
const int temp_i=5;
const int *temp_pi=&temp_i;
push temp_pi;
  也就是说,和5个可变个数参数并排挨着的实参ri,在实际调用时,会被处理成向堆栈压进一个const int *类型的地址值。那么在f函数内的代码中,void *base=&ri;又将指向何处呢?按前面的分析,&ri会被处理成&*temp_pi,其结果就是temp_pi的值,temp_i的地址。试图取回和1、2、3、4、5挨着的这个内部指针的地址,其结果却是内部指针temp_pi的值,temp_i的地址,它并不在可变个数参数的1、2、3、4、5中1的旁边。所以调用将无法正常取得...部分的参数。


原创粉丝点击