zoukankan      html  css  js  c++  java
  • C++中引用和指针的区别

    c++中的引用详解

     

    什么是引用?

    引用是C++语言的一个特殊的数据类型描述,用于在程序的不同的部分使用两个以上的变量名指向同一块地址,使得对其中任何一个变量的操作实际上都是对同一地址单元进行的。

    使用时的注意事项:

    引用的特点:

    1.一个变量可取多个别名。

    2.引用必须初始化。

    3.引用只能在初始化的时候引用一次 ,不能更改为转而引用其他变量。

    4.对引用进行操作,实际上就是对被引用的变量进行操作,

    5.引用仅是变量的别名,而不是实实在在地定义了一个变量,因此引用本身并不占用内存,而是和目标变量共同指向目标变量的内存地址.声明引用时,目标的存储状态不会改变

    6.表达式中的取地址符&不再是取变量的地址,而是用来表示该变量是引用类型的变量。

    引用的应用:

    1.基础引用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void TestReference1 () 
         int a = 1; 
         int& b = a; 
        b=2;
         cout<<"a:address->" <<&a<< endl; 
         cout<<"b:address->" <<&b<< endl; 
    //输出结果a,b都等于2
     
    }

    2.const引用:

     常引用声明方式:const  类型标识符  &引用名 = 目标变量名;

      用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为const,达到了引用的安全性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include<iostream.h>
    void main(){
        int a=1;
        int &b=a;
        b=2;
        cout<<"a="<<a<<endl;//2
        int c=1;
        const int &d=c;
    //    d=2;//编译错误 error C2166: l-value specifies const object
        c=2;//正确
    }

      这不光是让代码更健壮,也有其它方面的需求。

     【例4】:假设有如下函数声明:

        string foo();

        void bar(string &s);

      那么下面的表达式将是非法的:

        bar(foo());

        bar("hello world");

      原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。

      引用型参数应该在能被定义为const的情况下,尽量定义为const 。

    3.引用作参数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    1.【值传递】
    void Swap (int left, int right)  {                              
         int temp = left; 
         left = right ; 
         right = temp ; 
       
    2.【引用传递】
    void Swap (int& left, int& right)//使用引用的话,不做临时拷贝,&的使用说明此处只是原参数的另一个名字而已,所以修改时直接在原参数的基础上修改变量值。 
         int temp = left; 
         right = left ; 
         left = temp ; 
       
    3.【指针传递】 
    void Swap (int* pLeft, int* pRight)//传入的是地址,因为地址是唯一的,所以指针通过地址的访问进而可修改其内容。 
         int temp = *pLeft; 
         *pLeft = *pRight; 
         *pRight = temp; 
    }

    //函数参数的传递方式:值传递,地址传递,引用(c++),如何选择呢?

    //看是否要修改值,要修改,用指针或者引用,不必修改,则用值传递

    //对于指针和引用,看传进来的是不是地址或者时new 出来的,是则选指针,不是则选择引用

    1
     

    由上例可以看出:

      (1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。

      (2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。

      (3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;

    另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。

       如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。

    4. 引用作为返回值

    说明:

      (1)以引用返回函数值,定义函数时需要在函数名前加&

      (2)用引用返回一个函数值的最大好处是,在内存中不产生被返回值的副本。

     以下程序中定义了一个普通的函数fn1(它用返回值的方法返回函数值),另外一个函数fn2,它以引用的方法返回函数值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    #include <iostream>
    using namespace std;
     float temp;//定义全局变量temp
     float fn1(float r);//声明函数fn1
     float &fn2(float r);//声明函数fn2 r
     float fn1(float r){//定义函数fn1,它以返回值的方法返回函数值
        temp=(float)(r*r*3.14);
        return temp;
      }
      
      float &fn2(float r){//定义函数fn2,它以引用方式返回函数值
         temp=(float)(r*r*3.14);
         return temp;
     }
      
     int main(){
         float e=10.0;
         float a=fn1(10.0);//第1种情况,系统生成要返回值的副本(即临时变量)
        // float &b=fn1(10.0); //第2种情况,可能会出错(不同 C++系统有不同规定)
       /*error: invalid initialization of non-const reference of type 'float&' from an rvalue of type 'float'
       */
         //不能从被调函数中返回一个临时变量或局部变量的引用
         float c=fn2(10.0);//第3种情况,系统不生成返回值的副本
         //可以从被调函数中返回一个全局变量的引用
         float &d=fn2(10.0); //第4种情况,系统不生成返回值的副本
         e=d;
      
         cout<<"a="<<a<<",c="<<c<<",d="<<d<<",e="<<e<<endl;
         //a=314,c=314,d=314
         return 0;
       } 

    引用作为返回值,必须遵守以下规则:

      (1)不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。

      (2)不能返回函数内部new分配的内存的引用。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。

      (3)可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。

      (4)引用与一些操作符的重载:流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << "hello" << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。

     引用虽方便,使用须谨慎:

    (1)&在这里不是求地址运算,而是起标识作用。

    (2)类型标识符是指目标变量的类型。

    (3)声明引用时,必须同时对其进行初始化。

    (4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。

    (6)不能建立引用的数组。因为数组是一个由若干个元素所组成的集合,所以无法建立一个由引用组成的集合,但是可以建立数组的引用。

    (7)引用常见的使用用途:作为函数的参数、函数的返回值。

    * 引用和指针的区别和联系(笔试热点)

    ★不同点:

    1.指针的作用有:(1).装地址(2).不同变量访问同一块空间

     引用:只可以实现用不同变量访问同一块空间

     2.引用定义了就一定要初始化,指针不用

     3.引用初始化后就不能再引用其他空间了

     4. 没有空的引用,指针可以为空(引用一定要保证引用的时一个合法的存储单元)(在c++中NULL就是0,而c中的NULL是空地址(void*)0)

     5. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;

     6. 指针和引用的自增(++)运算意义不一样;引用是值的增减,指针是地址的偏移

     7.从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。

    ★相同点:两者都是地址的概念,指针指向一块儿内存,其内容为所指内存的地址;引用是某块儿内存的别名。

    指针比引用更为灵活,但是其风险也很大。使用指针时一定要检查指针是否为空(NULL),且空间回收后指针最好置零,以免野指针的发生造成内存泄漏等问题。

    本博客转载于:https://www.cnblogs.com/curo0119/p/8530038.html

  • 相关阅读:
    线性最大子数组的求法(二)
    高难度智力题
    职业规划
    良好的学习习惯
    毕业生面试绝招
    编写Java程序最容易犯的21种错误
    spring02
    spring_01
    用soapUI生成客户端代码
    idea创建git分支
  • 原文地址:https://www.cnblogs.com/shiheyuanfang/p/13353640.html
Copyright © 2011-2022 走看看