当前位置: 首页 > 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.xdnf.cn/news/1271053.html

相关文章:

  • 【无标题】AI 赋能日常效率:实用案例与操作心得分享
  • B.10.01.6-DDD领域驱动设计:从理论到落地的完整指南
  • 数据挖掘2.6 Perceptron Modeling 感知器建模
  • Qdrant Filtering:must / should / must_not 全解析(含 Python 实操)
  • 心灵笔记:正念冥想
  • 解决python错误:playwright._impl._errors.TimeoutError: Timeout 30000ms exceeded.
  • 3.5.2_1 随机访问介质访问控制
  • Python中的Lambda函数详解
  • 【排序算法】④堆排序
  • NTP /Chrony 网络时间协议
  • Leetcode-19. 删除链表的倒数第 N 个结点
  • 比较useCallback、useMemo 和 React.memo
  • 机器学习 K-Means聚类 无监督学习
  • 第4章 程序段的反复执行for语句P115练习题(题及答案)
  • 元宇宙技术如何改变社交方式?
  • 哈希与安全
  • pgAdmin 仪表盘的system部分不能显示,报SYSTEM_STATS扩展没有安装
  • C++ 中的智能指针
  • Qt 综述:从基础到一般应用
  • 机器翻译中的语言学基础详解(包括包括语法、句法和语义学等)
  • 记一次奇异的bug
  • n8n 入门指南:更适合跨境出海搞钱的AI智能体
  • 基于 InfluxDB 的服务器性能监控系统实战(一)
  • vue3上传的文件在线查看
  • 【linux基础】Linux命令提示符解析与操作指南
  • 如何在 Ubuntu 24.04 LTS Linux 上安装 Azure Data Studio
  • 编译技术的两条演化支线:从前端 UI 框架到底层编译器的智能测试
  • “自动报社保 + 查询导出 ” 的完整架构图和 Playwright C# 项目初始化模板
  • 基于IPD体系的研发项目范围管理
  • 【渲染流水线】[几何阶段]-[几何着色]以UnityURP为例