二十分钟弄懂C++11 的 rvalue reference (C++ 性能剖析 (5))
C++ 11加了许多新的功能。其中对C++性能和我们设计class的constructor或assignment可能产生重大影响的非rvalue reference莫属!我看了不少资料,能说清它的不多。下面我企图用简单的例子来说明,希望读者能够理解并应用这一重要的语言构造。
1.rvalue reference 是reference (即指针)
比如下面两条语句的语义完全一样:
int &&p = 3; // line 1
const int &cp = 3 // line 2
2. rvalue reference 指向临时变量
上面的line1和line2的共同点是,他们都指向临时变量。所不同的是下面两句:
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出口时将会被销毁,除非我们能获取他的reference或pointer,然后将它的_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))的更多相关文章
- 彻底弄懂LSH之simHash算法
马克·吐温曾经说过,所谓经典小说,就是指很多人希望读过,但很少人真正花时间去读的小说.这种说法同样适用于“经典”的计算机书籍. 最近一直在看LSH,不过由于matlab基础比较差,一直没搞懂.最近看的 ...
- 必须弄懂的495个C语言问题
1.1 我如何决定使用那种整数类型? 如果需要大数 值(大于32, 767 或小于¡32, 767), 使用long 型.否则, 如果空间很重要(如有大数组或很多结构), 使用short 型.除此之外 ...
- SQL Server-聚焦NOLOCK、UPDLOCK、HOLDLOCK、READPAST你弄懂多少?(三十四)
前言 时间流逝比较快,博主也在快马加鞭学习SQL Server,下班回来再晚也不忘记更新下博客,时间挤挤总会有的,现在的努力求的是未来所谓的安稳,每学一门为的是深度而不是广度,求的是知识自成体系而不是 ...
- 一文弄懂神经网络中的反向传播法——BackPropagation【转】
本文转载自:https://www.cnblogs.com/charlotte77/p/5629865.html 一文弄懂神经网络中的反向传播法——BackPropagation 最近在看深度学习 ...
- 这一次,彻底弄懂 JavaScript 执行机制
本文转自https://juejin.im/post/59e85eebf265da430d571f89#heading-4 本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还 ...
- 打工心态废掉了很多人,包括你吗?(你把现在这家公司的业务都弄清楚、弄懂了吗?君子报仇十年不晚!不离不弃!)good
我只拿这点钱,凭什么去做那么多工作,我傻呀. 我为公司干活,公司付我一份报酬,等价交换而已,我不欠谁的. 我只要对得起这份薪水就行了,多一点我都不干,做了也白做. 工作嘛,又不是为自己干,说得过去就行 ...
- 彻底弄懂AngularJS中的transclusion
点击查看AngularJS系列目录 彻底弄懂AngularJS中的transclusion AngularJS中指令的重要性是不言而喻的,指令让我们可以创建自己的HTML标记,它将自定义元素变成了一个 ...
- 彻底弄懂 JavaScript 执行机制
本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我. 不论你是javascript新手还是老鸟,不论是面试求职,还是日常开发工作,我们经常会遇到这样的情况:给定 ...
- 彻底弄懂 Unicode 编码
彻底弄懂 Unicode 编码 今天,在学习 Node.js 中的 Buffer 对象时,注意到它的 alloc 和 from 方法会默认用 UTF-8 编码,在数组中每位对应 1 字节的十六进制数. ...
随机推荐
- 【HDOJ】3329 The Flood
超简单BFS. /* 3329 */ #include <iostream> #include <queue> #include <cstdio> #include ...
- Unity NGUI Tween的用法
unity版本:4.5 NGUI版本:3.6.5 参考链接:http://www.colabug.com/thread-1029974-1-1.html,作者:COLABUG.COM 橘虞 htt ...
- thinkphp中的session()方法
系统提供了Session管理和操作的完善支持,全部操作可以通过一个内置的session函数完成. 用法 session($name, $value='') 参数 name(必须):如果传入数组 则表示 ...
- 更改Visual Studio 2010/2012/2008的主题设置
一.更改主题: 主题网站:http://studiostyl.es/ Visual Studio 2010发布也已经有一段时间了,不过安装后默认的白底的主题长时间看代码可能会感觉眼睛酸痛,况且时间长了 ...
- document.getElementById的简便方式
封装自己的元素获取方法,使元素获取变得简便 注意:1.应该要防止定义的被重写,可将同名的重新定义 2.可将封装的对象置为全局对象,方便使用 通过id查找单个元素 封装方式: //通过id查找单个元 ...
- HDOJ(HDU) 2153 仙人球的残影(谜一样的题、、、)
Problem Description 在美丽的HDU,有一名大三的同学,他的速度是众所周知的,跑100米仅仅用了2秒47,在他跑步过程中会留下残影的哎,大家很想知道他是谁了吧,他叫仙人球,既然名字这 ...
- 吐槽iOS国际化:关于NSLocalizedString的使用
http://blog.csdn.net/aries4ever/article/details/8185405
- RabbitMQ挂掉问题处理
开发环境中的rabbitmq总是会挂掉,rabbitmq的执行都是ssh远程登录执行命令: rabbitmq-server & 认为加了&,进程会在后台执行不会受到终端的影响.所以不知 ...
- windows7实用快捷键 分类: windows常用小技巧 2014-04-18 14:34 169人阅读 评论(0) 收藏
几个比较实用的快捷键 windows键:简写成win win+ 方向键上下,可以使当前窗体放大或缩小 win+ 方向键左右,可以使当前窗体悬靠在左边或右边 win+Home 仅保留当前窗体 ...
- 利用Asp.net MVC处理文件的上传下载
如果你仅仅只有Asp.net Web Forms背景转而学习Asp.net MVC的,我想你的第一个经历或许是那些曾经让你的编程变得愉悦无比的服务端控件都驾鹤西去了.FileUpload就是其中一个, ...