0%

专题知识点6:引用摘抄

2020年4月26日 下午3:50

复习: 2020年5月16日 下午5:27

  1. C++的学习最关键是是要站在需求的角度,理解C++为什么要设计这个关键字,它是为了遇到了什么样的需求导致了它想设计这样一个语法
  2. 引用就是这样:
    1. 首先记住一句话:引用的本质是为了解决拷贝问题,提高效率和空间
    2. 引用分为左值引用和右值引用,也同样是为了解决多余拷贝的问题
  3. 至于,下面的具体代码分析,我觉得其实一个综合能力的考察,其中涉及到
    1. C++内存模型:不同的对象,对象生命周期,对象名字的作用域是不同的
    2. 对函数返回值的理解
      1. 我自己的一个技巧是:把函数当成一个变量的定义来理解,这样就能把”函数“去掉
      2. eg:
        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
        32
        33
        34
        35
        36
        float &fn2(float r){ //&说明返回的是temp的引用,换句话说就是返回temp本身
        temp=r*r*3.14;
        return temp;
        }

        当出现我们把函数的返回值赋值给其他变量时
        将函数float &fn2(float r){} 抽象为 float & X
        注意:这里的X,当做是一个匿名变量,类比匿名函数lambda就能理解

        # case:1
        float &fn2(float r){
        temp=r*r*3.14;
        return temp;
        }
        float c = fn2(5.0);
        ||
        转换
        \||/
        float& x = temp;// 步骤1:将函数转为为变量理解
        float c = x;// 步骤2:函数返回 fn2(5.0) 转换为 x
        //步骤3:步骤一的变量,是临时变量,不论是不是引用类型,都会消失
        注:这里解释x这个引用变量内存被消失,也不影响,因为没有对象指向x,步骤2中x指向的是temp,而不是临时变量x!


        # case:2
        float fn1(float r){
        temp=r*r*3.14;
        return temp;
        }
        float &b=fn1(5.0)//[Error]
        ||
        转换
        \||/
        float x = temp;
        float &c = x;
        注:case2与case1最大的区别是:c这个引用指向是是临时变量x!而不是temp,所以会报错。

具体分析:

C/C++ 引用作为函数的返回值_C/C++_Jeff_的博客-CSDN博客

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//代码来源:RUNOOB
#include<iostream>
using namespace std;
float temp;
float fn1(float r){
temp=r*r*3.14;
return temp;
}
float &fn2(float r){ //&说明返回的是temp的引用,换句话说就是返回temp本身
temp=r*r*3.14;
return temp;
}
int main(){
float a=fn1(5.0); //case 1:返回值
//float &b=fn1(5.0); //case 2:用函数的返回值作为引用的初始化值 [Error] invalid initialization of non-const reference of type 'float&' from an rvalue of type 'float'
//(有些编译器可以成功编译该语句,但会给出一个warning)
float c=fn2(5.0);//case 3:返回引用
float &d=fn2(5.0);//case 4:用函数返回的引用作为新引用的初始化值
cout<<a<<endl;//78.5
//cout<<b<<endl;//78.5
cout<<c<<endl;//78.5
cout<<d<<endl;//78.5
return 0;
}
  1. case 1:用返回值方式调用函数(如下图,图片来源:伯乐在线):
    1. 返回全局变量temp的值时,C++会在内存中创建临时变量并将temp的值拷贝给该临时变量。当返回到主函数main后,赋值语句a=fn1(5.0)会把临时变量的值再拷贝给变量a
  2. case 2:用函数的返回值初始化引用的方式调用函数(如下图,图片来源:伯乐在线)
    1. 这种情况下,函数fn1()是以值方式返回到,返回时,首先拷贝temp的值给临时变量。返回到主函数后,用临时变量来初始化引用变量b,使得b成为该临时变量到的别名。由于临时变量的作用域短暂(在C++标准中,临时变量或对象的生命周期在一个完整的语句表达式结束后便宣告结束,也就是在语句float &b=fn1(5.0);之后) ,所以b面临无效的危险,很有可能以后的值是个无法确定的值。
    2. 如果真的希望用函数的返回值来初始化一个引用,应当先创建一个变量,将函数的返回值赋给这个变量,然后再用该变量来初始化引用:
      1
      2
      int x=fn1(5.0);
      int &b=x;
  3. case 3:用返回引用的方式调用函数(如下图,图片来源:伯乐在线)
    1. 这种情况下,函数fn2()的返回值不产生副本,而是直接将变量temp返回给主函数,即主函数的赋值语句中的左值是直接从变量temp中拷贝而来(也就是说c只是变量temp的一个拷贝而非别名) ,这样就避免了临时变量的产生。尤其当变量temp是一个用户自定义的类的对象时,这样还避免了调用类中的拷贝构造函数在内存中创建临时对象的过程,提高了程序的时间和空间的使用效率。
  4. case 4:用函数返回的引用作为新引用的初始化值的方式来调用函数(如下图,图片来源:伯乐在线)
    1. 这种情况下,函数fn2()的返回值不产生副本,而是直接将变量temp返回给主函数。在主函数中,一个引用声明d用该返回值初始化,也就是说此时d成为变量temp的别名。由于temp是全局变量,所以在d的有效期内temp始终保持有效,故这种做法是安全的。

不要返回局部变量

  1. 不能返回局部变量的引用。如上面的例子,如果temp是局部变量,那么它会在函数返回后被销毁,此时对temp的引用就会成为“无所指”的引用,程序会进入未知状态。
  2. 不能返回函数内部通过new分配的内存的引用。虽然不存在局部变量的被动销毁问题,但如果被返回的函数的引用只是作为一个临时变量出现,而没有将其赋值给一个实际的变量,那么就可能造成这个引用所指向的空间(有new分配)无法释放的情况(由于没有具体的变量名,故无法用delete手动释放该内存),从而造成内存泄漏。因此应当避免这种情况的发生
  3. 当返回类成员的引用时,最好是const引用。这样可以避免在无意的情况下破坏该类的成员。
    1. 类的成员函数返回引用所引发的问题_AderStep-CSDN博客
    2. 如果我们返回类成员的引用,那么相当于给了用户写这个赶回值的接口,通过这个返回的引用我们可以直接修改成员变量的值,即使是private
      1. base.GetX( ) = 10;
    3. 这时候,我们就需要使用const int& GetX( )来定义成员函数,这样只能读,不能写,这样的语句就会报错base.GetX( ) = 10;
  4. 可以用函数返回的引用作为赋值表达式中的左值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include<iostream>
    using namespace std;
    int value[10];
    int error=-1;
    int &func(int n){
    if(n>=0&&n<=9)
    return value[n];//返回的引用所绑定的变量一定是全局变量,不能是函数中定义的局部变量
    else
    return error;
    }

    int main(){
    func(0)=10;
    func(4)=12;
    cout<<value[0]<<endl;
    cout<<value[4]<<endl;
    return 0;
    }