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);

}

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

  1. 彻底弄懂LSH之simHash算法

    马克·吐温曾经说过,所谓经典小说,就是指很多人希望读过,但很少人真正花时间去读的小说.这种说法同样适用于“经典”的计算机书籍. 最近一直在看LSH,不过由于matlab基础比较差,一直没搞懂.最近看的 ...

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

    1.1 我如何决定使用那种整数类型? 如果需要大数 值(大于32, 767 或小于¡32, 767), 使用long 型.否则, 如果空间很重要(如有大数组或很多结构), 使用short 型.除此之外 ...

  3. SQL Server-聚焦NOLOCK、UPDLOCK、HOLDLOCK、READPAST你弄懂多少?(三十四)

    前言 时间流逝比较快,博主也在快马加鞭学习SQL Server,下班回来再晚也不忘记更新下博客,时间挤挤总会有的,现在的努力求的是未来所谓的安稳,每学一门为的是深度而不是广度,求的是知识自成体系而不是 ...

  4. 一文弄懂神经网络中的反向传播法——BackPropagation【转】

    本文转载自:https://www.cnblogs.com/charlotte77/p/5629865.html 一文弄懂神经网络中的反向传播法——BackPropagation   最近在看深度学习 ...

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

    本文转自https://juejin.im/post/59e85eebf265da430d571f89#heading-4 本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还 ...

  6. 打工心态废掉了很多人,包括你吗?(你把现在这家公司的业务都弄清楚、弄懂了吗?君子报仇十年不晚!不离不弃!)good

    我只拿这点钱,凭什么去做那么多工作,我傻呀. 我为公司干活,公司付我一份报酬,等价交换而已,我不欠谁的. 我只要对得起这份薪水就行了,多一点我都不干,做了也白做. 工作嘛,又不是为自己干,说得过去就行 ...

  7. 彻底弄懂AngularJS中的transclusion

    点击查看AngularJS系列目录 彻底弄懂AngularJS中的transclusion AngularJS中指令的重要性是不言而喻的,指令让我们可以创建自己的HTML标记,它将自定义元素变成了一个 ...

  8. 彻底弄懂 JavaScript 执行机制

    本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我. 不论你是javascript新手还是老鸟,不论是面试求职,还是日常开发工作,我们经常会遇到这样的情况:给定 ...

  9. 彻底弄懂 Unicode 编码

    彻底弄懂 Unicode 编码 今天,在学习 Node.js 中的 Buffer 对象时,注意到它的 alloc 和 from 方法会默认用 UTF-8 编码,在数组中每位对应 1 字节的十六进制数. ...

随机推荐

  1. 图论(A*算法,K短路) :POJ 2449 Remmarguts' Date

    Remmarguts' Date Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 25216   Accepted: 6882 ...

  2. 【线段树】HDU 5493 Queue (2015 ACM/ICPC Asia Regional Hefei Online)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5493 题目大意: N个人,每个人有一个唯一的高度h,还有一个排名r,表示它前面或后面比它高的人的个数 ...

  3. 【动态规划】Vijos P1104 采药(NOIP2005普及组第三题)

    题目链接: https://vijos.org/p/1104 题目大意: T时间,n个物品,每个耗时ti,可获得收益ci,求最大收益. 题目思路: [动态规划] 01背包裸题.一维二维都过了,放个一维 ...

  4. 【转】Ansys 13.0 flexlm not running完美解决方案

    http://jingyan.baidu.com/article/af9f5a2dd9843a43150a4550.html 实测,12.1 用此方法问题同样得解.

  5. 高效算法——Bin Packing F - 贪心

      Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Descripti ...

  6. 暴力求解——除法 Division,UVa 725

    Description Write a program that finds and displays all pairs of 5-digit numbers that between them u ...

  7. UVa 674: Coin Change

    动态规划题.对于1,5,10,25,50五种币值的硬币,编号为0~4,存入数组cent中.数组iWay的元素iWay[k][i]表示仅使用0~i的硬币凑出k分钱的方法数,按是否使用编号为i的硬币分类, ...

  8. 那些年我用awk时踩过的坑——awk使用注意事项

    由于项目经历原因,经常使用awk处理一些文本数据.甚至,我特意下载了一个windows上的awk:gawk.exe,这样在windows上也能享受awk处理数据的方便性,. 俗话说,"常在河 ...

  9. Spring task任务调度详解

    spring内部有一个task是Spring自带的一个设定时间自动任务调度 task使用的时候很方便,但是他能做的东西不如quartz那么的多! 可以使用注解和配置两种方式,配置的方式如下 引入Spr ...

  10. Greenplum 数据库架构分析

    Greenplum 数据库是最先进的分布式开源数据库技术,主要用来处理大规模的数据分析任务,包括数据仓库.商务智能(OLAP)和数据挖掘等.自2015年10月正式开源以来,受到国内外业内人士的广泛关注 ...