http://blog.chinaunix.net/uid-20726254-id-3486721.htm

这个绝对是新增的top特性,篇幅非常多。看着就有点费劲,总结更费劲。

原来的标准当中,参数与返回值的传值形式涉及到对象的复制,传值完成后,中间产生的临时对象又会马上被销毁,某些自定义的对象或者容器有很多元素时复制的开销非常大,而且例如IO对象或unique_ptr对象也不允许复制;传址在返回值的某些场景又会有局部对象的问题。c++11中新增了move的概念,用一个词描述就是"steal"。

1. 右值引用相关

个人理解,能出现在赋值运算符左侧即能被赋值的对象称之为左值,类似地右侧的赋值对象称为右值,都可以是单一变量或表达式。左值有其自己的identity,程序相对长期地为其永久或暂时地分配内存空间;右值多为临时变量(如某些表达式的中间值),生命期相对短暂(ephemeral),所有的字面常量均为右值。一个整形变量i,其为左值,我们可以赋给i任何其能存储的整型值;i+5这个表达式是右值,因为无论i是多少,你都不可能把20赋值给(i+5)。

右值引用必须绑定在右值上,书中的原话绑定的值是 an object that is about to be destroyed. 所以不能绑在左值上。

普通(regular)左值引用也不能绑定在右值上,const引用可以绑常量。

以前标准中的引用都是左值引用,运算符“&”,右值引用的运算符是"&&"。

 
1
2
3
4
5
6
int i = 42;
int &r = i;                   // ok: r refers to i
int &&rr = i;               // error: cannot bind an rvalue reference to an lvalue
int &r2 = i * 42;          // error: i * 42 is an rvalue
const int &r3 = i * 42; // ok: we can bind a reference to  const  to an rvalue
int &&rr2 = i * 42;      // ok: bind rr2 to the result of the multiplication

上面是几个书中的例子。注意:rr2是个右值引用,是说它绑定的是个右值,但其本身是左值,即下面这语句非法:

 
1
int &&rr3 = rr2;   // error: the expression rr2 is an lvalue!

即凡是可以 var_type var_name; 这样定义出来的变量(variable)其自身都是左值。

2. std::move相关。

右值引用因为绑定对象即将被销毁,意味着没有人会继续访问他们,所以就可以把他们(的资源)steal过来。

虽然不能将右值引用绑在左值上,但通过利用utility头文件新增的函数模板move,它返回传入对象的右值引用,可以达到 steal的效果。

 
1
int &&rr3 = std::move(rr2);   // ok

再提醒:一旦使用了move,编译器就默认传入对象已经不打算使用了,是可以被销毁的,move之后该对象的值已经不确定,不要再访问。还有由于对象偷取与复制的差别巨大,不注意会产生非常难定位的bug,所以所有使用move的地方一定要使用全称std::move,给大家以提醒。(其实c++11在algorithm头文件也新增了一个move,参数与意义都与此截然不同)。

3. move构造函数

对象可以被复制构造,满足条件场景下,自然也就可以被“剪切”构造。move构造函数就用于此,它也属于copy-control系列函数,后者的5个成员现在更新为:拷贝构造函数,复制性质的赋值运算符,move构造函数,move性质的赋值运算符,析构函数。

 
1
2
3
4
5
6
7
8
9
10
class base
{
public:
    int* p;
    base():{p = new(...);}
    ~base(){delete p;}
 
    base(base&& ref):p(ref.p){ref.p = nullptr;}
 
};

新创建对象的指针p接管了来源对象指针p所持有的资源,并将后者置空。特别注意move构造函数一定要来源对象内部存储资源的变量设置正确的状态,如指针置空等,避免来源对象析构时内存误释放。

对应的赋值运算符则一定要判断比较this指针和来源对象地址是否相同,进行自我move操作的保护。

move构造函数创建新对象后,来源对象并不是马上被销毁,但是出于一种析构函数随时可以运行的状态,不要再去访问它的值,这已经不可预计。

4. move构造函数的合成关系(喂,SBL你的inside c++11 object model啥时出版啊)

考构和其赋值运算符如果我们没有声明自己的,编译器会默认合成出来一个memberwise copy版本的。move构造函数和运算符却不是如此。如果类中有声明自己的考构和赋值运算符,或析构函数,则编译器不会合成默认的move操作函数,这时如果进行右值引用的move操作调用,实际触发的将会是拷贝构造函数。

