对象移动

右值的定义:只能出现在等号“=”右边的表达式。

当我们需要拷贝或者复制一个右值的时候,我们可以考虑移动右值。因为拷贝会新建一个对象,当对象初始化需要分配内存,拷贝的分配内存开销也很大,而移动不会新建对象,而是接管右值的资源。
对象移动是相对于对象拷贝的,在一些场景(比如:把对象作为一个参数传递)移动会比拷贝提升性能。还有一个原因是有些对象禁止拷贝:例如IO类和unique_ptr。

被移动的对象必须是右值,如果不是可以用std::move()显式转换为右值,但转换之后不能对这个对象的值做任何假设,分配内存的指针清空。

对移动操作的定义最好是noexcept的,因为标准库容器比如vector在push_back的过程中有可能会重新分配内存,需要将原来的对象拷贝或者移动到新内存。如果移动过程是有可能出现异常的话,那移动到一半出了异常,而前边移动的对象的值都改变了(移动之后不能对被移动的对象的值做任何假设),使得vector无法恢复成push_back前的样子。所以移动构造函数如果没有noexcept,vector会优先使用拷贝构造函数。

移动的两种具体形式:移动构造函数、移动赋值函数

class B{
public:
	string s = "s";
	B(){}
	//移动构造函数
	B(B&& b) noexcept :s(std::move(b.s)) {}
	//移动赋值函数
	B& operator=(B&& b)noexcept{
		s = std::move(b.s);
	}
};

如果类的对象都是可移动(存在移动构造函数)的而且这个类没有定义拷贝构造函数,operator= 和 析构函数,那编译器会为这个类合成移动构造函数。

class B{
public:
	string s = "s";
};

int main()
{
	B b;
	B a = b;
	cout << a.s<< b.s << endl;
	return 0;
}
//输出为两个s
int main()
{
	B b;
	B a = std::move(b);
	cout << a.s<< b.s << endl;
	return 0;
}
//输出为一个s,b的s被移动给了a
posted @ 2022-05-18 17:24  hellozhangjz  阅读(61)  评论(0编辑  收藏  举报