【ChernoC++笔记】移动赋值运算符

【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;
}
  1. 要赋值就需要覆盖当前对象,因为当前对象可能已经分配了一些内存。但是,如果直接将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;
    }
  2. 通常在赋值操作符中,还需要保证当前对象不等于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;
    }
  3. 测试的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));
posted @   rthete  阅读(53)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
主题色彩
点击右上角即可分享
微信分享提示