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);
    }
  • 相关阅读:
    LoadRunner如何监控Linux下的系统资源
    shareeverything and sharenothing原理区别
    LoadRunner下如何监控Windows系统资源
    简明 Vim 练级攻略
    自由地使用那10000个Web协议的License进行压力测试
    自己做的西直门桥
    Visual Studio问题解答(不断更新)
    记2012微软编程之美全国挑战赛
    matlab也能创作歌曲
    拈游戏
  • 原文地址:https://www.cnblogs.com/tinaluo/p/14403167.html
Copyright © 2011-2022 走看看