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

相关文章:

  • 优化器:SGD、Adam、RMSprop等优化算法对比与机器翻译应用
  • day 16 stm32 IIC
  • 【Java EE初阶 --- 网络原理】JVM
  • 堆----3.数据流的中位数
  • 【Redis】Redis-plus-plus的安装与使用
  • 自定义通知组件跟随右侧边栏移动
  • SQL基本
  • 探索Trae:使用Trae CN爬取 Gitbook 电子书
  • 2025-08-09 李沐深度学习14——经典卷积神经网络 (2)
  • 生态问题是什么?
  • P1890 gcd区间
  • 如何理解SA_RESTART”被信号中断的系统调用自动重启“?
  • SELinux 入门指南
  • ROS2 多线程 与组件机制
  • Python NumPy入门指南:数据处理科学计算的瑞士军刀
  • Qt 的对象线程亲和性规则
  • 华为欧拉OpenEnler系统在启动MindIE时权限问题的解决方法
  • 从灵感枯竭到批量产出:无忧秘书创作平台如何重构内容生产者的工作流程?全环节赋能分析
  • Spring Boot 集成 Quartz 实现定时任务(Cron 表达式示例)
  • WinForm 中 ListView 控件的实战应用与功能拓展
  • kafka架构原理快速入门
  • AI大语言模型在生活场景中的应用日益广泛,主要包括四大类需求:文本处理、信息获取、决策支持和创意生成。
  • 软件定义车辆加速推进汽车电子技术
  • Blender 快捷键速查表 (Cheat Sheet)
  • 【线性代数】6二次型
  • 可直接运行的 Playwright C# 自动化模板
  • 通过 Certimate 统一管理 SSL 证书 支持自动化申请、全平台部署
  • 【线性代数】线性方程组与矩阵——(1)线性方程组与矩阵初步
  • 数据挖掘2.6 Perceptron Modeling 感知器建模
  • 我想做自动化报社保,用哪种技术更好一点呢?