二十分钟弄懂C++11 的 rvalue reference (C++ 性能剖析 (5))

C++ 11加了许多新的功能。其中对C++性能和我们设计classconstructorassignment可能产生重大影响的非rvalue reference莫属!我看了不少资料,能说清它的不多。下面我企图用简单的例子来说明,希望读者能够理解并应用这一重要的语言构造。

1.rvalue reference 是reference (即指针)

比如下面两条语句的语义完全一样:

int &&p = 3;             // line 1

const int &cp = 3      // line 2

2. rvalue reference 指向临时变量

上面的line1line2的共同点是,他们都指向临时变量。所不同的是下面两句:

p = 5;       // p 的内容变成了5

cp = 5;      // 编译出错:cp 不能改动(常数)

3.rvalue reference可以简化moving 语义 – 提高object 拷贝性能

很好,我们现在可以通过rvalue reference修改(阴暗中的)临时变量了。那么这有什么用呢?目前C++11所宣称的最主要的应用就是所谓的“moving semantics (迁移语义)”。请看下面例子:

class SimpleString

{

       char * _ptr;

public:

       SimpleString(const char *p);

       SimpleString(const SimpleString & another);

       ~SimpleString();

       operator const char * () { return _ptr; }

       SimpleString & operator = (const SimpleString & another);

       static void Test();

private:

       void GetStr(const char *p);

};

SimpleString::SimpleString(const char *p): _ptr(nullptr)

{

       GetStr(p);

}

SimpleString::SimpleString(const SimpleString & another): _ptr(nullptr)

{

       GetStr(another._ptr);

}

void SimpleString::GetStr(const char *p)

{

       if (_ptr)

             delete [] _ptr;

 

       size_t l= ::strlen(p);

       _ptr = new char[l+1];

       ::strcpy_s(this->_ptr, l+1, p);

} 

SimpleString::~SimpleString()

{

       if (_ptr)

       {

             delete [] _ptr;

             printf("SimpleString d‘tr called for \n");

       }

}

SimpleString & SimpleString::operator = ( const SimpleString & another)

{

       GetStr(another._ptr);

       return *this;

}

namespace

{

      // simple string factory

       SimpleString CreateString()

       {

             SimpleString temp("A temp string created!");

             return temp;

       }

} 

void SimpleString::Test()

{

       SimpleString ret = CreateString();

       printf("ret is: &s \n", ret);

}

上面是一个为了试验用的简单string class。 假设我们有一个函数CreateString, 返回一个创建的SimpleString 值。然后赋给接受变量ret。 这个简单的逻辑有什么问题呢?

这里就是临时变量copy constructor的问题。我们这里用了SimpleString::SimpleString(const SimpleString & another),它用GetStr来构建一个新的指针_ptr。然后将临时变量的_ptr所指内容拷贝过来。

这是常见的做法,但是很昂贵的。CreateString函数已经构建了一个有效的_ptr,为什不能拷贝指针呢?

原来,因为CreateString里的temp变量是临时变量,它在CreateString出口时将会被销毁,除非我们能获取他的referencepointer,然后将它的_ptr设为null。这是个好主意,我们再加一个函数:

void SimpleString::MoveStr(SimpleString & another)

{

       if (this->_ptr)

             delete this->_ptr;

 

       this->_ptr = another._ptr;

       another._ptr = nullptr;

}

然后把copy constructor 改写:

SimpleString::SimpleString(const SimpleString & another): _ptr(nullptr)

{

       MoveStr(const_cast<SimpleString &>(another)); // line 100

}

这样一来,我们就只构建一次_ptr了,测试的结果也证明了这一点。

上面讲的和rvalue reference有何关系呢?

我对line 100的方案不太满意:

1) 我们改变了原来copy constructor的常规意义,现在只要你赋值与另一变量,你就失去了你自己的值。我们希望这个功能只适合于“临时变量”。

2) const_cast 不太好,不美观。

现在,我们因该悟出rvalue reference的意义了吧?

根据第二节,rvalue reference是指向临时变量的,正好是用于指向CreateString产生的临时变量。

