【转】C++ 11 右值引用的理解

右值引用的目的之一,是为了C++中一个比较影响性能的问题:拷贝临时对象,例如,在

1 int foo(){ ... }
2 int x;
3 x = foo();  

中,在第三句中,发生了以下的事情:

1。销毁 x 所持有的资源;

2。拷贝函数 foo 返回的临时对象所持有的资源;

3。销毁 foo 返回的临时对象;

经历了三次的操作,然而实际上,如果直接将 x 指向 foo 所返回的临时对象,调用临时对象的析构函数去销毁 x 之前拥有的资源的话,省略了很多麻烦的步骤,但是由于之前的 C++ 中对右值的限制,右值是不能被取地址的,所以对右值的引用也无从谈起,新引入的“右值引用”特性既与此相关。

以下部分为引用,但貌似 CU 没把文章贴全。

http://doc.chinaunix.net/CPP/201206/2240544.shtml

C++ 11中引入的一个非常重要的概念就是右值引用。理解右值引用是学习“移动语义”(move semantics)的基础。而要理解右值引用,就必须先区分左值与右值。

对左值和右值的一个最常见的误解是:等号左边的就是左值,等号右边的就是右值。左值和右值都是针对表达式而言的,左值是指表达式结束后依然存在的持久对象,右值是指表达式结束时就不再存在的临时对象。一个区分左值与右值的便捷方法是:看能不能对表达式取地址,如果能,则为左值,否则为右值。下面给出一些例子来进行说明。

1 int a = 10
2 int b = 20
3 int *pFlag = &a;
4 vector<int> vctTemp;
5 vctTemp.push_back(1);
6 string str1 = "hello "
7 string str2 = "world"
8 const int &m = 1  

请问,a,b, a+b, a++, ++a, pFlag, *pFlag, vctTemp[0], 100, string("hello"), str1, str1+str2, m分别是左值还是右值?

a和b都是持久对象(可以对其取地址),是左值;

a+b是临时对象(不可以对其取地址),是右值;

a++是先取出持久对象a的一份拷贝,再使持久对象a的值加1,最后返回那份拷贝,而那份拷贝是临时对象(不可以对其取地址),故其是右值;

++a则是使持久对象a的值加1,并返回那个持久对象a本身(可以对其取地址),故其是左值;

pFlag和*pFlag都是持久对象(可以对其取地址),是左值;

vctTemp[0]调用了重载的[]操作符,而[]操作符返回的是一个int &,为持久对象(可以对其取地址),是左值;

100和string("hello")是临时对象(不可以对其取地址),是右值;

str1是持久对象(可以对其取地址),是左值;

str1+str2是调用了+操作符,而+操作符返回的是一个string(不可以对其取地址),故其为右值;

m是一个常量引用,引用到一个右值,但引用本身是一个持久对象(可以对其取地址),为左值。

区分清楚了左值与右值,我们再来看看左值引用。左值引用根据其修饰符的不同,可以分为非常量左值引用和常量左值引用。

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

常量左值引用可以绑定到所有类型的值,包括非常量左值、常量左值、非常量右值和常量右值。

可以看出,使用左值引用时,我们无法区分出绑定的是否是非常量右值的情况。那么,为什么要对非常量右值进行区分呢,区分出来了又有什么好处呢?这就牵涉到C++中一个著名的性能问题——拷贝临时对象。考虑下面的代码:

1 vector<int> GetAllScores()
2 {
3 vector<int> vctTemp;
4 vctTemp.push_back(90);
5 vctTemp.push_back(95);
6 return vctTemp;
7 }  

当使用vector<int> vctScore = GetAllScores()进行初始化时,实际上调用了三次构造函数。尽管有些编译器可以采用RVO(Return Value Optimization)来进行优化,但优化工作只在某些特定条件下才能进行。可以看到,上面很普通的一个函数调用,由于存在临时对象的拷贝,导致了额外的两次拷贝构造函数和析构函数的开销。当然,我们也可以修改函数的形式为void GetAllScores(vector<int> &vctScore),但这并不一定就是我们需要的形式。另外,考虑下面字符串的连接操作:

1 string s1("hello");
2 string s = s1 + "a" + "b" + "c" + "d" + "e"

在对s进行初始化时,会产生大量的临时对象,并涉及到大量字符串的拷贝操作,这显然会影响程序的效率和性能。怎么解决这个问题呢?如果我们能确定某个值是一个非常量右值(或者是一个以后不会再使用的左值),则我们在进行临时对象的拷贝时,可以不用拷贝实际的数据,而只是“窃取”指向实际数据的指针(类似于STL中的auto_ptr,会转移所有权)。C++ 11中引入的右值引用正好可用于标识一个非常量右值。C++ 11中用&表示左值引用,用&&表示右值引用,如:

int &&a = 10

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

非常量右值引用只能绑定到非常量右值,不能绑定到非常量左值、常量左值和常量右值(VS2010 beta版中可以绑定到非常量左值和常量左值,但正式版中为了安全起见,已不允许)。如果允许绑定到非常量左值,则可能会错误地窃取一个持久对象的数据,而这是非常危险的;如果允许绑定到常量左值和常量右值,则非常量右值引用可以用于修改常量左值和常量右值,这明显违反了其常量的含义。

