当前位置: 首页 > news >正文

C++11中的移动语义

一、核心概念

  • 左值与右值

    • 左值(Lvalue):可以取地址的对象(有名字的变量),如int a = 5;中的a
    • 右值(Rvalue):临时对象或字面量(无名字),如5、表达式a + b的结果、函数返回的临时对象。
  • 移动语义
    当操作右值(临时对象)时,不复制其资源(如堆内存),而是直接 “窃取” 资源的所有权,避免不必要的内存分配 / 释放,提升效率。

二、移动构造函数

1. 定义

移动构造函数是一种特殊的构造函数,参数为当前类的右值引用,用于从右值对象 “移动” 资源到新对象。

class MyString {
private:char* data;int length;public:// 移动构造函数(参数为右值引用)MyString(MyString&& other) noexcept : data(other.data), length(other.length) {// 标记原对象资源为“空”,避免析构时重复释放other.data = nullptr;other.length = 0;}
};
2. 特点
  • 参数必须是右值引用(&&),且通常不应该是const(因为需要修改原对象)。
  • 通常用noexcept修饰(告诉编译器此函数不会抛出异常,便于容器优化)。
  • 作用:接管右值对象的资源(如堆内存、文件句柄),原对象会被置为 “空状态”(避免析构冲突)。

三、移动赋值运算符

1. 定义

移动赋值运算符用于将一个右值对象的资源 “移动” 给已存在的对象,类似于移动构造,但针对的是赋值操作。

class MyString {
public:// 移动赋值运算符MyString& operator=(MyString&& other) noexcept {if (this != &other) {  // 避免自赋值// 释放当前对象的资源delete[] data;// 接管右值对象的资源data = other.data;length = other.length;// 标记原对象资源为“空”other.data = nullptr;other.length = 0;}return *this;}
};
2. 特点
  • 与移动构造类似,参数为右值引用,通常用noexcept修饰。
  • 需先释放当前对象的资源,再接管右值对象的资源,最后处理原对象。

四、与拷贝构造 / 赋值的区别

操作拷贝构造 / 赋值移动构造 / 赋值
参数常量左值引用(const T&右值引用(T&&
行为复制资源(深拷贝)转移资源所有权(不复制)
原对象状态保持不变被修改为 “空状态”(如指针置空)
适用场景左值对象(有名字的变量)右值对象(临时对象)

五、问题

  1. 何时会调用移动构造 / 赋值?

    • 当实参是右值时(如临时对象、std::move转换的左值)。
      例:
    MyString a;
    MyString b = std::move(a);  // 调用移动构造(a被转为右值)
    b = MyString("temp");       // 调用移动赋值(临时对象是右值)
    
  2. std::move的作用?

    • 并非 “移动” 资源,而是将左值强制转换为右值引用,让编译器优先选择移动语义。
    • 注意:std::move后原对象可能变为 “空状态”,不应再使用(除非重新赋值)。
  3. 默认移动函数的生成规则?

    • 若用户未定义任何拷贝构造、拷贝赋值、移动构造、移动赋值或析构函数,编译器会自动生成默认移动函数。
    • 若定义了拷贝操作(拷贝构造 / 赋值),编译器不会自动生成移动函数(需手动定义)。
    • 若定义了移动函数,默认拷贝函数会被删除(需显式定义才可用)。
  4. 为什么移动函数通常用noexcept

    • 标准容器(如vector)在扩容时,若元素的移动构造是noexcept,会使用移动语义;否则会使用拷贝(避免移动中抛异常导致数据丢失)。
    • 加上noexcept可提升容器操作的效率。
  5. 移动语义的优势?

    • 避免临时对象的深拷贝,减少内存分配 / 释放开销(尤其对大对象,如字符串、容器)。
    • 例:函数返回大对象时,编译器可通过移动语义避免拷贝(返回值优化 RVO 的补充)。
  6. 如何禁用移动语义?

    • 显式删除移动函数:MyString(MyString&&) = delete;
    • 或定义拷贝函数(编译器不再生成默认移动函数)。

六、经典场景示例

#include <iostream>
#include <utility> // for std::moveclass Resource {
private:int* data;
public:Resource() : data(new int[1000]) { std::cout << "构造函数:分配资源\n"; }// 移动构造Resource(Resource&& other) noexcept : data(other.data) {other.data = nullptr;std::cout << "移动构造:转移资源\n";}// 移动赋值Resource& operator=(Resource&& other) noexcept {if (this != &other) {delete[] data;data = other.data;other.data = nullptr;std::cout << "移动赋值:转移资源\n";}return *this;}~Resource() {if (data) delete[] data;std::cout << "析构函数:释放资源\n";}
};int main() {Resource a;               // 构造:分配资源Resource b = std::move(a); // 移动构造:b接管a的资源(a变为空)Resource c;c = std::move(b);         // 移动赋值:c接管b的资源(b变为空)return 0;
}

输出:

构造函数:分配资源
移动构造:转移资源
构造函数:分配资源
移动赋值:转移资源
析构函数:释放资源
析构函数:释放资源
析构函数:释放资源

http://www.dtcms.com/a/323641.html

相关文章:

  • Python训练营打卡Day27-类的定义和方法
  • 【后端】Java Stream API 介绍
  • C++11 ---- 线程库
  • 机器学习(西瓜书)学习——绪论
  • 编译技术的两条演化支线:从前端 UI 框架到底层编译器的智能测试
  • 通过MQTT实现OTA升级方案
  • Linux-Redhat9.5静默安装Oracle19.25单实例教程【参照官方文档,超级详细】
  • 【35】C#实战篇——StopRecordingTimer_Tick事件函数中,解绑函数自己,那么该函数会立即结束吗?还会继续执行该函数中剩余部分吗?
  • windows、linux应急响应入侵排查
  • Oracle数据库重启后打开异常状态的检查步骤
  • 模拟人脑处理文本——从分句到分词,从段落到时间线叙事
  • MySQL时间类型
  • windows上LM-Studio下载安装教程
  • 谷歌搜索 sg_ss 逆向分析
  • 自闭和标签形式(self-closing tags)和标准标签形式
  • [概率 DP]808. 分汤
  • C++入门学习3
  • 开漏和推挽模式的区别
  • QT第一讲- Qt初探
  • XSS攻击演示
  • 常用信号深度解析(SIGINT、SIGPIPE、SIGALRM、SIGTERM等)
  • 101-基于Python的个性化音乐推荐系统
  • 码上爬第三题【协程+浏览器调试检测】
  • 本文章分享一个本地录音和实时传输录音给app的功能(杰理)
  • [GPU]什么是“硬件TL”在UnityURP中的体现
  • 疏老师-python训练营-Day40训练和测试的规范写法
  • 并发编程基础:继承Thread vs 实现Runnable - 深入解析与最佳实践
  • Tob大客户销售面试经验
  • 华为交换机进阶功能和场景化配置
  • 最长回文子串(马拉车/Manacher‘s )算法