原来,只要我们在SimpleString里加一个moving copy constructor(注意&&):

SimpleString::SimpleString(SimpleString && another): _ptr(nullptr)

{

       MoveStr(another);

}

我们便无需更改SimpleString::SimpleString(const SimpleString & another)了。C++编译自动地在这一行SimpleString ret = CreateString() call 我们的moving constructor SimpleString::SimpleString(SimpleString && another), 而不是我们的copy constructor.

大家不妨试试!

总结

C++11利用rvalue reference,使我们可以方便地实现 moving constructor 语义。这对上述类似的问题(特别是std里的container用法)提供了解决C++传统的临时变量拷贝的功能隐患。

附录:修改后的代码

// header: RValueRef.h

class SimpleString

{

         char * _ptr;

public:

         SimpleString(const char *p);

         SimpleString(const SimpleString & another);

         SimpleString(SimpleString && another); // moving constructor

 

         ~SimpleString();

         operator const char * () { return _ptr; }

         SimpleString & operator = (const SimpleString & another);

         SimpleString & SimpleString::operator = ( SimpleString && another);

 

         static void Test();

 

private:

         void GetStr(const char *p);

         void MoveStr(SimpleString & another);

};

// C++: rvalue.cpp

#include "stdafx.h"

#include <string.h>

#include <stdlib.h>

#include <stdio.h>

#include <errno.h>

#include "RValueRef.h"

 

SimpleString::SimpleString(const char *p): _ptr(nullptr)

{

         GetStr(p);

}

 

SimpleString::SimpleString(const SimpleString & another): _ptr(nullptr)

{

         GetStr(another._ptr);

}

 

// Moving constructor helps move temp var’s _ptr to ourselves.

SimpleString::SimpleString(SimpleString && another): _ptr(nullptr)

{

         MoveStr(another);

}

void SimpleString::GetStr(const char *p)

{

         if (_ptr)

                 delete [] _ptr;

 

         size_t l= ::strlen(p);

         _ptr = new char[l+1];

         ::strcpy_s(this->_ptr, l+1, p);

}

SimpleString::~SimpleString()

{

         if (_ptr)

         {

                 printf("SimpleString d‘tr called for ‘%s‘\n", _ptr);

                 delete [] _ptr;

         }

}

SimpleString & SimpleString::operator = ( const SimpleString & another)

{

         GetStr(another._ptr);

         return *this;

}

SimpleString & SimpleString::operator = ( SimpleString && another)

{

         MoveStr(another);

         return *this;

}

void SimpleString::MoveStr(SimpleString & another)

{

         if (this->_ptr)

                 delete this->_ptr;

 

         this->_ptr = another._ptr;

         another._ptr = nullptr; // don’t forget to do this

}

namespace

{

          SimpleString CreateString()

         {

                 SimpleString temp("A temp string created!");

                 return temp;

         }

 

}

void SimpleString::Test()

{

         SimpleString ret = CreateString();

         printf("ret is: &s \n", ret);

}

时间: 2024-08-01 22:46:10

二十分钟弄懂C++11 的 rvalue reference (C++ 性能剖析 (5))的相关文章

【转】彻底弄懂最短路径问题(图论)

来源:彻底弄懂最短路径问题 http://www.cnblogs.com/hxsyl/p/3270401.html P.S.根据个人需要,我删改了不少 问题引入 问题:从某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径——最短路径.解决最短路的问题有以下算法,Dijkstra算法,Bellman-Ford算法,Floyd算法和SPFA算法,另外还有著名的启发式搜索算法A*,不过A*准备单独出一篇,其中Floyd算法可以求解任意两点间的最短路径的长度.笔者认为任意一个最

必须弄懂的495个C语言问题

必须弄懂的495个C语言问题 1.1 我如何决定使用那种整数类型? 如果需要大数 值(大于32, 767 或小于?32, 767), 使用long 型.否则, 如果空间很重要(如有大数组或很多结构), 使用short 型.除此之外, 就使用int 型.如果严格定义的溢出特征很重要而负值无关紧要, 或者你希望在操作二进制位和字节时避免符号扩展的问题, 请使用对应的无符号类型.但是, 要注意在表达式中混用有符号和无符号值的情况. 尽管字符类型(尤其是无符号字符型) 可以当成"小" 整型使用

