三、引用#

1. 什么是引用#

引用,顾名思义是某一个变量或对象的别名,对引用的操作与对其所绑定的变量或对象的操作完全等价。引用在使用时,有几个要注意的地方:

  1. &不是求地址运算符,而是起到标志作用
  2. 引用的类型和绑定的变量类型必须相同 (指针也一样)
  3. 声明引用的同时,必须对其进行初始化,否则报错。
  4. 引用相当于变量或者某个对象的别名,但是不能再将已有的引用名作为其他变量或者对象的名字或别名 (和指针不一样。)
  5. 引用不是定义一个新的变量或对象,因此内存不会为引用开辟新的空间存储这个引用
  6. 不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。
 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
#include<iostream>
using namespace std;

int main(){
    //语法:类型 &引用名=目标变量名;
    int a = 3 ; 
    int &b = a ;

    //此时a也会变成33
    b = 33;



    vector<string> names {"张三"  "李四" ,"王五"};

    // 这仅仅是改变了str的值,并不会改变vector里面的元素
    for(auto str:names){
        str = "赵六"; 
    }

    //此处不会产生新的变量,所以修改的都是vector里面的元素
    for(auto &str : names){
        str = "赵六" ;
    }
    return 0 ;    
}

2. 左值和右值#

C++的表达式要么是左值 lvalue,要么是右值 rvalue 这两个名词是从C语言继承过来的。左值可以出现在赋值语句的左侧和右侧,右值只能出现在右侧。最长见到左值和右值的地方,是在函数的参数以及报错的日志信息里面。

不能简单的以等号的左右来判断是否是左值还是右值

判断是否是左值,有一个简单的办法,就是看看能否取它的地址,能取地址的就是左值 。使用排除法,其他的即为右值。

左值可看作是 对象,右值可看作是 而有时候对象也可以是左值也可以右值,一句话概括:当一个对象成为右值时,使用的是它的值(内容) , 而成为左值时,使用的是它的身份(在内存中的位置)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
//只能把左值赋值给引用,不能把右值赋给引用
int square(int &n){
    return n * n ; 
}

int main(){
    int x = 1000;  // x是一个左值, 而 1000 是一个右值。
    x = 1000 + 20 ;  //x 是左值, 1000 + 20 是右值,

    int b = 10; // b 是一个左值, 10 是一个右值
    b = x ;   //b是一个左值, 而 x依然是一个左值。


    int num = 10 ;
    square(num) ; //正确

    square(5) //错误。因为5是右值 ,不能赋值引用。
    return 0;    
}

3. 左值引用#

平常所说的引用,实际上指的就是左值引用 lvalue reference , 常用单个 & 来表示。 左值引用只能接收左值,不能接收右值。const 关键字会让左值引用变得不同,它可以接收右值

 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
#include <iostream>
using namespace std;

//函数原型
int add(int &num1);
void print(vector<int> &scores);

int main(){

    int a = 3 ;
    int &b = a ; //ar是一个左引用,实际上可以看成是a的一个别名。

    // 这是不允许的。
    // 1. 从引用层面理解的话是: 引用接收的一个变量,给某个变量起别名
    // 2. 从左右值的层面理解是,这是一个左值引用,只能接收左值。 3 属于右值。
    int &c = 3 ;  //错误!


    int a = 3 ;
    add(a) ; //正确
    add(3) ; //错误! 参数要求的是一个左值引用,只能赋值左值 ,3 属于右值


    vector<int> scores{60,70,80,90};
    print(scores); //正确
    print({60,70,80,90}); //错误!
}

4. 右值引用#

为了支持移动操作,在c++11版本,增加了右值引用。右值引用一般用于绑定到一个即将销毁的对象,所以右值引用又通常出现在移动构造函数中。

看完下面的例子,左值和右值基本就清楚了,左值具有持久的状态,有独立的内存空间,右值要么是字面常量,要么就是表达式求值过程中创建的临时对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int main(){

    int i = 66;
    int &r = i ; //r 是一个左引用,绑定左值 i

    int &&rr = i ; //rr是一个右引用,绑定到左值i , 错误! 
    int &r2 = i*42 ; //  r2 是一个左引用, 而i*42是一个表达式,计算出来的结果是一个右值。 错误!

    const int &r3 = i*42; // 可以将const的引用,绑定到右值 正确 
    int &&rr2 = i*42 ; // 右引用,绑定右值 正确

    return 0 ;    
}
  • 示例
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<vector>
using namespace std;

//函数原型
int add(int &&num1);
void print(vector<int> &&scores);

int main(){
    int a = 3 ;
    add(a) ; //错误!参数要求的是一个右值引用,只能赋值右值
    add(3) ; //正确! 


    vector<int> scores{60,70,80,90};

    //print接收的是一个右值,此处的scores是一个左值。
    print(scores); //错误!

    //{60,70,80,90} 属于右值。
    print({60,70,80,90}); //正确

    return 0 ;
}