常量右值引用可以绑定到非常量右值和常量右值,不能绑定到非常量左值和常量左值(理由同上)。

有了右值引用的概念,我们就可以用它来实现下面的CMyString类。

 1 class CMyString
 2 {
 3 public:
 4 // 构造函数
 5 CMyString(const char *pszSrc = NULL)
 6 {
 7 cout << "CMyString(const char *pszSrc = NULL)" << endl;
 8 if (pszSrc == NULL)
 9 {
10 m_pData = new char[1];
11 *m_pData = ‘  

原文地址:http://blog.csdn.net/ls871211/article/details/7688718#

时间: 2024-10-21 07:45:10

【转】C++ 11 右值引用的理解的相关文章

c++11 右值引用与转移语义

右值引用 (Rvalue Referene) 是 C++ 新标准 (C++11, 11 代表 2011 年 ) 中引入的新特性 , 它实现了转移语义 (Move Sementics) 和精确传递 (Perfect Forwarding).它的主要目的有两个方面: 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率. 能够更简洁明确地定义泛型函数. 左值与右值的定义 C++( 包括 C) 中所有的表达式和变量要么是左值,要么是右值.通俗的左值的定义就是非临时对象,那些可以在多条语句中使

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

c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的持久化对象,右值是指表达式结束时就不再存在的临时对象.所有的具名变量或者对象都是左值,而右值不具名.很难得到左值和右值的真正定义,但是有一个可以区分左值和右值的便捷方法:看能不能对表达式取地址,如果能,则为左值,否则为右值. 看见书上又将右值分为:将亡值和纯右值. 纯右值就是c++98标准中右值的概

c++11 右值引用

http://blog.csdn.net/cpd92/article/details/50651700 http://www.tuicool.com/articles/i2qyyyB move 就是右值引用,减少了copy和释放的过程,提高了程序性能. #include <iostream> #include <string> #include<vector> using namespace std; class MyString { private: char* _d

C++ 11 右值引用以及std::move

转载请注明出处:http://blog.csdn.net/luotuo44/article/details/46779063 新类型: int和int&是什么?都是类型.int是整数类型,int&则是整数引用类型.相同int&&也是一个类型.两个引號&&是C++ 11提出的一个新的引用类型.记住,这是一个新的类型.默念10次吧.假设你记住这个新类型,那么非常多疑问都能迎刃而解.而且对<Effective Modern C++>说到的void f(

c++11——右值引用

1. 左值和右值 左值是表达式结束之后仍然存在的持久化对象,而右值是指表达式结束时就不再存在的临时对象.     c++11中,右值分为两种类型:将亡值(xvalue, expiring value),另一个是纯右值(prvalue, pure rvalue). 非引用返回的临时变量.运算表达式产生的临时变量.原始字面量和lambda表达式等都是纯右值:将亡值是c++11新增的.与右值引用相关的表达式,比如,将要被移动的对象.T&&函数返回值.std::move返回值和转换为T&&

c++11 右值引用和移动语义

什么是左值.右值 最常见的误解: 等号左边的就是左值,等号右边的就是右值 左值和右值都是针对表达式而言的, 左值是指表达式结束后依然存在的持久对象 右值是指表达式结束时就不再存在的临时对象区分: 能对表达式进行取地址,则为左值 :否则为右值 为什么引入右值引用?std::vector<String> v;v.push_back("hello,world"); 调用 String::String(const char *); 调用 String::String(const S

C++11:右值引用和转移赋值

1.首先认识左值和右值的定义: 左值:表达式可以引用到一个对象,并且这个对象是一块内存空间并可以检测和存储,这个表示即是左值. 右值:直接引用了一个存储在内存地址中的数据. 右值最大限度只能被一个常量引用: const int &a = 1; 规则:临时变量是右值,且可以改变: T().set().get() T为临时变量,set()设置新值,get()获取更改后的值. 2.首先写一个MyString类: #include<vector> class MyString { privat

C++11之右值引用:从左值右值到右值引用

C++98中规定了左值和右值的概念,但是一般程序员不需要理解的过于深入,因为对于C++98,左值和右值的划分一般用处不大,但是到了C++11,它的重要性开始显现出来. C++98标准明确规定: 左值是可以取得内存地址的变量. 非左值即为右值. 从这里可以看出,可以执行&取地址的就是左值,其他的就是右值. 这里需要明确一点,能否被赋值不是区分C++左值和右值的区别. 我们给出四个表达式: string one("one"); const string two("two&

[转载] C++11中的右值引用

C++11中的右值引用 May 18, 2015 移动构造函数 C++98中的左值和右值 C++11右值引用和移动语义 强制移动语义std::move() 右值引用和右值的关系 完美转发 引用折叠推导规则 特殊模板参数推导规则 解决完美转发问题 引用 在C++98中有左值和右值的概念,不过这两个概念对于很多程序员并不关心,因为不知道这两个概念照样可以写出好程序.在C++11中对右值的概念进行了增强,我个人理解这部分内容是C++11引入的特性中最难以理解的了.该特性的引入至少可以解决C++98中的