前言

最近在看C++ Primer的时候,对于对象移动一直不太懂,所以在查找各种资料,仔细研究代码后,打算写篇博客记录下来,果然还是不要得过且过,看见不懂的就查,弄懂为止最好了。

对象移动

很多时候都会发生对象拷贝,但是拷贝有个问题,对于有些仅仅做完拷贝就销毁的情况,其实没必要,更好的做法是进行移动元素;在新标准中,可以用容器保存不可拷贝的类型,前提是他能被移动即可;

右值引用

  1. 标准库容器、string和shared_str类既支持移动也支持拷贝,IO类和 unique _xstr 类可以移动但不能拷贝;

  2. 右值引用只能绑定到一个将要销毁的对象,通过 && 来获得右值引用;

  3. 右值引用:所引用的对象将要被销毁;对象没有其他用户;这些意味着右值引用的代码可以自由的接管所引用的对象的资源;

  4. 变量是左值,因此我们不能将一个右值引用直接绑定到一个变量上,但是常量可以,即使变量是右值引用类型也不可以;

    例子:

    1. int && rr1 = 42; //正确:字面意思是右值
    2. int && rr2 = rr1; //错误:表达式rr1是左值
  5. 调用了move函数就意味着承诺,不能对移动后对源对象的值做任何的假设,但是你可以销毁,也可以赋予它新值,就是不能使用一个移后源对象的值;

  6. 使用move的代码应该使用std:move而不是简单的move,这样做可以避免潜在的名字冲突;

    例子:

    1. int && rr3 = std::move(rr1);

移动构造函数和移动赋值运算符

  1. 和拷贝构造函数不同,移动构造函数不分配任何内存,在完成资源移动后,移动构造函数还需要确保移后源对象是否可以销毁掉,其必须不再指向被移动的资源,因为其资源的所有权已经归属到新创建的对象(如果对象中存在成员指针,则需要将其置为空);

    例子:

    1. Derived::Derived(Derived && d) noexcept: tag(d.tag){
    2. d.tag = "";
    3. }

    其中tag是Derived的成员属性;

  2. noexcept 是我们承诺一个函数不抛出异常的一种方法,在一个构造函数中,noexcept出现在参数列表和初始化列表开始的冒号之间(在类的声明和定义中都需要指定noexcept);

    (使用方法如上)

  3. 不能在使用右侧运算对象的资源之前就释放左侧运算对象的资源;在移动操作之后,移后源对象必须保持有效的、可析构的状态,但是客户不能对其进行任何假设;

    例子:

    1. Derived& operator=(const Derived& other) {
    2. if (this != &other) {
    3. name = other.name;
    4. tag = other.tag;
    5. }
    6. return *this;
    7. }

    注意:函数参数是引用类型的

  4. 和拷贝构造操作不同,如果一个类定义了自己的拷贝构造函数,拷贝赋值函数或者析构函数,编译器就不会为它合成移动构造函数和移动赋值运算符,则其就会使用拷贝操作替代移动操作;

    只有当一个类没有定义任何自己版本的拷贝控制成员,而且它的所有数据成员都能移动构造或移动赋值时,编译器才会为它合成移动构造函数和移动赋值运算符;

  5. 和拷贝操作不同,移动操作永远不会隐式定义为删除的函数;

    合成的移动操作定义为删除的函数遵循定义删除的合成拷贝类似原则:

    • (和拷贝构造函数不同)移动构造函数被定义为删除的函数的条件:有类成员定义了自己的拷贝构造函数并且未定义移动构造函数,或者又是有类成员未定义自己的拷贝构造函数并且编译器不能为其合成移动构造函数;

    • 如果有类成员的移动构造函数或移动赋值运算符被定义为删除的或是不可访问的,则类的移动构造函数或移动赋值运算符被定义为删除的;

    • (和拷贝构造函数类似)如果类的析构函数被定义为删除的或是不可访问的,则类的移动构造函数被定义为删除的;

    • (和拷贝赋值运算符类似)如果类成员是const的或者是引用,则类的移动赋值运算符被定义为删除的;


    下面这种情况是正确的,没有定义为删除的情况:

    1. class X {
    2. public:
    3. int i; // 内置类型可以移动
    4. string s; // string定义了自己的移动操作
    5. };
    6. class HasX {
    7. public:
    8. X ele; // X有何合成的移动操作
    9. };
    10. X x;
    11. X x2 = std::move(x); // 使用合成的移动构造函数
    12. HasX hx;
    13. HasX hx2 = std::move(hx); //使用合成的移动构造函数

    但是在列举的四种情况就是错的,移动操作则会被定义为删除的。

    注意:定义了一个移动构造函数或移动赋值运算符的类必须也定义自己的拷贝操作,否则,这些成员默认地被定义为删除的。

  6. 如果一个类既有移动构造函数,也有拷贝构造函数,编译器则会使用普通的函数匹配规则来确定会使用哪个构造函数,赋值也是类似;

  7. 如果一个类没有移动构造函数,那么函数匹配原则则会保证该类型的对象会被拷贝,即使试图通过调用move来移动也是如此;

    例子:

    1. class Foo {
    2. public:
    3. Foo() = default;
    4. Foo(const Foo&); //拷贝构造函数
    5. //并未定义移动构造函数
    6. };
    7. Foo f1;
    8. Foo f2(f1); //拷贝构造函数
    9. Foo f3(std::move(f1)); //拷贝构造函数,因为未定义移动构造函数

    注意:用拷贝操作代替移动操作是安全的,一般情况下,前者都会满足后者的要求;

  8. 拷贝并交换赋值运算符和移动操作

    同时实现移动构造函数和赋值运算符:

    1. class Person {
    2. public:
    3. // 添加的移动构造函数
    4. Person(Person && p) noexcept : name(p.name) {
    5. p.name = "";
    6. }
    7. // 赋值运算符既是移动赋值运算符,也是拷贝赋值运算符
    8. Person& operator=(Person p) {
    9. swap(*this, p);
    10. return *this;
    11. }
    12. private:
    13. string name;
    14. };

    赋值运算符会交换两个对象的指针以及成员,在swap之后,p的指针会指向this,而this则会指向p的内存,当p离开其作用域之后,其的成员也会被销毁,自然两者就会同时实现喽~;

