C++11移动语义解析

2023-11-12

当给函数传递对象当做函数参数时,可以使用引用类型来减少拷贝对象的代价,尤其是避免容器的拷贝等。 但是当把函数内的局部对象当做返回值时,我们无法返回该局部对象的引用,导致每次返回局部对象都会进行拷贝。 因为返回局部对象的引用是无意义的,当函数调用完成,局部对象就被析构,所以其引用指向了一块析构的内存。程序如果使用移动操作,避免了拷贝,将新变量指向了局部变量的内容。例如:

std::vector<int> GetNums() {
  std::vector<int> nums;
  nums.push_back(1);
  nums.push_back(2);
  return nums;
}


std::vector<int> result = GetNums();  // 调用vector的移动构造函数,避免了拷贝。
标准库的容器vector,string等实现了拷贝语义,所以这些容器作为函数的局部对象时都可以直接返回。
 

C++11提供了移动语义,分别是移动构造函数和移动赋值运算符,那么什么时候会触发移动语义?

当右值引用被当做初始值或者赋值操作的右侧运算对象时,程序将使用移动操作。

1. 对于移动构造函数两种情况会触发移动语义:函数返回把临时对象当做返回值,使用std::move。

2. 对于移动赋值运算符三种情况会触发移动语义:函数返回把临时对象当做返回值,使用std::move, 临时对象。

以以下代码为例:


#include <string.h> 
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <map>
#include <utility>
#include <functional>
#include <algorithm>
#include <cassert>
 
class Student {
public:
  Student() {
    id_ = 0;
    name_ = nullptr;
  }
 
  Student(int id, const char* name) {
    std::cout << "Constructor" << std::endl;
    id_ = id;
    size_t len = strlen(name);
    name_ = new char[len + 1];
    strcpy(name_, name);
  }
 

  ~Student() {
    std::cout << "De-constructor" << std::endl;
    id_ = 0;
    if (name_ != nullptr) {
      delete[] name_;
      name_ = nullptr;
    }
  }
 
  Student(const Student& student) {
    std::cout << "Copy Constructor" << std::endl;
 
    id_ = student.id_;
 

    size_t len = strlen(student.name_);
    name_ = new char[len + 1];
    strcpy(name_, student.name_);
  }
 
  Student(Student&& student) {
    std::cout << "Move Constructor" << std::endl;
     id_ = student.id_;
     name_ = student.name_;
 
    student.id_ = 0;
    student.name_ = nullptr;
  }
 
  Student& operator=(const Student& student) {
    std::cout << "Copy Assignment" << std::endl;
 
    if (this == &student) {
      return *this;
    }
 
    id_ = student.id_;
 
    size_t len = strlen(student.name_);
    name_ = new char[len + 1];
    strcpy(name_, student.name_);
 
    return *this;
  }
 
  Student& operator=(Student&& student) {
    std::cout << "Move Assignment" << std::endl;
 
    if (this == &student) {
      return *this;
    }
    if (name_ != nullptr) {
      delete[] name_;
    }
 
    id_ = student.id_;
    name_ = student.name_;
 
    student.id_ = 0;
    student.name_ = nullptr;
 
    return *this;
  }
 
  void Print() {
    std::cout << id_ << " : " << (name_ == nullptr ? "NULL": name_) << std::endl;
  }
 
private:
  int id_;
  char* name_;
};
 
void PrintStudent(Student student) {
  student.Print();
}
 
Student GetStudent() {
  printf("get student\r\n");
  Student student(123, "123");
  return student;
}
 