这一次,彻底弄懂 JavaScript 执行机制

本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我. 文章转自:https://juejin.im/post/59e85eebf265da430d571f89 不论你是javascript新手还是老鸟,不论是面试求职,还是日常开发工作,我们经常会遇到这样的情况:给定的几行代码,我们需要知道其输出内容和顺序.因为javascript是一门单线程语言,所以我们可以得出结论: javascript是按照语句出现的顺序执行的 看到这里读者要打人了:我难道不知道js

【CodeForces】343D Water tree (线段树好题!还未弄懂)

/* 此题的方法除了用线段树求子树,通过标记父亲,更新儿子的方法,来更新祖先,学习了. 对于建树的方法由于并没有说明父亲与儿子的顺序,所以需要通过两次添加. 并且pre变量可以获得父亲的位置,还未弄懂! */ #define _CRT_SECURE_NO_WARNINGS #include<cstring> #include<cstdio> #include<iostream> #include<algorithm> using namespace std;

C++11标准之右值引用(rvalue reference)

1.右值引用引入的背景 临时对象的产生和拷贝所带来的效率折损,一直是C++所为人诟病的问题.但是C++标准允许编译器对于临时对象的产生具有完全的自由度,从而发展出了Copy Elision.RVO(包括NRVO)等编译器优化技术,它们可以防止某些情况下临时对象产生和拷贝.下面简单地介绍一下Copy Elision.RVO,对此不感兴趣的可以直接跳过: (1) Copy Elision Copy Elision技术是为了防止某些不必要的临时对象产生和拷贝,例如: struct A { A(int)

C++11新特性之 rvalue Reference(右值引用)

右值引用可以使我们区分表达式的左值和右值. C++11引入了右值引用的概念,使得我们把引用与右值进行绑定.使用两个"取地址符号": int&& rvalue_ref = 99; 需要注意的是,只有左值可以付给引用,如: int& ref = 9; 我们会得到这样的错误: "invalid initialization of non-const reference of type int& from an rvalue of type int&q

SEOer都想弄懂的百度权重

百度权重我相信是SEOer都想弄懂弄透的一个东西,百度权重的算法经常会让SEOer们感到头疼,今天我们来详细分析一下. 百度权重,本来的含义应该是百度对一个网站的整体评价.这里说的百度权重,是站长工具等网站上的根据网站关键词(指数)在百度的排名给出的一个数值 .注意,是非官方的定义. 关于百度权重的权威性.首先,大多数站长已经了解到一点,很多网站给出的百度权重数值很多时候是不一致的.所以可以确定的一点是,百度权重并不权 威,没有统一的答案.其次,爱站网和站长工具的百度权重是怎么算出来的.这两家的

(被检讨)为何没在二十分钟内做完要求的题目

时间:2015.3.6 地点:基础教学楼601 要求:在二十分钟内编写一段代码满足能随机抽取几组四则运算题目 因为没在规定时间完成特写此检讨,望广大园友共勉! 正确代码如下: #include<iostream>using namespace std;int main(){ int a,b,c,d,k; for(k=0;k<10;k++) {  a=rand()%99;  b=rand()%99;  c=rand()%5;  if(c==1) cout<<a<<&

学习PS必须弄懂的专业术语

在学习PS的过程中,我们经常会遇到一些专业术语,下面我们来对一些常用的.比较难理解的术语进行简单讲解. 像素:像素是构成图像的最基本元素,它实际上是一个个独立的小方格,每个像素都能记录它所在的位置和颜色信息.下图中每一个小方格就是一个像素点,它记载着图像的各种信息. 选区:也叫选取范围,是PS对图像做编辑的范围,任何编辑对选区外无效.当图像上没有建立选择区时,相当于全部选择.下图中的黑白相间的细线就是选择区的边界,对图像的操作只对选择区内有效. 羽化:对选择区的边缘做软化处理,其对图像的编辑在选