~$ 存档

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

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

移动语义

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\n", 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\n", p2.m_pSource);
    printf("p1资源:%p\n", 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\n", m_pSource);
    }
    ~Person() { 
        delete[]m_pSource; //释放资源
        std::cout << "析构执行" << std::endl; 
    }
};

Person test_args(Person person) {
    printf("add %p\n", person.m_pSource);
    return person;
}
int main()  
{
    Person p1;
    printf("add %p\n", p1.m_pSource);
    test_args(p1);
}
posted on 2021-02-15 03:55  LuoTian  阅读(227)  评论(0编辑  收藏  举报