移动构造和移动赋值与std::move
------------------------------------移动构造------------------------------------------
传统的深拷贝深赋值
对于类中,含有指针的情况,要自实现其拷贝构造和拷贝赋值。也就是所谓的深拷贝和深赋值。我想这己经成为一种共识了。
比如如下类:
#include <iostream>
using namespace std;
class HasPtrMem
{
public:
HasPtrMem():_d(new int(0)){
cout<<"HasPtrMem()"<<this<<endl;
} HasPtrMem(const HasPtrMem& another)
:_d(new int(*another._d))
{
cout<<"HasPtrMem(const HasPtrMem&
another)"<<this<<"->"<<&another<<endl;
} ~HasPtrMem(){
delete _d;
cout<<"~HasPtrMem()"<<this<<endl;
} int * _d;
}; HasPtrMem getTemp()
{
return HasPtrMem();
} int main(int argc, char *argv[])
{ // HasPtrMem a;
// HasPtrMem b(a);
// cout<<*a._d<<endl;
// cout<<*b._d<<endl;
HasPtrMem&& ret = getTemp();
return 0;
}
上面的过程,我们己经知晓,ret 作为右值引用,引用了临时对象,由于临时对象是待返回对象的复本,所以表面上看起来是,待返回对象的作用域扩展了,生命周期也延长了。
从右值引到移动构造
前面我们建立起来了一个概念,就是右值引用。用右值引用的思想,再来实现一下拷贝。这样,顺便把临时对象的问题也解决了。
#include <iostream>
using namespace std; class HasPtrMem
{
public:
HasPtrMem():_d(new int(0)){
cout<<"HasPtrMem()"<<this<<endl;
} HasPtrMem(const HasPtrMem& another)
:_d(new int(*another._d)){
cout<<"HasPtrMem(const HasPtrMem& another)" <<this<<"->"<< &another<<endl;
}
HasPtrMem(HasPtrMem &&another)
{
cout<<this<<" Move resourse from "<<&another<<"->"<< another._d <<endl;
_d = another._d;
another._d = nullptr;
} ~HasPtrMem(){
delete _d;
cout<<"~HasPtrMem()"<<this<<endl;
}
int * _d;
}; HasPtrMem getTemp()
{
return HasPtrMem();
} int main(int argc, char *argv[])
{
HasPtrMem a = getTemp();
return 0;
}
移动构造
如下是,移动构造函数。我们借用临时变量,将待返回对象的内容“偷”了过来。
移动构造充分体现了右值引用的设计思想,通过移动构造我们也在对象层面看清了右值引用的本质。从而对于普通类型右值引用内部是怎样操作的的也就不难理解了。
//移动构造
HasPtrMem(HasPtrMem &&another)
{
cout<<this<<" Move resourse from "<<&another<<"->"<< another._d<<endl;
_d = another._d;
another._d = nullptr;
}
再来看一下拷贝构造函数,我们对比一下区别:
HasPtrMem(const HasPtrMem& another)
:_d(new int(*another._d)){
cout<<"HasPtrMem(const HasPtrMem& another)" <<this<<"->"<< &another<<endl;
}
移动构造相比于拷贝构造的区别,移动构造通过指针的赋值,在临时对象析构之前,及时的接管了临时对象在堆上的空间地址。
关于默认的移动构造函数
对于不含有资源的对象来说,自实现拷贝与移动语义并没有意义,对于这样的类型 而言移动就是拷贝,拷贝就是移动。
拷贝构造/赋值和移动构造/赋值,必须同时提供或是同时不提供。才能保证同时俱有拷贝和移动语义。只声明一种的话,类只能实现一种语义。
只有拷贝语义的类,也就是 C++98 中的类。而只有移动语义的类,表明该类的变量所拥有的资源只能被移动,而不能被拷贝。那么这样的资源必须是唯一的。只有移动语义构造的类型往往是“资源型”的类型。比如智能指针,文件流等。
效率问题
#include <iostream> using namesapce std; class Copyable
{
public:
Copyable(int i)
:_i(new int(i))
{
cout<<"Copyable(int i):"<<this<<endl;
} Copyable(const Copyable & another)
:_i(new int(*another._i))
{
cout<<"Copyable(const Copyable & another):"<<this<<endl;
} Copyable(Copyable && another)
{
cout<<"Copyable(Copyable && another):"<<this<<endl;
_i = another._i;
}
Copyable & operator=(const Copyable &another)
{
cout<<"Copyable & operator=(const Copyable &another):"<<this<<endl;
if(this == & another)
return *this;
*_i=*another._i;
return *this;
} Copyable & operator=(Copyable && another)
{
cout<<"Moveable & operator=(Moveable && another):"<<this<<endl;
if(this != &another)
{
*_i = *another._i;
another._i = NULL;
}
return * this;
} ~Copyable()
{
cout<<"~Copyable():"<<this<<endl;
if(_i)
delete _i;
} void dis()
{
cout<<"class Copyable is called"<<endl;
}
void dis() const
{
cout<<"const class Copyable is called"<<endl;
} private:
int * _i;
}; void putRRValue(Copyable && a)
{
cout<<"putRRValue(Copyable && a)"<<endl;
a.dis();
} void putCLValue(const Copyable & a)
{
cout<<"putCRValue(Copyable & a)"<<endl;
a.dis();//error!
} //const T&和T&&重载同时存在先调用谁?
void whichCall(const Copyable & a)
{
a.dis();
} void whichCall(Copyable && a)
{
a.dis();
} int main(int argc, char *argv[])
{
// Copyable rrc = getCopyable(); cout<<"调用移动构造"<<endl;
Copyable a =Copyable(2);//匿名对象/临时对象优先调用右值引用 构造-右值构造 cout<<"调拷贝构造"<<endl;
Copyable ca(a); cout<<"直接构造右值"<<endl;
Copyable && rra =Copyable(2); cout<<"=================================="<<endl; //右值引用与const引用。 效率是否一样?
cout<<"右值引用传参"<<endl;
putRRValue(Copyable(2));
cout<<"Const 引用传参"<<endl;
putCLValue(Copyable(2));
cout<<"----------------------"<<endl; //优先调用哪种重载? T&& 还是 const T&?
whichCall(Copyable(2));
//这个没什么好纠结的!T&&的出现就是了解决 const T &接受匿名/临时对象后,不能调用非cosnt函数的问题。 return 0;
}
在接收一个右值的情况下,T&& 与 const T& 在效率别无二致,关键的是,T&&满足了我们non-const对象的使用情况。
--------------------------------------------------移动构造-----------------------------------------------
#include <iostream> using namespace std; class Copyable
{
public:
Copyable(int i)
:_i(new int(i))
{
cout<<"Copyable(int i):"<<this<<endl;
} Copyable(const Copyable & another)
:_i(new int(*another._i))
{
cout<<"Copyable(const Copyable & another):"<<this<<endl;
} Copyable(Copyable && another)
{
cout<<"Copyable(Copyable && another):"<<this<<endl;
_i = another._i;
}
Copyable & operator=(const Copyable &another)
{
cout<<"Copyable & operator=(const Copyable &another):"<<this<<endl;
if(this == & another)
return *this;
*_i=*another._i;
return *this;
} Copyable & operator=(Copyable && another)
{
cout<<"Moveable & operator=(Moveable && another):"<<this<<endl;
if(this != &another)
{
delete _i;
_i = another._i;
}
return * this;
} ~Copyable()
{
cout<<"~Copyable():"<<this<<endl;
if(_i)
delete _i;
} void dis()
{
cout<<"class Copyable is called"<<endl;
}
void dis() const
{
cout<<"const class Copyable is called"<<endl;
} int * _i;
}; int main(int argc, char *argv[])
{ Copyable a();
Copyable b();
cout<<"-----copyassin start------"<<endl;
a = b; //拷贝赋值
cout<<"-----moveassin start------"<<endl;
a = Copyable(); //移动赋值
getchar(); return ;
}
移动赋值,拷贝赋值的区分,前者是在两个对象都存在的情况下,一个给另一个赋值。后者是拿一个即将构造的对象给一个已存在的对象赋值。
效率问题:移动赋值也是将一个将亡值的资源直接接管了过来,无需再去申请新的内存。尤其是对象中所含堆上资源比较大的情况下,在效率上的体现是非常高的。
移动构造,移动赋值,是有极高效率的。右值引用的价值也体现在这里。主题的思想是把把将亡值的资源接管过来。而不用自己去申请空空间。
------------------------------------模板函数std::move----------------------------
虽然不能将一个右值引用直接绑定到左值上,但是我们可以显式的将一个左值转换为对应的右值引用类型。我们还可以通过调用一个名为move的新标准库函数来获得绑定到左值上的右值引用,此函数定义在untility中。move函数使用了**机制来返回给定对象的右值引用。
int &&rra = rr1; //error: //! error!右值引用不能直接绑定到左值
int &&rr = std::move(rr1); // ok!
move调用告诉编译器:我们有一个左值,但是我们希望像处理一个右值一样去处理他。
调用move就意味着承诺:除了对rr1赋值或者销毁它之外,我们将不能再使用它。在调用move之后,我们不能对移动后的源对象值做任何的假设。
移动构造和移动赋值与std::move的更多相关文章
- C++11右值引用和std::move语句实例解析
关键字:C++11,右值引用,rvalue,std::move,VS 2015 OS:Windows 10 右值引用(及其支持的Move语意和完美转发)是C++0x将要加入的最重大语言特性之一.从实践 ...
- std::move的原理与实现,右值引用的深入理解
这次我真的懂了.... 首先C++11引入了右值引用 && ‘&&’这个要连起来看,是一个整体,C++多了一个关键字而已. 不是引用的引用.是船新的一种语法.那有什么用 ...
- C++ 11 右值引用以及std::move
转载请注明出处:http://blog.csdn.net/luotuo44/article/details/46779063 新类型: int和int&是什么?都是类型.int是整数类型,in ...
- C++ 11 左值,右值,左值引用,右值引用,std::move, std::foward
这篇文章要介绍的内容和标题一致,关于C++ 11中的这几个特性网上介绍的文章很多,看了一些之后想把几个比较关键的点总结记录一下,文章比较长.给出了很多代码示例,都是编译运行测试过的,希望能用这些帮助理 ...
- Item 25: 对右值引用使用std::move,对universal引用则使用std::forward
本文翻译自<effective modern C++>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 右值引用只能绑定那些有资格被move的对象上去.如 ...
- 透彻理解C++11新特性:右值引用、std::move、std::forward
目录 浅拷贝.深拷贝 左值.右值 右值引用类型 强转右值 std::move 重新审视右值引用 右值引用类型和右值的关系 函数参数传递 函数返还值传递 万能引用 引用折叠 完美转发 std::forw ...
- C++11中std::move、std::forward、左右值引用、移动构造函数的测试
关于C++11新特性之std::move.std::forward.左右值引用网上资料已经很多了,我主要针对测试性能做一个测试,梳理一下这些逻辑,首先,左值比较熟悉,右值就是临时变量,意味着使用一次就 ...
- 左值 lvalue,右值 rvalue 和 移动语义 std::move
参考文章: [1] 基础篇:lvalue,rvalue和move [2] 深入浅出 C++ 右值引用 [3] Modern CPP Tutorial [4] 右值引用与转移语义 刷 Leetcode ...
- 一文带你详细介绍c++中的std::move函数
前言 在探讨c++11中的Move函数前,先介绍两个概念(左值和右值) 左值和右值 首先区分左值和右值 左值是表达式结束后依然存在的持久对象(代表一个在内存中占有确定位置的对象) 右值是表达式结束时不 ...
随机推荐
- linux c编程:文件的操作
在Linux系统中,系统是通过inode来获得这个文件的信息.在Linux系统中,inode的信息都是封装在stat这个结构体中.可以通过man 2 stat来查看stat的具体结构.从中可以看到包含 ...
- ELK日志收集系统搭建
架构图 ELK 架构图:其中es 是集群,logstash 是单节点(猜想除非使用nginx对log4j的网络输出分发),kibana是单机(用不着做成集群). 1.拓扑图 2.logstash ...
- CUDA: 常量内存与事件
常量内存: 常量内存用于保存在核函数执行期间不会发生变化的数据,在变量面前添加 __constant__ 修饰符: __constant__ Sphere s[SPHERES]; cudaMe ...
- LeetCode:奇偶链表【328】
LeetCode:奇偶链表[328] 题目描述 给定一个单链表,把所有的奇数节点和偶数节点分别排在一起.请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性. 请尝试使用原地 ...
- redis的持久化RDB与AOF
redis 持久化 Redis是一种内存型数据库,一旦服务器进程退出,数据库的数据就会丢失,为了解决这个问题,Redis提供了两种持久化的方案,将内存中的数据保存到磁盘中,避免数据的丢失. RDB ...
- 《锋利的jQuery》打造个性网站整合
搜索框文字效果 网页换肤 导航效果 广告效果 添加超链接提示 产品横向滚动效果 光标滑动列表效果 产品详细页面效果(放大镜,遮罩,选项卡,评分等) 1.搜索框文字效果 <!DOCTYPE htm ...
- 升级GCC 6.2编译LLVM的问题
[ 55%] Built target RTInterception.x86_64 [ 55%] Building ASM object projects/compiler-rt/lib/saniti ...
- 20145239杜文超 《Java程序设计》第1周学习总结
20145239<Java程序设计>第1周学习总结 教材学习内容总结 第一周. 通过教材简单的了解了java的历史.因为之前看过视频,所以有一个大致明了的认识. 识记了Java三大平台:J ...
- 利用ThinkPHP做项目步骤
ThinkPHP使用规则:约定大于配置 创建入口文件: 1.在ThinkPHP目录下创建一个入口文件index.php 2.访问入口文件的同时系统会自动把对应的应用目录文件Test创建出来 3.打开H ...
- JS工具函数汇总
备注:http://phpjs.org/ 这个站点把PHP常用的方法用js实现了,推荐一下 1.从数组中随机获取几个不重复项 //从一个给定的数组arr中,随机返回num个不重复项 function ...