为什么会有右值引用?(移动构造、移动赋值)

news/2024/11/28 20:43:05/

         目录

1、左值引用的缺陷

2、移动构造:解决临时对象的深拷贝

3、拓展:移动赋值 


1、左值引用的缺陷

左值引用作为函数参数传递,减少了参数拷贝;但是作为函数返回值,并不适用于所有场景,比如要返回一个临时对象。

直接返回临时对象的话,此时编译器就会调用拷贝构造,构造一个新的临时对象保存到main函数的栈上,然后销毁原本的临时对象。如果对象中有一个大小为10000的数组,深拷贝对程序运行效率影响较大

class Point
{
public:Point(int x, int y): _x(x), _y(y) {}Point(const Point& p):_x(p._x), _y(p._y){cout << "Point(Point& p)被调用了 —— 深拷贝" << endl;}Point& operator=(const Point& p){_x = p._x;_y = p._y;}Point getPos() {Point p(_x, _y);        // 创建临时对象return p;               // 返回临时对象}
private:int _x;int _y;
};

2、移动构造:解决临时对象的深拷贝

移动构造是右值引用的应用之一,其本质就是将要返回的临时对象的资源移动到其他地方。下面通过示意图和案例来了解。

(1) 移动构造的本质

移动构造也是构造函数的一种,区别在于,传递的参数必须是右值,即表达式、函数返回值等。其本质是将右值保存的资源,转移到其他临时对象。以上述 getPos 函数为例

getPos函数返回一个临时对象A,临时对象A出了作用域就被销毁,肯定不能直接返回,所以会先调用移动构造(函数返回值是右值)。

移动构造会将临时对象A的资源转移到新构造的对象B中,然后再销毁临时对象A,这样的话,就减少了深拷贝,提升程序的运行效率。

 

(2) 移动构造具体实现

所谓转移资源,其实就是将临时对象A和临时对象B的资源交换:

  • 临时对象A:传递的右值形参 p
  • 临时对象B:构造的对象 *this
// 移动构造
Point(Point&& p)
{// cout << "Point(Point&& p) 被调用了 —— 资源转移" << endl;this->swap(p);
}// 资源交换
void swap(Point& p)
{std::swap(_x, p._x);std::swap(_y, p._y);
}

(3) 移动构造测试

我们在先前的代码中加入移动构造,然后使用同样的方法测试

class Point
{
public:Point(int x, int y): _x(x), _y(y) {}Point(const Point& p):_x(p._x), _y(p._y){cout << "Point(Point& p)被调用了 —— 深拷贝" << endl;}Point& operator=(const Point& p){_x = p._x;_y = p._y;}// 移动构造Point(Point&& p){cout << "Point(Point&& p) 被调用了 —— 资源转移" << endl;swap(*this, p);}// 交换资源void swap(Point& p){std::swap(_x, p._x);std::swap(_y, p._y);}Point getPos() {Point p(_x, _y);return p;}
private:int _x;int _y;
};

 

3、拓展:移动赋值 

(1) 起因

当我们把一个对象赋给另一个对象的时候,其实就是把一个对象里的内容完全拷贝到另一个对象,这个时候也会发生深拷贝。

Point p(10, 20);
// Point p1 = p;        // 会被编译器优化成调用拷贝构造
Point p1(20, 30);
p1 = p;        // 赋值完毕以后打算舍弃 p

假如我们的目的是,将 p 赋给 p1 以后舍弃对象p,若对象p中包含一个大小为10000的数组,这个时候的深拷贝就比较影响效率了。

(2) 移动赋值

移动赋值借用了移动构造的思路,既然不需要对象p,那就索性将一个对象p的资源转移到对象p1

那么我们就需要新增一个 operator=() 的重载了

Point& operator=(Point&& p)
{// cout << "Point& operator=(Point&& p) 被调用了 —— 移动赋值,资源转移" << endl;this->swap(p);    // swap 函数的定义详见上面的移动构造return *this;
}

 

(3) 移动赋值测试

测试用的代码需要稍微改动一下,移动赋值的形参必须是一个右值,这里我们需要用move函数将对象p 转换成右值。其内部也是以特殊的方式直接返回传入的对象,这样的话返回值就变成了一个临时对象,也就是右值。

int main() {Point p(10, 20);Point p1(20, 30);p1 = move(p);return 0;
}


http://www.ppmy.cn/news/19016.html

相关文章

使用 Grafana 请求API接口

目的: 使用Grafana 配合JSON API 插件 请求API接口,完成可视化,实现一些简单的请求功能 假设我们想将如下的API接口返回的json数据可视化 这里借用一下 小熊同学的 金融数据接口 用请求如下接口举例 https://api.doctorxiong.club/v1/fund/detail?code000001&startDat…

树莓派配置Python虚拟环境、安装PyQt5、安装PySide2

要从头设置好一台可用于开发的树莓派&#xff0c;可以参考树莓派 4B 无屏幕&#xff0c;连接WiFi、SSH、VNC&#xff0c;系统换源、pip换源&#xff0c;安装中文输入法 Python虚拟环境 树莓派&#xff08;或者说arm平台&#xff09;使用Python虚拟环境的正确方式是使用pipenv…

网关超详解

文章目录网关详解一、Spring Cloud Gateway用法二、实现三、网关的分类四、什么是网关五、网关的作用路由负载均衡统一鉴权统一处理跨域统一业务处理访问控制发布控制流量染色接口保护统一日志统一文档网关详解 一、Spring Cloud Gateway用法 去看官网&#xff1a;https://sp…

casbin权限和配置文件的理解

官方文档 基础权限模型 下图为我基于个人理解画出来的(关于多租户RBAC模型可能有误) 发现一篇博客讲的还行Casbin权限模型&#xff0c;看他的权限系统设计模型分析部分 casbin配置文件内容的结构解释 注意matchers可以设置多个。我在知道这个之前一直疑惑为什么需要policy_…

Sprig框架集成(SSM框架) | Sping+SpringMVC+Mybatis

SSM框架 SSM是spingspringMVCmybatis集成的框架&#xff1a;标准的MVC模式&#xff0c;整个系统划分为表现层&#xff0c;controller层&#xff0c;service层&#xff0c;DAO层四层 Spring&#xff08;业务层&#xff09; Spring就像是整个项目中装配bean的大工厂&#xff0c;在…

我的硕士前半生

本篇文章属于随笔类&#xff0c;它可能无法对你起到直接帮助&#xff0c;它只是我这个普通学生一年半以来的一些足迹与思考。本文首发于我的个人博客 Forever Young 我的本科像硕士&#xff0c;有实验室有工位&#xff0c;桌子超级大&#xff0c;有老师带有学长可以问。 我的硕…

mysql之存储过程

目录 1.概念 2.创建语法 3.调用 4.示例 5.删除 6.查看 1.概念 存储过程和函数类似java中的方法&#xff0c;能够提高代码的重用性&#xff0c;能够简化操作 一组预先编译好的SQL语句的集合&#xff0c;理解成批处理语句&#xff0c;减少了编译次数并且减少了和数据库服…

用户画像计算更新

3.1 用户画像计算更新 目标 目标 知道用户画像建立的流程应用 无 3.1.1 为什么要进行用户画像 要做精准推送同样可以使用多种推荐算法&#xff0c;例如&#xff1a;基于用户协同推荐、基于内容协同的推荐等其他的推荐方式&#xff0c;但是以上方式多是基于相似进行推荐。而构…