移动构造函数和移动赋值函数

移动构造函数

1
2
3
4
5
6
MyClass( MyClass && other ) noexcept
: data( other.data ), size( other.size ) /* 转移资源,浅拷贝 */
{
other.data = nullptr; /* 防止源对象析构时释放资源 */
other.size = 0;
}

我们之前学习过拷贝构造函数,并且还深刻去讨论为什么这样写拷贝构造函数的原因。

相比拷贝构造函数,移动构造函数的参数是类型为&&是可以理解的,因为移动语义接受右值,但是为什么没有 const 属性了?

在移动构造或移动赋值操作中,通常会从源对象中“移动”资源(例如指针或文件句柄等),然后将源对象置于一种安全的状态(例如将指针设为 nullptr 或将资源句柄重置)。如果参数为 const Type&&,那么在移动构造中就无法修改源对象的状态,这样移动的语义就无法实现。

移动构造函数中 data 指向 临时资源 other.data

移动赋值函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
MyClass & operator=( MyClass && other ) noexcept
{
if ( this != &other ) /* 防止自我赋值 */
{
delete[] data; /* 释放当前对象的资源,用以接收 other 的 data 资源 */

/* 转移资源 */
data = other.data;
size = other.size;

/* 置空源对象的资源 */
other.data = nullptr;
other.size = 0;
}
return(*this);
}

移动语义和复制语义的根本不同

移动语义内部是不会 new 新对象的,这也是为什么它比复制语义高效的缘故。

移动构造的成员变量赋值是浅拷贝(把堆空间由相应的成员变量指向,自己不会 new 的),而拷贝构造是深拷贝(堆空间需要自己 new,然后把数据全部拷贝出来)。

移动赋值也是一样的,只不过它需要先把自己的资源释放掉,因为赋值发生在两个已存在的对象之间,然后接下来的操作就和移动构造没有什么区别。

看来移动语义和复制语义的根本不同点就是,移动语义是浅拷贝(改变指向),复制语义是深拷贝(重新 new)。