当类中没有声明自己的任何一个copy-control函数,并且当所有非静态成员都可以move时,才会合成默认的move构造函数。

move构造函数和运算符从不会(never)隐式(implicit)被认为是delete,当我们声明其为default时,下列场景会变delete

(1)当类有某成员定义了其考构而没有m-con时,此类的m-con编译器认为是delete的,mv运算符同理,递归定义就是子成员的m-con合成不出来,该类的也肯定合成不出来。

(2)子成员的m-con或运算符是私有或delete,则此类的m-con和运算符是delete。

(3)类似考构,子成员或本身的析构是私有或delete,m-con为delete。

(4)类似赋值运算符,有const成员或引用成员,move运算符为delete。

经过试验,以下个人理解:在没有move成员时,都会调用考构成员,毕竟move风险太大。move成员的default最好不要随便加,没加时,调用std::move编译器还可以换成考构,加了后表示就要move,一旦有上面限制无法合成就会报错,虽然是编译的,不熟悉的搞起来还是有点麻烦。可能是类似提示,不知道为什么扯上了 constexpr

error: defaulted declaration 'base::base(const base&&)'
error: does not match expected signature 'constexpr base::base(base&&)'

BT的来了:move成员会反作用于考构成员,有定义move系列,没定义考构系列时,考构系列被认为是delete的。在base内定义实现了move系列成员,没有考构成员,下面的obj2两种形式定义都不会成功。

 
1
2
3
base obj1;
base obj2 (obj1);
base obj2= obj1;

提示都一样,左值不能绑到右值引用上,默认直接进入了move构造函数:

error: cannot bind 'base' lvalue to 'base&&'
error:   initializing argument 1 of 'base::base(base&&)'

结论:

(1)定义了move系列,则一定要定义考构系列。

(2)考构和move都有,左值引用进考构,右值引用进move.

(3)有考构没move,都进考构,即使通过std::move.

(4)copy-control系列的5个成员,自定义了一个,其余的最好都定义。

 
1
2
3
base obj2,obj1;
obj2 = base(5);//obj2 = std::move(base(5));
obj2 = obj1;

考构,mv都有,2行mv,3行考构运算符;

只有考构,都进考构运算符,无论第二行加不加std::move。

5. move迭代器,make_move_iterator,在iterator头文件,不导入也能用,把普通迭代器转成move迭代器。

 
1
2
3
4
5
6
7
8
9
    vector<int> ivec{1,2,3};
    vector<int> ivec2(ivec.begin(),ivec.end());
    vector<int> ivec3(make_move_iterator(ivec.begin()), make_move_iterator(ivec.end()));
 
    cout<<ivec.size()<<endl; cout<<ivec2.size()<<endl;="" cout<<ivec3.size()<<endl;="" ivec3.push_back(0);="" cout<<ivec.size()<<endl;="" cout<<ivec3.size()<<endl;<="" pre="">
输出:3 3 3 3 4 。ivec2复制ivec的内容,ivec3则是“偷”了过来,虽然ivec的size仍是3,但内部已经不能保证,在ivec3压入元素0后,其size为4,但ivec的size仍为3。
<p>
    <br>
</p>                                   </ivec.size()<<endl;></int></int></int>
 

l

