C++引用、左值和右值

目录

一,引用

1,函数参数引用传递

2,引用型变量

3,函数返回引用型

二,左值和右值

1,左值和右值

2,常见的左值和右值

(1)变量、字面量

(2)表达式

(3)else

3,取址和解地址

4,引用和const

5,move

6,右值引用

三,拷贝构造和移动构造

1,类默认生成的拷贝构造和移动构造函数

2,noexcept

3,vector的移动扩增


一,引用

1,函数参数引用传递

#include<iostream>
using namespace std;

void f(int& x)
{
    x--;
}

int main()
{
    int x=3;
    f(x);
    cout<<x;
    return 0;
}

输出2

常见错误:

void f(int& x)
{
    x--;
}

int main()
{
    f(3);
    return 0;
}

2,引用型变量

#include<iostream>
using namespace std;

int main()
{
    int x=3;
    int& x2=x;
    x2++;
    cout<<x;
    return 0;
}

输出4

常见错误:

int main()
{
    int& x = 3;
    return 0;
}

3,函数返回引用型

#include<iostream>
using namespace std;

int& f(int arr[],int id)
{
    if(id>sizeof(arr)/sizeof(int))id=0;
    return arr[id];
}

int main()
{
    int arr[]={1,2,3};
    f(arr,1)++;
    f(arr,10)++;
    cout<<arr[0]<<arr[1]<<arr[2];
    return 0;
}

输出233

二,左值和右值

1,左值和右值

(1)左值就是有内存地址的,可以用&取地址,右值是没有内存地址的,也就不能取址。

(2)非const的左值可以放在赋值运算符的左边,const左值不行,右值不行。

    int x = 1; // 正确,x是左值,1是右值
    1 = x; // 错误
    int a = x; // 正确, a是左值
    a + 1 = 0; // 错误,a+1是右值

(3)左值和右值描述的其实是变量是否作为一个对象保存在计算机中,和存储的位置是不是在内存中没有绝对的关系

比如左值变量用register修饰,虽然保存在寄存器中,但仍然是左值。而如果对register变量取址,那么它一开始就不会被放入寄存器。

(4)右值分为纯右值、将亡值。

2,常见的左值和右值

(1)变量、字面量

变量是左值,const变量是const左值。

string字面量是const左值,其他字面值是右值

(2)表达式

有的表达式产生的是左值,有的表达式产生的是右值。

int main()
{
    int x = 1, y = 2;
    x + 1; // 右值
    x + y; // 右值
    ++x; //左值引用
    x++; // 右值
    &x; // 右值
    x=1; // 左值引用
    cout<<x; // 左值引用

    int* a;
    *a; // 左值
    a + 1; // 右值
    *(a + 1); // 左值
    return 0;
}

(3)else

一般匿名变量指的就是表达式产生的右值,但也不绝对,例如*(a + 1)这个匿名变量就是左值。

函数是左值。

左值对象的成员是左值。

3,取址和解地址

取址 & 是根据左值得到右值,解地址 * 是根据左值或者右值得到左值。

即取址 & 只能用于左值,而 * 都可以。

4,引用和const

& 作为引用时,是可以用于右值的。

void f(const int &x)
{
    cout << x;
}

int main()
{
    f(3);
    return 0;
}

即,非const的左值引用、const的左值和右值引用都是可以的,非const的右值引用是不行的

而这居然是可以的:

void f(const int &constant)
{
    cout << constant;
    const int* const_p = &constant;
    int* modifier = const_cast<int*>(const_p);
    (*modifier)++;
    cout << constant;
}

int main()
{
    f(3);
    return 0;
}

输出34

而如果是这样:

void f(const int &constant)
{
    const int* const_p = &constant;
    int* modifier = const_cast<int*>(const_p);
    (*modifier)++;
}

int main()
{
    const int x = 3;
    f(x);
    cout << x;
    return 0;
}

输出3

说明参数相当于是按值传递的。

换个角度看,f(3)其实是右值引用,f(x)是左值引用。

5,move

move函数可以实现把左值换成右值

template <class _Ty>
_NODISCARD constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept { // forward _Arg as movable
    return static_cast<remove_reference_t<_Ty>&&>(_Arg);
}

其实也就是把一个变量本身作为返回值,函数的返回值自然就是右值。

6,右值引用

用&&做单目运算符时,表示右值引用。

Compiler Explorer可以在线编译代码。

下面2个代码的汇编是一样的:

void f(int &&constant)
{
    return;
}
 
int main()
{
    f(3);
    return 0;
}

void f(const int &constant)
{
    return;
}
 
int main()
{
    f(3);
    return 0;
}

对于右值,其实只是没有给它取名字而已,和左值变量的主要区别在于内存,而在寄存器处,对于计算来说是一样的。

而右值引用这个新语法,使得我们可以像左值引用一样,直接引用一个变量而不用copy,从而高效引用右值,这就是移动语义、完美转发。

从网上盗了一张图:

这里写图片描述

三,拷贝构造和移动构造

1,类默认生成的拷贝构造和移动构造函数

参考六个默认函数

2,noexcept

析构函数、移动构造函数、移动赋值运算符、swap函数一般不允许抛出异常。

3,vector的移动扩增

vector添加元素时,可能会发生空间倍增。

发生倍增时,可能发生元素移动,也可能发生元素拷贝。

如果元素类型的移动构造函数有noexcept,即保证了不会抛出异常,那么就采用移动的方式,否则就采用拷贝的方式。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值