zoukankan      html  css  js  c++  java
  • C++11核心知识点 —— 移动语义

    这篇根据一些文章整理,对移动语义进行详细记录

    移动语义

    const& 复制构造存在的问题

    复制构造在前面的文章中有记录,它的主要一个问题在于使用const &进行常引用,导致被复制的对象不能修改。按照常理来说,一般不需要修改被复制的对象,但在某些情况下却非常有用。
    首先看个代码:

    Person make_person(){
        auto person=Person();
        return person;
    }

    分析:
    首先产生一个局部对象,由于返回的是一个对象,因此产生复制构造操作,而且要求复制构造函数必须为const &的形式,否则出现错误:找不到合适的复制构造函数。因此Person的定义至少像这种形式:

    class Person{
    public:
        Person(const Person &person){...} //const &复制构造
    };

    这种样式是没有问题的,但是考虑一种情况,如果Person拥有资源怎么办?假如它有一个char *分配了100个字节的堆,那么问题演变成下面的模型:

    模型:已知A有资源R,现在希望能将R的所有权移给B,出发点在于避免重复多次的分配资源

    由于复制构造是常引用,我们可以做到把B.R = A.R,这样B拥有了A的资源,但是两者都指向同一块资源,在析构中势必会delete资源,那么B得到的资源将会无效!
    因此思路可以转换为:如果有一个复制构造不仅可以赋值,而且可以修改被复制的对象,那么这个问题就解决了。

    解决思路如下:B.R = A.R,然后将 A.R =nullptr。这样对象A即使析构使用delete也没有问题。在这种需求下,移动复制构造隆重出场!

    移动复制构造

    移动复制构造主要和右值有关系。右值主要包括临时对象和字面量的形式。在C++中有几种复制构造,因此编译器会选择最合适的重载。还是以上面的例子为例,在产生临时对象这一步,编译器选择的重载顺序如下:
    移动复制构造 > const &复制构造

     代码比较清楚的说明这一点,在临时对象采用哪种形式的构造上,编译器首选是移动复制构造,其中第四步的std::move下面记录下。

    class Person {
    public:
        char *m_pSource; //资源
        Person(const Person &person) {
            std::cout << "复制构造执行" << std::endl;
        }
        Person(Person &&person) {
            this->m_pSource = person.m_pSource;
            person.m_pSource = nullptr;
            std::cout << "移动复制构造执行" << std::endl;
        }
        Person() {
            m_pSource = new char[100];
            printf("原始资源:%p
    ", m_pSource);
        }
        ~Person() { 
            delete[]m_pSource; //释放资源
            std::cout << "析构执行" << std::endl; 
        }
    };
    Person test_Person() {
        Person person = Person();
        return person;
    }
    
    int main()  
    {
        Person p1 = test_Person();
        Person p2(std::move(p1));
        printf("p2资源:%p
    ", p2.m_pSource);
        printf("p1资源:%p
    ", p1.m_pSource);
    }

    再论右值及std::move

    std::move等价为对左值执行static_cast<T &&>操作,那么原对象将会变成临终值,临终值也是右值的一种形式。用处在于可以选择移动复制构造的重载。正如上面4标识的,std::move(p1)之后将会调用右值引用的重载。
    如果不使用,则调用const &的重载,比如 Person p3(p2)。

    经过一系列的move,原始资源从最初的局部对象person,最终辗转到p2,person -> p1 ->p2,printf说明了这一点

    一道测试题

    /*
        @一道测试题,如果能看懂代码为什么出问题,说明整个基础概念都了解了
        @tinaluo 2021-02-15夜
    */
    class Person {
    public:
        char *m_pSource; //资源
        Person(const Person &person) {
            std::cout << "复制构造执行" << std::endl;
        }
        Person(Person &&person) {
            this->m_pSource = person.m_pSource;
            person.m_pSource = nullptr;
            std::cout << "移动复制构造执行" << std::endl;
        }
        Person() {
            m_pSource = new char[100];
            printf("原始资源:%p
    ", m_pSource);
        }
        ~Person() { 
            delete[]m_pSource; //释放资源
            std::cout << "析构执行" << std::endl; 
        }
    };
    
    Person test_args(Person person) {
        printf("add %p
    ", person.m_pSource);
        return person;
    }
    int main()  
    {
        Person p1;
        printf("add %p
    ", p1.m_pSource);
        test_args(p1);
    }
  • 相关阅读:
    jython resources
    Installing a Library of Jython ScriptsPart of the WebSphere Application Server v7.x Administration Series Series
    jython好资料
    ulipad install on 64bit win7 has issue
    an oracle article in high level to descibe how to archtichre operator JAVA relevet project
    table的宽度,单元格内换行问题
    Linux常用命令大全
    dedecms系统后台登陆提示用户名密码不存在
    登录织梦后台提示用户名不存在的解决方法介绍
    Shell常用命令整理
  • 原文地址:https://www.cnblogs.com/tinaluo/p/14403167.html
Copyright © 2011-2022 走看看