美文网首页
二、引用

二、引用

作者: 木鱼_cc | 来源:发表于2018-06-01 19:40 被阅读0次

    1.变量名

    • 变量名实质上是一段连续存储空间的别名,是一个标号(门牌号)
    • 通过变量来申请并命名内存空间
    • 通过变量的名字可以使用存储空间

    问题:对一段连续的内存空间只能取一个别名吗?

    2.引用的概念

    变量名,本身是一段内存的引用,即别名(alias).引用可以看做是一个已定义变量的别名。

    引用的语法: Type &name = var;

    int main()
    {
         int a = 10;//在内存上开辟4个字节,a就是代表这4个字节内存的别名
         int *p = &a;//让一个指针 指向这4个字节的空间
    
        *p = 20;
    
        //C++提供了另一种对变量的间接操作的方式,就是使用引用
        int &b = a;//定义一个引用b指向a,b此时就是变量a的别名
       
        b = 40;
        cout<<"a="<<a<<endl;//输出a=40
        cout<<"b="<<b<<endl;//输出b=40
    
        cout<<"&a="<<&a<<endl;
        cout<<"&b="<<&b<<endl;//地址一样
    
    
    }
    

    3.规则

    • 引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故而类型与原类型保持一致,且不分配内存。与被引用的常量有相同的地址。
    • 声明的时候必须初始化,一经声明,不可变更。
    • 可对引用,再次引用。多次引用的结果,就是某一变量具有多个别名。
    • &符号前有数据类型时,是引用。其他皆为地址。
    int &c;//只是单纯定义一个引用,不初始化,会报错!引用一定要初始化,除了作参数
    
    int printA(int &a)//引用作为函数参数的时候,不需要初始化,因为形参一定会被赋值
    {
      cout<<"a="<<a<<endl;
      return 0;
    }
    

    4.引用作为函数参数

    struct teacher
    {
        int id;
        char name[64];
    }
    
    void printT(Teacher t)//局部临时变量,会发生值拷贝
    {
       cout<<"id="<<t.id<<endl;
       cout<<"name="<<t.name<<endl;
       t.id = 100;//修改失败!
    }
    
    void printT2(Teacher *t)
    {
       cout<<"id="<<t->id<<endl;
       cout<<"name="<<t->name<<endl;
       t->id = 100;//修改成功
    }
    
    void printT3(Teacher &t)//Teacher &t = t1;t就是t1的一个别名
    {
       cout<<"id="<<t.id<<endl;
       cout<<"name="<<t.name<<endl;
       t.id = 10;//修改成功
    }
    -------------------------------------------------------------------
    void my_swap(int a,int b)//无法实现两数据交换
    {
        int temp = a;
        a = b;
        b = temp;
    }
    
    void my_swap1(int *a,int *b)//开辟了两个指针空间实现交换
    {
        int temp = *a;
        *a = *b;
        *b = temp;
    }
    void my_swap2(int &a,int &b)
    {
        int temp = a;
        a = b;
        b = temp;
    
    }
    
    int main(void)
    {
        Teacher t1 = {1,"shang3"};
         printT(t1);
         cout<<t1.id<<endl;//输出为1
    
         printT2(&t1);
         cout<<t1.id<<endl;//输出为100
    
         printT3(t1);
         cout<<t1.id<<endl;//输出为10
    
        int a = 10;
        int b = 20;
        my_swap(a,b);
        cout<<"a="<<a<<"   "<<"b="<<b<<endl;//交换不成功
    
        my_swap1(&a,&b);
        cout<<"a="<<a<<"   "<<"b="<<b<<endl;//成功
    
        my_swap2(a,b);
        cout<<"a="<<a<<"   "<<"b="<<b<<endl;//成功
    
    
    }
    
    

    5.引用的意义

    • 引用作为其他变量的别名而存在,因此在一些场合可以代替指针
    • 引用相对于指针来说具有更好的可读性和使用性
    int main(){
         int a = 10;
         int b = 20;
         int &re = a;//引用必须被初始化
        //引用在初始化之后,不能够被改变
         re = b;//a=b 而不是让 re引用指向b
         cout<<"re="<<re<<endl;
         cout<<"a="<<a<<endl;
         cout<<"b="<<b<<endl;
        //输出re=20,a=20,b=20
    
        re = 100;
        cout<<"re="<<re<<endl;
        cout<<"a="<<a<<endl;
        cout<<"b="<<b<<endl;
        //输出re=100,a=100,b=20
    
    }
    

    6.引用的本质

    1. 引用在C++中的内部实现是一个常指针
      Type &name <=====> Type * const name
    2. C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占的空间大小与指针相同
    3. 从使用角度,引用会让人误会其只是一个别名,没有自己的存储空间。这是C++为了实用性而做出的细节隐藏。

    在研究引用的时候,可以将引用理解为一个常指针
    在理解引用的时候,可以将引用理解为一个变量的别名

    void motifyA(int * const a)//常指针,也是一个常量,也是必须要初始化,也不能被修改
    {
        *a = 100;
    }
    
    void motifyB(int &a)
    {
       a = 1000;//a实际上是一个常量指针,但是如果你给a赋值,编译器会有一个隐形的 * 操作
    }
    
    struct Teacher
    {
       int a;
       char name[64];
    };
    
    //Teacher &t没有作类型拷贝,只开辟了4字节内存用来指向传进来的便来
    //Teacher t 的话就是拷贝了传进来的参数!
    void motifyTeacher(Teacher &t)
    {
        t.id = 100;
        //如果说t是一个常指针,*t就是指针指向的内存空间 (*t).id=100;
    }
    
    
    int main(void)
    {
       int a = 20;
       motifyA(&a);
       cout<<"a = "<<a<<endl;//100
    
       motifyB(a);
       //int value --> int &a , int &a = value给引用指定指向哪个变量的时候,编译器提供又一个隐形的操作
       //a = &value;
       cout<<"a = "<<a<<endl;//1000
    
      return 0;
    }
    

    7.引用作为函数返回值

    引用作为函数返回值
    引用作为函数返回值(更细)

    int getA1()
    {
        int a = 10;
        return a;
    }//value_a = a;
    
    int &getA2()
    {
         int a = 20;
         return a;
    }//匿名引用 int &=a;  ===>  int &temp = &a;对a的地址取别名
    
    int &getBB(){
         static int b = 200;
         return b;
    }
    
    
    int main(void)
    {
        int value_a = 0;
        value_a = getA1();//正确,不是把a的空间返回来,而是把a的值拷贝回来
        cout<<"value_a="<<value_a<<endl;//10
       
        value_a = getA2();
        //int value_a = temp; //int value_a = *temp 这是一个值拷贝
        cout<<"value_a="<<value_a<<endl;//20
    
       int &r_a = getA2();//没有值拷贝操作,禁止!
       cout<<"r_a"<<r_a<<endl;//10
    
      int &r_b = getBB();//此时的r_b就是getBB中的static int b
      //当函数返回值,不是子函数局部变量,就可以用引用进行接收
       cout<<"r_b"<<r_b<<endl;//没问题
    
      r_b = 2000;//此时r_b就是getBB()中b的别名,修改r_b就是修改b
      int value_b = getBB();//会有一个临时的引用int &temp = b;value_b = *temp;发生了值拷贝
      cout<<"value_b"<<value_b<<endl;//2000
    
     getBB() = 3000;//static b = 3000,当引用作为函数的返回值的时候,只要这个引用是合法的,就可以当左值
    
    }
    

    8.指针引用

    struct Teacher
    {
       int id;
       char name[64];
    };
    
    int getTeacher(Teacher **pp)
    {
       Teacher *p = NULL;
       p = (Teacher *)malloc(sizeof(Teacher));
       if(p==NULL)
       {
          cout<<"error"<<endl;
          return -1;
       }
       memset(p,0,sizeof(Teacher));
      p->id = 30;
      strcpy(p->name,"zhang3");
    
      *pp = p;
       return 0;
    }
    
    int getTeacher02(Teacher * &tr){//此时tr就是main中的tp
        //这里就直接对外面的tp进行操作了
       //不需要像上面那样取二级指针间接操作
       tr = (Teacher*)malloc(sizeof(Teacher));
        if(tr==NULL)
       {
          cout<<"error"<<endl;
          return -1;
       }
        tr->id = 40;
        strcpy(tr->name,"li4");
    }
    
    
    void freeTeacher(Teacher **pp)
    {
        if(pp == NULL)return;
        Teacher *p = *pp;
        if(p!=NULL){
          free(p);
         *pp = NULL;
       }
    }
    
    void freeTeacher02(Teacher *&tr)
    {
       if(tr != NULL){
          free(tr);
          tr = NULL;
        }
    }
    
    int main(void)
    {
       Teacher *tp = NULL;
       
        //getTeacher(&tp);
        getTeacher02(tp);
    
        cout<<tp->id<<","<<tp->name<<endl;
       
       //freeTeacher(&tp);
       freeTeacher(tp);
    }
    
    

    9.const 引用

    const引用有较多使用。它可以防止对象的值被随意修改。因而具有一些特性。

    • const对象的引用必须是const的,将普通引用绑定到const对象是不合法的。这个原因比较简单。既然对象是const的,表示不能被修改,引用当然也不能修改,必须使用const引用。实际上,
    const int a = 1;
    int &b = a;
    这种写法是不合法的,编译不过
    
    • const引用可使用相关类型的对象(常量,非同类型的变量或表达式)初始化。这个是const引用与普通引用最大的区别。
    const int &a = 2; //合法
    const int &b = a;//也是合法的
    
    int x = 3;
    const int &re1 = x;//x可以改变,re1不可以改变
    cout<<"re1 = "<<re1<<endl; 
    
    x = 20;
    cout<<"re1 = "<<re1<<"  "<<"x = "<<x<<endl;//都是20 
    
    ------so-------
    //const引用一般用在形参上,来限制被引用的变量不能被修改
    void printX(const int &re)//re不能被改变!
    {
      cout<<"re "<<re<<endl;
    }
    ------------------------
    
    
    const int &re2 = 10;
    //用const引用一个字面量
    //当用一个const引用去引用一个字面量的时候,字面量是没有地址的
    //引用是无法对字面量取地址的,临时创建一个int temp , 10---->temp
    //const int &re2 = temp;
    //用re2就是代表temp,re2是const的引用,你无法去改变temp的值
    
    int &re2 = 10;//这样会报错!非常量引用必须是左值,左值是可以赋值的,有内存空间的
    
     cout<<"re2 "<<re2<<endl;
    

    相关文章

      网友评论

          本文标题:二、引用

          本文链接:https://www.haomeiwen.com/subject/fxkksftx.html