数据窃取——移动操作

来源:互联网 发布:单片机的时钟电路图 编辑:程序博客网 时间:2024/06/04 22:13

右值引用

C++11标准中引入了一种新的引用类型——右值引用(rvalue reference),右值引用必须绑定到一个右值上,用&&来获取右值引用。

右值

左值表示一个对象的身份,如:

返回左值引用的函数、下标、解引用、前置递增/递减运算符

而右值表示一个对象的

返回非引用类型的函数、算数运算符、位运算、逻辑运算符、后置递增/递减运算符

我们看出右值有以下特点

  1. 右值没有其他用户
    右值是无名的,只在他存在的语句中使用,过后甚至都无法再找到它
  2. 右值即将被销毁
    右值是一个字面值或临时变量,使用过后资源就被释放

右值的右值引用不是右值

int &&rr1 = 43;         //字面值是一个右值,声明一个右值引用来绑定它int &&rr2 = rr1;        //错误,rr1是一个左值

rr1是一个右值引用,但它本身却不是一个右值,原因是rr1是一个变量,有名字并且有着和其他变量一样的生成期,并不是一个临时对象,所以rr1是一个左值。


拷贝和移动(资源)

类经常会管理一部分资源,形如

class A{public:    string *ps;    //构造函数,真正分配资源    A(const string &_s = "hello"):ps(new string(_s)) {         cout << "constructed" << endl;    }    //析构函数,释放资源    ~A()    {        delete(ps);        cout << "destructed" << endl;        system("pause");    }    //拷贝构造函数,真正分配资源    A(const A& rhs):ps(new string(*rhs.ps)) {         cout << "copy" << endl;    }};

new产生的内存就是我们所称的资源,在用一个类的对象初始化另一个对象时,会执行类的拷贝构造函数(根据拷贝行为的不同又分为深拷贝和浅拷贝,浅拷贝只负责源对象控制资源的指针,并不赋值资源,与源对象共享资源)。
在对象的赋值时也会有类似的行为。

有了右值的概念后,我们发现在用一个临时对象初始化一个类对象或者为其赋值时,这个临时对象和它的资源会被拷贝,而执行完这个语句后,临时对象及其资源就会被销毁,这样就造成了不必要的操作,反正临时对象也是即将被销毁的,我们可否不销毁临时对象的资源,也不拷贝这个资源,而是直接利用这个资源?

于是就有了移动操作——当构造函数或赋值运算符的对象是一个右值时,我们直接把右值的资源“盗取”过来

//移动构造函数A(A&& rhs):ps(rhs.ps) {         rhs.ps = nullptr;    }

这个移动构造函数做了两件事:

  1. 让构造对象的ps指针指向临时变量的资源,构造对象并没有重新new自己的资源
  2. 把临时对象的ps指针置为空,临时变量销毁时可以安全的析构,不会把那块资源销毁
    这里写图片描述

std::move

std::move只是返回一个对象的右值引用,用来触发类中定义的移动构造函数、移动赋值运算符

//move在标准库中的实现template<class _Ty>    constexpr remove_reference_t<_Ty>&&        move(_Ty&& _Arg) _NOEXCEPT    {   // forward _Arg as movable    return (static_cast<remove_reference_t<_Ty>&&>(_Arg));    }

移动赋值运算符

用一个临时对象为类的对象赋值时,同样可以使用移动操作来避免不必要的拷贝。

//移动赋值操作A& A::operator=(A &&rhs){    this->ps = rhs.ps;    rhs.ps = nullptr;}

实际上还有一个更加通用和安全的写法

A& A::operator=(A rhs){    //类中定义的void swap(const A&,cosnt A&)操作    //并非std::swap     //    swap(*this,rhs);    return *this;}void A::swap(const A& lhs,const A& rhs){    using std::swap;    swap(lhs.ps,rhs.ps);}

operator=接受一个A的对象而不是引用,在参数传递时会构造一个临时对象
这里写图片描述
这样的操作是安全的,因为临时对象会正常的析构

在传参时的那一次构造也合理的,根据实参是左值还是右值,会调用拷贝或是移动构造函数

  • 如果是左值,左值的资源不应该被销毁,这一次拷贝必不可少
  • 如果是右值,调用移动构造函数并没有分配新的资源,不会造成浪费
原创粉丝点击