【C++】引用的深层理解

     

目录

       引用

为什么会有引用及概念的由来

引用型变量 和 const 型指针

常引用


引用

    为什么会有引用及概念的由来

    ① 参数传递的问题

     在c语言中仅有值传递指针传递两种方式,这两种方式在函数传参时,尤其是自定义类型的传参,暴露出一些局限性:

     值传递时会拷贝实参的副本既多占空间,又浪费时间;

     指针传递又会产生不安全的指针访问。

    解决这个问题就引入了一种新的传递方式,它本质上是指针传递,但是隐藏了间接引用运算符 “*” ,使得形参可以直接访问间接对象。

    ② 运算符重载的问题

     在顺序表中其赋值问题可以通过运算符重载来解决,但此时又暴露出c语言值传递和指针传递的深层问题。假设赋值运算符函数是值传递 

     void  operator=(SeqList  x,  SeqList  y); //赋值运算符函数声明

有顺序表L1 和 L2,如下图:

         【C++】引用的深层理解_第1张图片

此时将L1 赋值 给 L2

        L2 = L1;

等价于调用赋值运算符函数语句

        operator = (L2, L1);

首先是将赋值语句 L2=L1 的左右操作数作为实参分别给形参x 和y 的初始化:

         SeqList  x=L2; 

         SeqList  y=L1; 

注意:此时初始化是浅拷贝,赋值运算符函数执行的是深拷贝。

     执行后结果如下图所示:   【C++】引用的深层理解_第2张图片

    可以看出,L2的size的值并没有所想的那样改为4,而x的size值却改为4,这是因为在值传递中,赋值运算符函数实现的是形参 y 给形参 x 深度赋值,而不是所需要的实参 L1 给实参 L2深度赋值。

    那如果将赋值运算符函数改用指针传递

 

     void  operator=(SeqList  *x,  SeqList  *y); //赋值运算符重载函数声明

 

    这时将 L2 = L1; 赋值语句 改为 &L2 = &L1; 赋值语句,然后左右操作数&L2和&L1作为实参分别给形参x 和y 的初始化:

     

         SeqList  *x=&L2; 

         SeqList  *y=&L1;

结果如下所示:

       【C++】引用的深层理解_第3张图片

  其执行的结果正是我们需要的,但是改进后的运算符语句 &L2=&L1 显然是不合法的,因为&L2是指针常量,不能是左值。

   若要保留赋值运算符语句的形式 L2=L1,又要实行运算符函数的指针传递,就要引入一种新的传递方式,它本质上是指针传递,但是实参隐藏了取地址符 “&”

那么,综合上面两个问题的解决方案。在指针传递中,实参和形参的关系如下:

          SeqList *  x=&L2;

          const  SeqList *  y=&L1;

    要同时隐藏形参前的间接引用运算符和实参前的取地址符,而又不和值传递形式混淆,一个简便方法是去掉间接引用运算符 “*” ,把取地址运算符 “&” 移到左侧

            

          SeqList &  x=L2;

          const  SeqList &  y=L1;

     这种形式,左侧的 “&” 表明 x 和 y 本质上还是指针,但是间接引用对象 L1 和 L2 在传址时不再需要 “&” ,指针 x 和 y 在访问间接引用对象时也不在需要加引用运算符 “*”

          这时赋值运算符函数的声明就可以是下面的形式了:

           void  operator = (SeqList  &x, const SeqList  &y);

      这时的实参 L2 给 L1 赋值,就是形参 x 给 y 赋值,如下所示:

【C++】引用的深层理解_第4张图片

  图中左边 x 和 y (即灰色的) 在系统内部是指针,只在初始化的时候才以前置的符号表露;右侧表示在程序层面上是省略了间接引用运算符的指针。这便是引用。

引用型变量 和 const 型指针

  引用型变量引用,撇开内部不谈,那么所谓的引用是给已存在变量取了一个别名,这是c++对c的一个重要扩充。

   定义的格式为:

                              类型 &  引用型变量 = 被引用变量名;

代码如下:

【C++】引用的深层理解_第5张图片

    b是引用型变量,a是被引用的变量。有了上面的理解,引用型变量b在初始化时得到被引用变量a的指针。初始化之后,b始终表示被隐藏了间接引用运算符的间接引用表达式 *b, 使 b 在形式上成为 a 的别名,即 a 的同义词,访问 b 就是访问 a。

    因为引用型变量作为隐藏的指针在初始化之后不能再被直接访问,因此也就不能被修改,   它实质是隐藏了间接引用运算符的 const 型指针。 

如图所示:

        【C++】引用的深层理解_第6张图片

常引用

   先来看代码

【C++】引用的深层理解_第7张图片

    因为引用型变量的实质是 const 型指针,它和const型指针一样,可以改变被引用的变量,所以不能引用 const型变量;上面代码a是const型变量,下面的引用是非法的。

    const型变量只能被const型引用来引用;所谓const型引用,是半隐藏的全 const 型指针,它可以引用 const 型变量,也可以引用非 const 型变量,即对被引用的变量都是 “只读” 的。

如下代码

【C++】引用的深层理解_第8张图片

临时变量和const

那么如果引用型变量的类型与被引用的变量类型不一样呢,下面就来看一段代码:

【C++】引用的深层理解_第9张图片

这段代码编译会出错,因为两者的类型不匹配;那么修改一下代码来看一下

【C++】引用的深层理解_第10张图片

此时编译通过(但可能会有警告),但不管怎样编译允许通过。

上面的一段代码报错,加了const 就可以通过是什么原因呢?下面来解释一下:

如果被引用的变量与引用的变量不匹配时,c++会创建类型正确的匿名变量(即临时变量),又临时变量具有const常属性;因为一个具有const属性的常量需要const型的引用的变量来引用,因此需要加const成为const型引用变量。


你可能感兴趣的:(c++,开发语言)