什么是左值、右值

最常见的误解:
  等号左边的就是左值,等号右边的就是右值
  左值和右值都是针对表达式而言的,
  左值是指表达式结束后依然存在的持久对象
  右值是指表达式结束时就不再存在的临时对象
区分:
  能对表达式进行取地址,则为左值 ;否则为右值

为什么引入右值引用?
std::vector<String> v;
v.push_back(“hello,world”);

  • 调用 String::String(const char *);
  • 调用 String::String(const String&);
  • 调用 String::~String()

问题症结在于,临时对象的构造和析构带来了不必要的资源拷贝。
如果有一种机制,可以在语法层面识别出临时对象,在使用临时对象构造新对象(拷贝构造)的时候,将临时对象所持有的资源『转移』到新的对象中,就能消除这种不必要的拷贝。
这种语法机制就是『右值引用』。

左值引用

根据其修饰符的不同,可分为非常量左值引用和常量左值引用

int ia = 10;   int &a = ia;

const int ib = 30;   int &b = ib;

const int &ri = 20;

非常量左值引用只能绑定到非常量左值

常量左值引用可以绑定到所有类型的值,包括 非常量左值、常量左值、右值(根据语法规则,无法区分出右值)

 右值VS临时对象

vector<int> get_all_scores()
{
vector<int> vec;
vec.push_back(1);
vec.push_back(2);
return vec;
}
vector<int> vec =get_all_scores();//这里实际上调用了三次构造函数

  

右值引用

右值引用 int &&refa;
引入右值引用后,『引用』到『值』的绑定规则也得到扩充:
左值引用可以绑定到左值: int x; int &xr = x;
非常量左值引用不可以绑定到右值: int &r = 0;
常量左值引用可以绑定到左值和右值:int x; const int &cxr = x; const int &cr = 0;
右值引用可以绑定到右值:int &&r = 0;
右值引用不可以绑定到左值:int x; int &&xr = x;
常量右值引用没有现实意义(毕竟右值引用的初衷在于移动语义,而移动就意味着『修改』)。

移动语义--std::move

编译器只对右值引用才能调用转移构造函数和转移赋值函数,而所有命名对象都只能是左值引用,如果已知一个命名对象不再被使用而想对它调用转移构造函数和转移赋值函数,也就是把一个左值引用当做右值引用来使用,怎么做呢?标准库提供了函数 std::move,这个函数以非常简单的方式将左值引用转换为右值引用。

通过移动语义,我们可以在没有必要的时候避免复制。

move函数的作用是 显式的将左值转换成右值,这意味着 被强转的对象在该语句之后不再使用

对于右值引用而言,它本身是右值么?
示例
1. 字符串的定义

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <vector>
#include <string>
using std::cout;
using std::endl;
using std::vector;
using std::string; class String
{
public:
String()
: _pstr(new char[1]())
{} String(const char * pstr)
: _pstr(new char[strlen(pstr) + 1]())
{
cout << "String(const char *)" << endl;
strcpy(_pstr, pstr);
} //复制构造函数
String(const String & rhs)
: _pstr(new char[strlen(rhs._pstr) + 1]())
{
cout << "String(const String & rhs)" << endl;
strcpy(_pstr, rhs._pstr);
}
     //如果传递的是右值,而复制构造函数和移动构造函数同时存在,此时移动构造函数优先执行。
//移动构造函数 C++11
String(String && rhs)
: _pstr(rhs._pstr)
{
cout << "String(String && rhs)" << endl;
rhs._pstr = NULL;
}
//移动赋值运算符函数
String & operator=(String && rhs)
{
cout << "String & operator=(String && )" << endl;
if(this != &rhs)
{
delete [] _pstr;
_pstr = rhs._pstr;
rhs._pstr = NULL;
}
return *this;
} //赋值运算符函数
String & operator=(const String & rhs)
{
cout << "String & operator=(const String&)" << endl;
if(this != &rhs)
{
delete [] _pstr;
_pstr = new char[strlen(rhs._pstr) + 1]();
strcpy(_pstr, rhs._pstr);
}
return *this;
} ~String()
{
delete [] _pstr;
cout << "~String()" << endl;
} const char * c_str() const
{ return _pstr; } friend std::ostream & operator<<(std::ostream & os, const String & rhs); private:
char * _pstr;
};
std::ostream & operator<<(std::ostream & os, const String & rhs)
{
os << rhs._pstr;
return os;
} int test0(void)
{
//vector<String> vec;
//vec.push_back("hello,world"); String s1("hello,world");
cout << "s1 = " << s1 << endl; s1 = String("shenzhen");
cout << "s1 = " << s1 << endl;
printf("s1's address = %p\n", s1.c_str());
cout << endl; String s2("wangdao");
cout << "s2 = " << s2 << endl;
s2 = std::move(s1);//显式的将一个左值转换成右值来使用
//这意味着 被强转的对象在该语句之后不再使用 cout << "s2 = " << s2 << endl;
printf("s2's address = %p\n", s2.c_str()); cout << "s1 = " << s1 << endl; cout << "......" << endl; return 0;
} void test1(void)
{
int a = 1;
int b = 2;
&a;
&b;
//&(a+b);// error, 右值
//&(a++);// error, 右值
&(++a);
int * pFlag = &a;
&pFlag;
&(*pFlag); //&100;//error,字面值,右值
//&string("hello");//error, 右值,匿名对象,
string s1("hello");
string s2("world");
//&(s1 + s2);//error, 右值 const int & m = 1;
&m;//左值 int && n = 1;//右值引用绑定到右值
&n; //int && x = a;//error 右值引用无法绑定到左值
} void test2(void)
{
//const引用不仅可以绑定到左值,也可以绑定到右值,
//const引用无法区分出传递过来的参数到底是左值还是右值
//
//C++11引入右值引用,解决该问题
//
String && ref1 = String("hello,world");
String s1("hello");
cout << ref1 << endl; const String & ref2 = s1;
cout << ref2 << endl;
} int main(void)
{
test0();
//test1();
//test2();
return 0;
}

  