C++ tuple类型的更多相关文章

  1. Tuple类型

    Tuple类型类似的体现了C#中的匿名类型 var person=new { Name="Eric"; Age=18: } 调用: Console.writeline( perso ...

  2. python学习第五天 List和tuple类型介绍及其List切片

    List 和tuple: python提供一种类似C语言数组的类型,但是使用起来确是相当的简洁.那就讲讲这神奇的python中list 和tuple吧. List类型: 1.直接贴代码: L = [' ...

  3. Scala Tuple类型

    Tuple可以作为集合存储不同类型的数据,初始化实例如下: val tuple = (1,3,3.14,"aa") val third = tuple._3 Tuple 下标访问从 ...

  4. .net 4.0 中的特性总结(四):Tuple类型

    Tuple是具有指定数量和顺序的值的一种数据结构.针对这种数据结构,.Net4.0中提供了一组Tuple类型,具体如下: Tuple   Tuple<T>   Tuple<T1, T ...

  5. Python之List和Tuple类型(入门3)

    转载请标明出处: http://www.cnblogs.com/why168888/p/6407682.html 本文出自:[Edwin博客园] Python之List和Tuple类型 1. Pyth ...

  6. Tuple类型的使用

    1.什么是Tuple Tuple类型,可以存放任何类型 2.Tuple有哪些分类 .Net 4.0 定义了8个泛型Tuple类,和一个Tuple静态类 3.Tuple的使用

  7. 元组tuple类型内置方法

    目录 元组tuple类型内置方法 用途 定义 常用操作+内置方法 优先掌握 存一个值or多个值 有序or无序 可变or不可变 元组tuple类型内置方法 元组是不可变的列表,在定义完成后后面就不可以进 ...

  8. Python开发的入门教程(二)-List和Tuple类型

    介绍 本文主要介绍Python中List和Tuple类型的基本知识和使用. Python创建list Python内置的一种数据类型是列表:list.list是一种有序的集合,可以随时添加和删除其中的 ...

  9. C++中tuple类型

    tuple是C++11新标准里的类型.它是一个类似pair类型的模板.pair类型是每个成员变量各自可以是任意类型,但是只能有俩个成员,而tuple与pair不同的是它可以有任意数量的成员.但是每个确 ...

  10. List tuple 类型转成数组

    SKlearning大部分的输入数据都是M * N数组. 然而我们从数据库或文件读取得来的通常是Python内定的类型tuple或list 它们的优势就不说了,但是直接把list或tuple构成的二维 ...

随机推荐

  1. oracle 创建表空间 、用户 、赋权、建表

    一.创建表空间 1.创建临时表空间 create temporary tablespace TS_TEM_TAB_SPACE tempfile 'D:\oracle\TS_TEM_TAB_SPACE. ...

  2. 产看Linux运行时间

    Linux下如何查看系统启动时间和运行时间 1.uptime命令输出:16:11:40 up 59 days, 4:21, 2 users, load average: 0.00, 0.01, 0.0 ...

  3. python进阶学习之高阶函数

    高阶函数就是把函数当做参数传递的一种函数, 例如: 执行结果: 1.map()函数 map()接收一个函数 f 和一个list, 并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 l ...

  4. JVM性能调优监控工具——jps、jstack、jmap、jhat、jstat、hprof使用详解

    摘要: JDK本身提供了很多方便的JVM性能调优监控工具,除了集成式的VisualVM和jConsole外,还有jps.jstack.jmap.jhat.jstat.hprof等小巧的工具,本博客希望 ...

  5. javaweb作业二

    作业:1.书写servlet的类架构及重要方法.(ServletConfig,Servlet)<---GenericServlet(getInitParameter(String str);in ...

  6. 20165203《Java程序设计》第八周学习总结

    20165203<Java程序设计>第八周学习总结 教材学习内容总结 第12章 进程与线程 进程的完成过程:代码加载.执行至执行完毕 线程:一个进程由多个线程组成. 线程的完成过程:自身的 ...

  7. SRILM语言模型格式解读

    先看一下语言模型的输出格式 \data\ ngram = ngram = ngram = \-grams: -5.24036 'cause -0.2084827 -4.675221 'em -0.22 ...

  8. 使用spring-boot-maven-plugin插件打包spring boot项目

    在spring-boot项目中使用spring-boot-maven-plugin插件进行打包,输出可执行JAR包.项目包含多个模块,当打完包后在本地的maven仓库中发现输出的可执行JAR非常小,并 ...

  9. ubuntu编译安装postgresql

    闲着没事用源码编译安装了postgresql,遇到了不少故障,记录一下. 1:用./configure配置时发生错误.看信息说是缺少相关包.有什么readline,zlip等. 我配置的很简单,只是配 ...

  10. 8-10 Coping Books uva714

    题意:把一个包含m个正整数的序列划分为k个   1<=k<=m<=500的非空连续子序列  使得每个正整数恰好属于一个序列  设第i个序列的各个数之和为 Si   你的任务是让所有的 ...