int main() {
  Student a(1, "1");
  std::cout << "====================================" << std::endl;
 
  PrintStudent(a);  
  //copy constructor : 按值传递形参,需要将a拷贝到一个临时对象‘tempA’
  //de-con :tempA析构
  std::cout << "================1====================" << std::endl;
 
  PrintStudent(Student(2, "2"));  
  //con:(2,'2')构建一个临时对象作为形参传递给PrintStudent(引用方式)
  //de-con:临时对象析构
  std::cout << "================2====================" << std::endl;
 
  PrintStudent(std::move(Student(2, "2")));  
  //con: (2,"2")构建一个临时对象A(左值)
  //move con:将临时对象A move成右值引用对象B
  //de-con: 临时对象A析构
  //de-con:临时对象B析构
  std::cout << "================3====================" << std::endl;
 
  PrintStudent(std::move(a));  
  a.Print();  
  //move con:将对象a move成右值引用对象b
  //de-con: 对象b析构
  std::cout << "================4====================" << std::endl;
 
  Student a2 = GetStudent(); 
  //con:当函数返回值为对象时,返回临时对象的引用,相当于直接构造新对象避免了拷贝
  std::cout << "================5====================" << std::endl;
 
  Student a3;
  a3 = Student(3, "3");  
  //con:构造临时对象
  //move assign:将临时对象copy给a3, 此时用的是move assign
  //de-con: 临时对象析构
  std::cout << "================6====================" << std::endl;
 
  Student a4(4, "4");
  a3 = a4;  
  //con:构造对象a4
  //copy assign: 对象a4赋值给a3, 此时用的是copy assign
  std::cout << "================7====================" << std::endl;
 
  a3 = std::move(a4);  
  //move assign : 将对象a4 move成右值引用对象3,此时用的是move assign
  std::cout << "================8====================" << std::endl;
 
  a3 = GetStudent();  

  //con : 构造对象student
  //move assign: student对象赋值给a3, 此时用的是move assign
  //de-con: student对象析构
  std::cout << "================9====================" << std::endl;
 
  return 0;
}

参考文章:

C++11右值引用(一看即懂)​​​​​​c++ 之 std::move 原理实现与用法总结_学之知之的博客-CSDN博客_std::move

C++11的移动语义_博客-CSDN博客

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++11移动语义解析 的相关文章

