【90】【Cherno C++】【中字】stdmove与移动赋值操作符
▶️移动构造与std::move
接上节的String类,我们可以通过string来构造新的对象dest:
// 拷贝构造
String string = "Hello";
String dest = string;
为了使用移动构造函数,string需要cast为临时变量:
// 移动构造
String dest = (string&&)string;
// 写法等价于
String dest((string&&)string);
这种cast方法并不是对每个类型都适用,例如,auto类型不能这样静态地推导。➡️➡️std::move
// 更优雅地移动构造
String dest(std::move(string));
如果你需要把一个已经存在的变量变成临时变量,可以标记它,表示你可以从这个特定的变量中偷取资源。即,使用std::move将其变成临时变量,这样就可以使用移动构造函数或移动赋值操作符来进行移动,从那个对象中获取资源。
▶️赋值操作符
以上操作都构造了一个新对象,这和接下来要说的赋值不同。
*赋值操作符:只有当把一个变量赋值给一个已有的变量时才会被调用。例如:
String string = "Hello";
// 构造
String dest(std::move(string));
// 赋值
dest = std::move(string);
此处的=
运算符实际就是对现有变量的赋值运算符。
▶️移动赋值函数:实际上是把另一个对象移到当前对象中。
// 移动赋值函数
String& operator=(String&& other) noexcept {
printf("Moved!\\n");
m_Size = other.m_Size;
m_Data = other.m_Data;
other.m_Data = nullptr;
other.m_Size = 0;
}
-
要赋值就需要覆盖当前对象,因为当前对象可能已经分配了一些内存。但是,如果直接将m_Data等于other.m_Data,就会造成内存泄漏,因为我们没有办法删除旧的数据。所以我们需要先删除旧数据:
String& operator=(String&& other) noexcept { printf("Moved!\\n"); delete[] m_Data; // 删除当前对象的旧数据 m_Size = other.m_Size; m_Data = other.m_Data; other.m_Data = nullptr; other.m_Size = 0; }
-
通常在赋值操作符中,还需要保证当前对象不等于other对象(自赋值操作)。不可以进行如下操作:
dest = std::move(dest);
自赋值操作时,移动赋值函数会删除dest数据,丢失数据。为了防止这种情况发生:
String& operator=(String&& other) noexcept { printf("Moved!\\n"); // 判断是否有自赋值操作 if(this != &other) { delete[] m_Data; m_Size = other.m_Size; m_Data = other.m_Data; other.m_Data = nullptr; other.m_Size = 0; } return *this; }
-
测试的main函数:
int main() { String apple = "Apple"; String dest = "Dest"; std::cout << "Apple: "; apple.Print(); std::cout << "dest: "; dest.Print(); dest = std::move(apple); std::cout << "Apple: "; apple.Print(); std::cout << "dest: "; dest.Print(); std::cin.get(); }
打印如下:
Created! // 创建apple Created! // 创建dest Apple: Apple // apple.Print() dest: Dest // dest.Print() Moved! // 移动赋值操作 Apple: // apple为空,内存被偷走 dest: Apple // dest为Apple Destroyed! Destroyed!
移动赋值使apple被设置为空,dest被设置为Apple,即转移了整个字符数组的所有权,没有做任何复制、分配或解除分配,相当于交换了两个变量。
❓移动构造和移动赋值的区别?
虽然都有用到=
符号,但移动构造函数是构造了一个新对象,移动赋值运算符是对已存在的对象赋值。
// 移动构造
String dest = std::move(apple);
// 移动赋值
dest = std::move(apple);
// 等价于
dest.operator=(std::move(apple));
标签:Cherno,String,dest,C++,运算符,other,Data,string,赋值
From: https://www.cnblogs.com/rthete/p/17545389.html