C++11:引用、左值引用、右值引用、常引用区别

1 引用

引用是一种更安全的“指针”,引用的底层是以指针的方式实现的。针对引用使用的地方,系统会自带解引用的过程。同时它也占据系统内存空间。

引用:必须初始化、不能二次定义引用变量。

int a = 5;
int& b ; //error!没有初始化
int& b = 3; //true
int &a = 1;
int &a = 2; //error!多次重定义
int a = 10; //这里的a是左值,10是右值

2 左值引用、右值引用

首先了解左值、右值的概念:

  • 左值:能用取地址&运算符获取对象的内存地址,表达式结束后依然存在的持久化内存对象,左值可出现在等式左边,也可出现在等式右边。
  • 右值:不能用取地址&符号说明的对象,只能存在于等式右边,使用后会自动消除。
    C++98之前能够使用的引用是左值引用,右值引用是C++11提出的概念,常用于代码优化中使用,赋值号左边的参数我们称呼为左值,右边的参数为右值。
    同时定义:
左值引用:Type& 引用名 =  左值表达式;
右值引用:Type&& 引用名 = 右值表达式;

如:

int i = 500;
int& r = i; //正确的左值引用
int& r = 500; //错误,500是一个右值

int&& r = 500; //正确的右值引用
int&& r = 500 * i ;//正确的右值引用

右值引用主要实现转移语义完美转发

2.1 转移语义

移动语义出现之前主要是拷贝语义
拷贝语义:对象的移动依靠内存拷贝实现,为新对象分配内存空间,拷贝旧对象进内存空间,删除旧对象。
移动语义:转移旧对象所有权到新对象,避免旧对象的复制和释放
因此移动语义可以极大提高效率。

//基于 “拷贝语义” 的 swap 函数
template <typename T>
void swap(T &a, T &b){
    T tmp = a;  //拷贝构造:将 a 拷贝至 tmp
    a = b;      //拷贝赋值:将 b 拷贝至 a
    b = tmp;    //拷贝赋值:将 tmp 拷贝至 b
}
//基于 “移动语义” 的 swap 函数
template <typename T>
void swap(T &&a, T &&b){
    T tmp(std::move(a));    //移动构造:将 a 的所有权转让给局部对象 tmp
    a = std::move(b);       //移动赋值:将 b 的所有权转让给 a
    b = std::move(tmp);     //移动赋值:将 tmp 的所有权转让给 b
}

  std::move 标准库函数可以接受一个左值并返回该左值的右值引用,因此方法二比方法一少了三次拷贝数据过程,极大提高swap函数的效率。

2.2 完美转发

完美转发:将函数参数传递给另一个参数时,参数的类型、值以及所有属性都不能改变,这在泛型编程当中有着广泛的应用。在 C++ 中,函数参数除了类型和值以外,还有 const 和 non-const 以及 左值和右值 两组属性。

通俗理解就是一个函数可以实现多种功能,减少函数的重写和重载,提高泛用性。

比如:

//代码 1
template <typename T> void f1(T &t){
    cout << "hello world" << endl;
}
template <typename T> void f1(const T &t){
    cout << "hello world" << endl;
}
int main(void){
    int i = 10;
    const int ci = 11;
    f1(i);  //调用 f1(int&),模板参数 T 是 int
    f1(ci); //调用 f1(const int&),模板参数 T 是 int
    f1(31); //调用 f1(const int&),模板参数 T 是 int
    return 0;
}

若实现f1不同参数传入,需要重载函数f1,如参数增加会提高维护量。

//代码 2
template <typename T> void f1(T &&t){
    cout << "hello world" << endl;
}
int main(void){
    int i = 10;
    const int ci = 11;
    f1(i);  //实参是一个左值,模板参数 T 是 int&
    f1(ci); //实参是一个左值,模板参数 T 是 const int&
    f1(31); //实参是一个右值,模板参数 T 是 int
    return 0;
}

模板函数C++11在原有绑定规则新增了引用折叠规则:

  • 若模板函数 func 的形参类型为 T&, 则不论传递给 func 的实参是左值还是右值,一律统统折叠为左值引用
  • 若模板函数 func 的形参类型为 T&&, 则传递给函数的左值实参折叠为左值引用,右值实参折叠为右值引用
    因此在定义函数模板时使用右值引用做形参,能够保证参数的属性(const 属性和 左/右值属性)不发生变化。极大的减少了工作量。

3 常引用

const 类型& 变量名; 

  引用绑定一个变量后就不能更改,不能解绑。const则限定了引用类型。
  通俗来说使得引用的对象不能修改,不能对引用值修改,保证了引用变量的安全性。
如下例子:

int a = 5;
const int& b = a;
b = 2; //error!不能对常引用类型b进行修改,提示表达式必须为可修改左值

这里的a是被常引用类型绑定的,可以这样理解,int& b是一个地址,添加const修饰符之后这个地址变成了const类型。保证了变量的只读,在函数设计中常采用常引用传参返回值为引用的方法,因为若传入的是形参,函数体内部会对形参产生一个临时内存地址,所以对形参的改变实际是对另一个临时变量的修改,不会影响到实参。但这种处理方式会让函数运行更缓慢,涉及到内存的创建和销毁。
因此若函数传入参数并不涉及影响到传参值时,使用常引用作为函数输入能够更高效

4 参考

[1] C++ 引用类型 — 左值引用、常引用和右值引用
[2] C++中常引用的作用和区别
[3] C++引用及常引用
[4] 左值引用和右值引用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

面条有点辣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值