随机推荐

  • 太牛叉了!解决“卡脖子”的国产自主 IDE [狗头.jpg]

    推荐关注 综合整理 程序员的那些事 ID iProgrammer 解决 卡脖子 的自主创新 IDE 最近有一个的国产自主创新的 CEC IDE 震动了程序员圈子 在 CEC IDE 官网简介中的 安全可控 条目自称 国企品牌 自主研发 注意
  • k8s运维 pod、node、namespace、pv处于terminating的原因及处理方法

    1 概述 node pod ns pv由于一些原因在生产中处于terminating的状态 常规方法无法删除 一下总结了一些原因以及删除方法 2 node处于Terminating状态原因及处理方法 node节点不可达的情况下 kubect
  • MATLAB嵌套循环求解1到1000的素数和

    熬夜打卡 代码都跑过一遍的 没有任何问题啦 方法一 matlab的嵌套循环 重在理解 clc clear s 0 for i 2 1000 for j 2 32 if mod i j break end end if j gt i j s
  • 【华为OD机试】阿里巴巴找黄金宝箱(IV)(C++ Python Java)2023 B卷

    时间限制 C C 1秒 其他语言 2秒 空间限制 C C 262144K 其他语言524288K 64bit IO Format lld 题目描述 一贫如洗的樵夫阿里巴巴在去砍柴的路上 无意中发现了强盗集团的藏宝地 藏宝地有编号从0 N的箱
  • 常用的BOM属性 - Kaiqisan

    终于出狱了 今天重新恢复博客的更新 大致谈谈我复习面试上面的查漏补缺的内容 首先讲讲什么是BOM BOM简单来说就是浏览器对象 只有js在浏览器环境运行时才会被赋予的对象 location对象 该对象内所有的属性都与URL有关 常常用于提取
  • 攻防世界————fileclude(内含php伪协议菜鸟讲解)

    先进去发现为一坨php代码 新手勉勉强强看得懂 接下来我们分析代码 WRONG WAY
  • Angular2-使用Augury来调试Angular2程序

    原文链接 http www jianshu com p efecaea287f2 推荐 Augury Angular专用的chrome 调试插件 如题 就在前几天的2016 12 8谷歌开发者大会上 angular2的leader来给我们演
  • idea字体主题集合

    http color themes com view index
  • 意念控制四旋翼 学习笔记

    第一部分 模块原始数据 拿到模块 在网上查了一圈 发现基本没什么有用的资料 很多都是一些相关但是没有实际价值的东西 许多论文都是再谈怎么去做 而没有实实在在的去完成这么一个过程 废话不多说 直接步入正题 昨天在网上才发现这个软件 据评论说是
  • 最近大火的「元宇宙」是什么?

    公众号后台回复 图书 了解更多号主新书内容 作者 腾讯技术工程特约撰稿人 李佳华 本文将介绍元宇宙的由来和底层技术 探讨海内外资本在这条赛道上的布局 元宇宙将会对哪些行业产生变革的影响 这些影响背后凸显了元宇宙的哪些价值 以及元宇宙逐步实现
  • openwrt reboot流程

    openwrt 系统中 当执行了 reboot 命令 系统将会发生什么事情呢 如何进行重启的呢 下面来一起看一下 reboot 应用层操作 首先 reboot 是由busybox 它是一个集成了常用Linux命令和工具的软件 提供的一个Li
  • leetcode算法面试题:串联所有单词的子串问题、单词拆分问题

    串联所有单词的子串问题 给定一个字符串 s 和一些 长度相同 的单词 words 找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置 注意子串要与 words 中的单词完全匹配 中间不能有其他字符 但不需要考虑 word
  • 数据挖掘算法基础-关联规则

    数据挖掘中 被常拿来说的啤酒尿布的例子就是一个很典型的运用关联算法来做购物来分析的例子 常被用于交易数据 关系数据的分析 发现数据集中隐藏的频繁模式 这些频繁模式可以用关联规则的形式表示 有效的关联规则对商家的商品进出货摆放都有很大的指导意
  • 直方图均衡化与直方图规定化

    一 认识图像 当我们面对图像的时候 我们面对的是抽象的矩阵 如下图 下面是0 255的灰度图像的表示 密密麻麻的 那么我们做的直方图 其实就是对这些像素值的统计 如下图所示 其中Bin表示条数 数据和范围是对图的解释 二 为什么要做直方图均
  • qt 嵌入web页面_Qt -在应用程序中嵌入Web内容之环境搭建

    一 Qt应用程序与Web结合的发展 1 从Qt5 5开始 Qt WebKit模块被废弃了 取而代之的是Qt WebEngine模块 当时可以使用该模块将应用程序与Web技术结合 2 Qt WebEngine模块提供了一个Web浏览器引擎 可
  • ChatGPT:概述Vue.js中data函数初始化和created钩子函数调用的顺序和问题解决方法

    ChatGPT 概述Vue js中data函数初始化和created钩子函数调用的顺序和问题解决方法 我将输入一段Vue代码 请你记住 created console log this queryInfo this getClueList
  • Libuv源码分析 —— 6. 事件循环【uv_run】

    通过之前的学习 咱们已经明白了在事件循环中的三个核心内容 分别是 Libuv源码分析 定时器 Libuv源码分析 idle prepare check Libuv源码分析 poll io 现在让咱们从头捋一遍事件循环到底完成了什么功能呢 u
  • scrapy里面的response.xpath(“用xpath插件找打的路径“)返回值为空?

    response xpath 用xpath插件找打的路径 返回值为空 1 可能是因为路径是有问题的 2 可能是start urls的路径是有问题的 可以从network中找找路径 复制一下
  • 使用vant2问题整理

    1 export createVNode imported as createVNode was not found in vue possible exports EffectScope computed customRef defaul
  • C++11移动语义解析

    当给函数传递对象当做函数参数时 可以使用引用类型来减少拷贝对象的代价 尤其是避免容器的拷贝等 但是当把函数内的局部对象当做返回值时 我们无法返回该局部对象的引用 导致每次返回局部对象都会进行拷贝 因为返回局部对象的引用是无意义的 当函数调用