右值引用、move与move constructor的更多相关文章

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

    转载请注明出处:http://blog.csdn.net/luotuo44/article/details/46779063 新类型: int和int&是什么?都是类型.int是整数类型,in ...

  2. C++ 11中的右值引用以及std::move

    看了很多篇文章,现在终于搞懂了C++ 中的右值以及std::move   左值和右值最重要的区别就是右值其实是一个临时的变量 在C++ 11中,也为右值引用增加了新语法,即&&   比 ...

  3. Item 25: 对右值引用使用std::move,对universal引用则使用std::forward

    本文翻译自<effective modern C++>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 右值引用只能绑定那些有资格被move的对象上去.如 ...

  4. C++11右值引用和std::move语句实例解析

    关键字:C++11,右值引用,rvalue,std::move,VS 2015 OS:Windows 10 右值引用(及其支持的Move语意和完美转发)是C++0x将要加入的最重大语言特性之一.从实践 ...

  5. C++ 11 左值,右值,左值引用,右值引用,std::move, std::foward

    这篇文章要介绍的内容和标题一致,关于C++ 11中的这几个特性网上介绍的文章很多,看了一些之后想把几个比较关键的点总结记录一下,文章比较长.给出了很多代码示例,都是编译运行测试过的,希望能用这些帮助理 ...

  6. 右值引用和std::move函数(c++11)

    1.对象移动 1)C++11新标准中的一个最主要的特性就是移动而非拷贝对象的能力 2)优势: 在某些情况下,从旧内存拷贝到新内存是不必要的,此时对对象进行移动而非拷贝可以提升性能 有些类如IO类或un ...

  7. 透彻理解C++11新特性:右值引用、std::move、std::forward

    目录 浅拷贝.深拷贝 左值.右值 右值引用类型 强转右值 std::move 重新审视右值引用 右值引用类型和右值的关系 函数参数传递 函数返还值传递 万能引用 引用折叠 完美转发 std::forw ...

  8. C++11中的右值引用及move语义编程

    C++0x中加入了右值引用,和move函数.右值引用出现之前我们只能用const引用来关联临时对象(右值)(造孽的VS可以用非const引用关联临时对象,请忽略VS),所以我们不能修临时对象的内容,右 ...

  9. C++11 的右值引用

    作者:Tinro链接:https://www.zhihu.com/question/22111546/answer/30801982来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请 ...

  10. (转)C++11使用emplace_back代替push_back (其中有关于右值引用)

    最近在写一段代码的时候,突然很好奇C++11中对push_back有没有什么改进以增加效率,上网搜了一些资料,发现果然新增了emplace_back方法,比push_back的效率要高很多. 首先,写 ...

随机推荐

  1. 对Oracle数据库坏块的理解

    1.物理坏块和逻辑坏块 在数据库中有一个概念叫做数据块的一致性,Oracle的数据块的一致性包括了两个层次:物理一致性和逻辑一致性,如果一个数据块在这两个层次上存在不一致性,那就对应到了我们今天要要说 ...

  2. vpn分类[转]

    目前常用的几种移动拨号的VPN技术及优势和劣势1)                WEB SSL优点:1.使用简单:每个终端用户不需要安装客户端,使用起来方便,不需要维护终端用户,通过IE直接来访问. ...

  3. BizTalk开发系列(十二) Schema设计之Group与Order

    开发BizTalk项目的时候会先约定各系统之间往来的消息格式. 由于BizTalk内部唯一使用XML文档.因此消息的格式为XML Schema(XML Schema 用于描述 XML 文档的结构).虽 ...

  4. 详解 Python 中的下划线命名规则

    在 python 中,下划线命名规则往往令初学者相当 疑惑:单下划线.双下划线.双下划线还分前后……那它们的作用与使用场景 到底有何区别呢?今天 就来聊聊这个话题. 1.单下划线(_) 通常情况下,单 ...

  5. OpenGL问题拾遗

    1 OpenGL设置好纹理以后显示不出来,显示为黑色 纹理默认会使用 mipmap .如果没有修改filter选项,或没有指定其他level的mipmap数据,就会显示不出来

  6. WPF 关于窗口的一些显示效果

    0. 一些常用尺寸大小: 1920x1080; 1600x900; 1280x720; 1024x576; 1. 设置窗口的边框样式 使用 Window.WindowStyle 属性可以设置窗口的边框 ...

  7. asp.net mvc 控制器中操作方法重载问题 解决

    Controllers: public ActionResult Index() { return View(db.GuestBooks.ToList()); } // // GET: /Guest2 ...

  8. J-LINK V8固件烧录指导

    1 J-LINK V8固件烧录指导 J-LINK 是使用过程中,如果内部固件意外损坏或丢失,请参考下面操作步骤说明,重新烧录JLINK固件. 1.1 安装固件烧录软件 请ATMEL官方网址下载AT91 ...

  9. Windows Server 2008 R2 辅域控制器如何升级成主域控制器

    一.实验模拟故障问题: zhuyu公司架设了一台主域控制器和一台辅域控制器,某一天,zhuyu公司的主域控制器系统崩溃,主域控制器系统也进不去. 虽然辅域控制器可以暂时代替主域控制器的普通工作,但是特 ...

  10. float属性

    float属性介绍 float给人一种捉摸不透的感觉,不过可以依照浏览器的解析机制(根据HTML文档,从上往下解析),对float属性了解一二.float有四种值:none/left/right/in ...