reference

关于reference的一些思考(?)

引用

我常常被指针和引用所搞混以至于在c++中经常胡乱使用这两个东西。指针是我比较熟悉的东西,它是一个变量,占有一个内存区域,只不过其中的值是其他变量的地址。引用实际上和指针很相似,我是通过将两者都转换为汇编观察他们的不同,我尝试的内容不多,我最直观的理解是把引用当作一个带有限制的指针:

  • 你无法取得引用的地址,尽管引用是占据内存区域的,这就使得你弄不出引用的引用来。如果观察汇编会发现如果对引用进行取址&操作,返回的是引用所指向的变量的地址

    1
    2
    3
    4
    5
    int a;
    int& ra = a;
    // 两者的输出是相同的
    std::cout << &ra;
    std::cout << &a;
  • 你无法改变一个引用的指向

    1
    2
    3
    int a, b;
    int& ra = a;
    ra = b; // 改变了a的值而不是让ra指向了b
  • 一般情况下不是NULLABLE的
    有了这些限制的情况下,引用指向性的能力减弱了,但是出现错误的概率也减小了

    1
    2
    3
    4
    A a;
    A* pa = &a;
    pa = NULL;
    pa->f() // ub

右值引用

这个时候右值引用的概念又让我困惑了,于是我尝试按照wiki上尝试从使用场景出发…

1
2
A a1, a2, a3;
a1 = a2 + a3; // 假设A重载了+并返回一个A

a2 + a3的结果是一个临时变量,其结果会被拷贝到a1中,其实这个a2 + a3的结果马上就会消亡,此时直接将其值”移动”到a1中似乎是更为合理的,反正不要浪费嘛… 为什么要额外拷贝一下。

然而c++中此时似乎需要有overload = operator来提供移动赋值语义,不然怎么区分拷贝赋值呢,于是就引入了移动构造和移动赋值语句。

说到这我就想起来c++里还有lvalue/prvalue/xvalue/glvalue/rvalue的概念似乎对理解右值引用比较有帮助

1
2
3
    glvalue     rvalue
/ \ / \
lvalue xvalue prvalue

定义里是这么说的,传统左值是带有名字的,可以使用&操作获取地址的值
传统右值呢就是非传统左值。通常是临时变量(运算结果啦 函数返回值啦),字面量(字面量会被用来构造一个临时变量)

1
2
3
4
5
6
7
8
9
10
class A{};

A f() {
return A();
}

int main() {
A a, a1;
A* pa = &(f()); // error: taking address of temporary [-fpermissive]
}

表达式(expression)是

  • 一系列操作符与函数在表达式上运算的结果
  • 字面量
  • 变量(变量作为表达式时候去掉其引用类型比如int&变为int))

表达式具有类型(type),但不能是引用类型和值类别(value category)
表达式的类型取决于函数运算的结果(把操作符也看做函数运算)
表达式的值类别帮助确定拷贝和移动语义