1. 基本介绍
-
移动赋值语法原理
移动赋值语法的原理是基于右值引用(rvalue reference)的概念。在C++11中引入了右值引用,通过使用双 ampersands(&&)来声明一个右值引用。 -
右值引用
右值引用可以绑定到临时对象(即右值),例如字面量、临时对象或者表达式的结果。与左值引用不同,右值引用可以被修改,因此可以将资源从一个对象转移到另一个对象。 -
移动赋值运算符
移动赋值运算符 (operator=)是一种特殊的成员函数,允许将一个对象的资源从另一个对象“移动”到当前对象而不是进行拷贝。它通常被实现为接受一个参数为右值引用类型的函数。 -
移动赋值的实现步骤
在实现移动赋值运算符时,通常需要执行以下步骤:
a. 检查自我赋值:使用 if (this != &other) 确保不是自我赋值。
b. 通过使用 std::move() 将资源从其他对象移动到当前对象。
c. 将其他对象中该资源的状态置为适当的默认状态。例如,在基本类型中将其设为0,在类中可能需要特定的操作。
d. 返回当前对象的引用。 -
移动赋值优势
移动赋值语法使得在某些情况下能够更高效地管理资源而无需进行额外的数据拷贝。这对于大型数据结构或具有显式资源管理的类特别有用,例如动态分配的内存或文件句柄等。
2. 代码示例:
下面是一个使用移动赋值的类及其子类的示例:
构建一个基类,一个子类,基类包含移动构造与移动赋值,同时子类中也包含移动构造与移动赋值操作;
#include <iostream>class Base {
public:Base(int data) : mData(data) {}// 移动构造函数Base(Base&& other) noexcept {std::cout << "Base Move constructor" << std::endl;mData = std::move(other.mData);other.mData = 0;}// 移动赋值运算符Base& operator=(Base&& other) noexcept {std::cout << "Base Move assignment operator" << std::endl;if (this != &other) {mData = std::move(other.mData);other.mData = 0;}return *this;}protected:int mData;
};class Derived : public Base {
public:Derived(int data1, int data2, int extraData): Base(data1), mMoreData(data2), mExtraData(extraData) {}// 移动构造函数Derived(Derived&& other) noexcept : Base(std::move(other)) {std::cout << "Derived Move constructor" << std::endl;mMoreData = std::move(other.mMoreData);other.mMoreData = 0;mExtraData = std::move(other.mExtraData);other.mExtraData = 0;}// 移动赋值运算符Derived& operator=(Derived&& other) noexcept {std::cout << "Derived Move assignment operator" << std::endl;if (this != &other) {Base::operator=(std::move(other));mMoreData = std::move(other.mMoreData);other.mMoreData = 0;mExtraData = std::move(other.mExtraData);other.mExtraData = 0;}return *this;}private:int mMoreData;int mExtraData;
};int main() {// 创建一个派生类对象并赋值给另一个派生类对象Derived derived1(20, 30, 40);Derived derived2 = std::move(derived1);return 0;
}
3. 运行结果:
运行这段代码,输出结果为:
Base Move constructor
Derived Move constructor
在这个示例中,Derived 类添加了一个成员变量 mExtraData。在移动构造函数和移动赋值运算符中,我们需要先调用基类的对应函数来处理基类的成员变量,然后再处理派生类的成员变量。在移动构造函数和移动赋值运算符中,必须确保不会忘记调用基类的相应函数。
4. 特别注意事项:
请注意,在移动赋值运算符中,我们需要检查自我赋值,并在释放资源之前将原对象的成员变量设置为适当的值。此外,在派生类的移动构造函数和移动赋值运算符中也需要处理派生类的额外成员变量。