引用和指针

来源:互联网 发布:千牛与淘宝什么区别 编辑:程序博客网 时间:2024/06/07 05:53

引用(reference)是c++对c语言的重要扩充。引用就是某一已存在变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。

引用的声明方法:类型标识符 &引用名=目标变量名;

假若有一变量a,想给它起一别名,可以这样写:

int  a; //定义a是整形变量int  &b = a; //声明b是a的引用
以上语句声明了b是a的引用,即b是的别名,经过这样声明后,a或b作用相同,代表同一变量。

注意:

(1)&在此不是求地址运算,而是起标识作用。
(2)类型标识符是指目标变量的类型。
(3)声明引用时,必须同时对其进行初始化。
(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。
int a=2,int &ra=a;
a为目标原名称,ra为目标引用名。给ra赋值:ra=1; 等价于 a=1;
(5)对引用求地址,就是对目标变量求地址。&ra与&a相等。即我们常说引用名是目标变量名的一个别名。别名一词好像是说引用不占据任何内存空间。但是编译器在一般将其实现为const指针,即指向位置不可变的指针。即引用实际上与一般指针同样占用内存。
(6)不能建立引用的数组。因为数组是一个由若干个元素所组成的集合,所以无法建立一个由引用组成的集合。但是可以建立数组的引用.
例如: int& ref [3]= {2,3,5};//声明ref引用的数组错误
但是可以这样写:
const int (&ref)[3] ={2,3,5}; //gcc编译的时候加上选项 -std=c++0x    ref[0] = 35; //错误

为什么要加上const ,因为{2,3,5}此时是个字面值数组,是保存在代码段里,只读的属性,如果不加,编译错误,而且后面对ref[0]的赋值也不会成功.
需要特别强调的是引用并不产生对象的副本,仅仅是对象的同义词。
引用必须在定义时马上被初始化,因为它必须是某个东西的同义词。你不能先定义一个引用后才初始化它。
(7)一个变量可以有多个引用,但是一个引用只能有一个变量。


下面讨论引用的两个主要用途:作为函数参数以及从函数中返回左值


引用参数

1、传递可变参数
传统的c中,函数在调用时参数是通过值来传递的,这就是说函数的参数不具备返回值的能力。
所以在传统的c中,如果需要函数的参数具有返回值的能力,往往是通过指针来实现的。比如,实现
两整数变量值交换的c程序如下:
void swapint( int*a, int*b){      int temp;      temp = *a;      *a = *b;      *b = temp;}

使用引用机制后,以上程序的c++版本为:
void swapint( int &a, int &b){      int temp;      temp = a;      a = b;      b = temp;}

调用该函数的c++方法为:swapint(x,y); c++自动把x,y的地址作为参数传递给swapint函数。
2、给函数传递大型对象
当大型对象被传递给函数时,使用引用参数可使参数传递效率得到提高,因为引用并不产生对象的
副本,也就是参数传递时,对象无须复制。
下面的例子定义了一个有限整数集合的类:
const maxCard=100;Class Set{      int elems[maxCard];//集合中的元素,maxCard表示集合中元素个数的最大值。      int card;//集合中元素的个数。public:      Set(){card=0;}//构造函数      friend Setoperator *(Set,Set);//重载运算符号*,用于计算集合的交集用对象作为传值参数      //friendSetoperator*(Set&,Set&)重载运算符号*,用于计算集合的交集用对象的引用作为传值参数...}


常引用声明方式:const 类型 标识符 &引用名 = 目标变量名;
用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为const,达到了引用的安全性。
【例】:
void Test{         int a ;      const int &ra=a;      ra=1; //错误,const修饰不可改      a=1; //正确}
【例】:假设有如下函数声明:
string foo( );    void bar(string & s);那么下面的表达式将是非法的:bar(foo( ));    bar("hello world");//原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。引用型参数应该在能被定义为const的情况下,尽量定义为const 。


#include <iostream>using namespace std;int& fun(int& a){      a++;      return a;} //把a返回引用函数,也就是说这个fun()就是a的别名int main(){      int b =10;      fun(b); //同理,fun(b)就是b自增后的b的别名      cout << b <<endl;      return 0;}

【例】:
void Test{       const int a = 10;       int &b = a; //错误。如果引用成功,可通过a修改成功,而原题是const修饰b,是不可修改的。       const  int &b = a; // 正确       const int &c = 10; //正确,表示10不可修改}
【例】:
void Test{       double a = 10.34;       const int &b = a; //正确,       a = 23.13;}      // b = 12;      //如果b是a得引用(两者类型相同),则两者使用同一空间,但事实上b并不是a得引用,因为两者有不同的空间,借助一段未命名空间过度,临时变量有常性,所以要加上const修饰。

【例】:数组引用

void Test{       int a[10];       int (&ra) [10] = a;       int (&ra) [9] = a; // 错误,两者类型相同,不可引用       a [0] = 10;       ra[1] = 20;}


        引用返回值
不要返回栈内存引用
方法:(1)创建全局变量
           (2)
FunTest(&a){       int a = 10;       return a;}
         保证返回值生命周期比函数生命周期长
int & FunTest(){       int ret = 10;       return ret;}int main(){        int &r = FunTest();        printf("%d\n", r);        printf("%d\n", r);} // 10    随机值    //第一次调用时,回来就销毁ret, 重新创建栈帧, 第二次打印出随机值



引用和多态


引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例。
【例】:
class A
{};
class B
{
public A{……}
};

B b;
A &Ref = b; // 用派生类对象初始化基类对象的引用
Ref 只能用来访问派生类对象中从基类继承下来的成员,是基类引用指向派生类。如果A类中定义有虚函数,并且在B类中重写了这个虚函数,就可以通过Ref产生多态效果。
       指针和引用的区别

1.指针和引用的定义和性质区别:

(1)指针:指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已。如:

int a=1;int *p=&a;//定义了一个整形变量和一个指针变量p,p的值是a存储单元的地址。    int a=1;int &b=a;//定义了一个整形变量a和这个整形a的引用b,事实上a和b是同一个东西,在内存占有同一个存储单元。

(2)指针可以有多级,但是引用只能是一级(int **p;合法 而 int &&a是不合法的)

(3)指针的值可以为空,但是引用的值不能为NULL,并且引用在定义的时候必须初始化;

(4)指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了。

(5)"sizeof引用"得到的是所指向的变量(对象)的大小,而"sizeof指针"得到的是指针本身的大小;

(6)指针和引用的自增(++)运算意义不一样;

2.指针和引用作为函数参数进行传递时的区别:

(1)指针作为参数进行传递:

#include<iostream>using namespace std;void swap(int *a,int *b){  int temp=*a;  *a=*b;  *b=temp;}int main(void){  int a=1,b=2;  swap(&a,&b);  cout<<a<<" "<<b<<endl;  system("pause");  return 0;}

结果为2 1;

用指针传递参数,可以实现对实参进行改变的目的,是因为传递过来的是实参的地址,因此使用*a实际上是取存储实参的内存单元里的数据,即是对实参进行改变,因此可以达到目的。

再看一个程序:

#include<iostream>using namespace std;void test(int *p){  int a=1;  p=&a;  cout<<p<<" "<<*p<<endl;}int main(void){    int *p=NULL;    test(p);    if(p==NULL)    cout<<"指针p为NULL"<<endl;    return 0;}

运行结果为:

0x22ff44 1

指针p为NULL

大家可能会感到奇怪,怎么回事,不是传递的是地址么,怎么p会是NULL?事实上,在main函数中声明了一个指针p,并赋值为NULL,当调用test函数时,事实上传递的也是地址,只不过传递的是指地址。也就是说将指针作为参数进行传递时,事实上也是值传递,只不过传递的是地址。当把指针作为参数进行传递时,也是将实参的一个拷贝传递给形参,即上面程序main函数中的p何test函数中使用的p不是同一个变量,存储2个变量p的单元也不相同(只是2个p指向同一个存储单元),那么在test函数中对p进行修改,并不会影响到main函数中的p的值。

如果要想达到也同时修改的目的的话,就得使用引用了。

2.将引用作为函数的参数进行传递。

在讲引用作为函数参数进行传递时,实质上传递的是实参本身,即传递进来的不是实参的一个拷贝,因此对形参的修改其实是对实参的修改,所以在用引用进行参数传递时,不仅节约时间,而且可以节约空间。

看下面这个程序:

#include<iostream>using namespace std;void test(int &a){  cout<<&a<<" "<<a<<endl;}int main(void){    int a=1;    cout<<&a<<" "<<a<<endl;    test(a);    system("pause");    return 0;}

输出结果为: 0x22ff44 1

          0x22ff44 1

再看下这个程序:

这足以说明用引用进行参数传递时,事实上传递的是实参本身,而不是拷贝。

所以在上述要达到同时修改指针的目的的话,就得使用引用了。

#include<iostream>using namespace std;void test(int *&p){  int a=1;  p=&a;  cout<<p<<" "<<*p<<endl;}int main(void){    int *p=NULL;    test(p);    if(p!=NULL)    cout<<"指针p不为NULL"<<endl;    system("pause");    return 0;}

输出结果为:0x22ff44 1

         指针p不为NULL









0 1
原创粉丝点击