什么是左值、右值

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

为什么引入右值引用?
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. hdu1827之强联通

    Summer Holiday Time Limit: 10000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  2. Scala 基础新手教程

    1.前言 近期在參加Hadoop和Spark培训.须要使用Scala,自学了一下作为入门.这里作一个记录. 2.下载 1) 在scala官网下载.地址: http://www.scala-lang.o ...

  3. R语言数据分析系列之五

    R语言数据分析系列之五 -- by comaple.zhang 本节来讨论一下R语言的基本图形展示,先来看一张效果图吧. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi ...

  4. 解读Unity中的CG编写Shader系列1——初识CG

    CG=C for Graphics  用于计算机图形编程的C语言超集 前提知识点: 1.CG代码必须用 CGPROGRAM ... ENDCG括起来 2.顶点着色器与片段着色器的主函数名称可任意,但须 ...

  5. Libx264 编码错误 Input picture width(320) is greater than stride (0)

    Ffmpeg libx264编码出现 Input picture width(320) is greater than stride (0),问题出在视频格式不正确. libx264 编码要求输入源的 ...

  6. 【转载】FAT32文件系统详解

    硬盘是用来存储数据的,为了使用和管理方便,这些数据以文件的形式存储在硬盘上.任何操作系统都有自己的文件管理系统,不同的文件系统又有各自不同的逻辑组织方式.例如:常见的文件系统有FAT,NTFS,EXT ...

  7. [框架安装趟雷指南]Ubuntu+1060+cuda+cudnn+Keras+TH+TF+MXnet

    [框架安装趟雷指南]Ubuntu+1060+cuda+cudnn+Keras+TH+TF+MXnet https://zhuanlan.zhihu.com/p/23480983 天清 9 个月前 写这 ...

  8. 李洪强iOS开发之 - 项目架构

    李洪强iOS开发之 - 项目架构 01 - 在Appdelegate中设置跟控制器 //导入头文件

  9. javaScript中innerHTML,innerText,outerHTML,outerText的区别

    开头说下innerText和outerText只在chrome浏览器中有效 定义和用法 innerHTML 属性设置或返回表格行的开始和结束标签之间的 HTML,包括标签. 来看代码 <!DOC ...

  10. linux cat命令(转载)

    来源:http://blog.sina.com.cn/s/blog_52f6ead0010127xm.html 1.cat 显示文件连接文件内容的工具: cat 是一个文本文件查看和连接工具. 查看一 ...