总结:

非常量左值引用只能绑定到非常量左值,不能绑定到常量左值、非常量右值和常量右值。
  1)如果允许绑定到常量左值和常量右值,则非常量左值引用可以用于修改常量左值和常量右值,
这明显违反了其常量的含义。
  2) 如果允许绑定到非常量右值,则会导致非常危险的情况出现,因为非常量右值是一个临时对象,
非常量左值引用可能会使用一个已经被销毁了的临时对象。

c++11 右值引用和移动语义的更多相关文章

  1. 关于C++11右值引用和移动语义的探究

    关于C++11右值引用和移动语义的探究

  2. C++11 右值引用和转移语义

    新特性的目的 右值引用 (Rvalue Referene) 是 C++ 新标准 (C++11, 11 代表 2011 年 ) 中引入的新特性 , 它实现了转移语义 (Move Sementics) 和 ...

  3. C++11 右值引用 与 转移语义

    新特性的目的 右值引用(R-value Reference)是C++新标准(C++11, 11代表2011年)中引入的新特性,它实现了转移语义(Move Semantics)和精确传递(Perfect ...

  4. [c++11]右值引用、移动语义和完美转发

    c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...

  5. 【转】C++11 标准新特性: 右值引用与转移语义

    VS2013出来了,对于C++来说,最大的改变莫过于对于C++11新特性的支持,在网上搜了一下C++11的介绍,发现这篇文章非常不错,分享给大家同时自己作为存档. 原文地址:http://www.ib ...

  6. C++11 标准新特性: 右值引用与转移语义

    文章出处:https://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/ 新特性的目的 右值引用 (Rvalue Referene) ...

  7. C++11新特性之右值引用(&&)、移动语义(move)、完美转换(forward)

    1. 右值引用 个人认为右值引用的目的主要是为了是减少内存拷贝,优化性能. 比如下面的代码: String Fun() { String str = "hello world"; ...

  8. [转][c++11]我理解的右值引用、移动语义和完美转发

    c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...

  9. C++11中右值引用和移动语义

    目录 左值.右值.左值引用.右值引用 右值引用和统一引用 使用右值引用,避免深拷贝,优化程序性能 std::move()移动语义 std::forward()完美转发 容器中的emplace_back ...

随机推荐

  1. vue2.0 + vux (一)Header 组件

    1.main.js import Vue from 'vue' import FastClick from 'fastclick' import VueRouter from 'vue-router' ...

  2. Linux 学习之虚拟机下的网络连接

    参考资料: http://wenku.baidu.com/link?url=_55RWvvBKQDoZjQSo-HQ3TdmLIzX1zkA_g1znCw0IXkwvxbxMiA3KfpyaL-lhv ...

  3. JavaScript插件编写指南

    在编写插件之前,大家要先了解做插件的几个要点: 插件需要满足的条件 一个可复用的插件需要满足以下条件: 插件自身的作用域与用户当前的作用域相互独立,也就是插件内部的私有变量不能影响使用者的环境变量: ...

  4. nodejs 模板字符串

    范例1: for (var i=0;i<10;i++){ var data = `公司名:${i}`; console.log(data) } 输出: 实例2: var name = '丁香医生 ...

  5. AngularJs + html 5 实现 裁剪上传

    直接上代码 directive.js app.directive('fileUploadersm', function () { return { restrict: 'E', transclude: ...

  6. python--函数嵌套 命名空间

    函数的嵌套调用 def func(): print(123) def func2(): func() print(345) func2() #结果:123 345 函数的嵌套定义 def func() ...

  7. Protostuff具体解释

    Protostuff具体解释 作者:chszs,未经博主同意不得转载. 经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs 一.Protostuff介绍 Proto ...

  8. 关于input:-webkit-autofill样式问题

    最近在整理项目的时候,遇到了一个chrome浏览器自动填充的样式问题, 用户名跟密码的input都设置为透明颜色,但是会变成黄色,打开chrome调试工具,发现有个input:-webkit-auto ...

  9. 如何在微信小程序中使用字体图标

    微信小程序中,在image标签里,可以在src中引用本地文件,但是background设置背景图或者使用字体图标的时候,却不能引用本地文件,只能用url地址的图片或字体,或者使用base64编码后的格 ...

  10. GreenPlum 安装方法详解

    一.安装环境准备 1.磁盘环境准备 磁盘分区典型配置如下: 文件系统 文件格式    大小  /        ext3   50GB,Linux系统的根目录,所有的目录都挂在这个目录下面,建议大小为 ...