C++ 之 移动构造函数

news2025/1/11 1:54:17

1、左值和右值

C++( 包括 C) 中所有的表达式和变量要么是左值,要么是右值。

  1. 通俗的左值的定义就是非临时对象,那些可以在多条语句中使用的对象,表达式结束后依然存在的持久化对象,所有的具名变量或者对象都是左值。
  2. 右值是指临时的对象,它们只在当前的语句中有效,他就像是代表了一个由编译器创建的临时内存位置,在访问它之后,该内存就会被回收,该值也不能被访问了。

具体代码如下 :

int i = 0;           // i是左值, 0是右值,i在当前表达式结束之后,依然可以被使用,被引用,而0不行,它是临时值,当前表达式结束之后,此表达式中的0值就不存在了
 
class A {
  public:
    int a;
};
 A getTemp()
{
    return A();
}
A a = getTemp();      // a是左值  getTemp()的返回值是右值(临时变量)
 
((i>0) ? i : j) = 1;   //右值不只是存在在表达式的右边,在此表达式中,0作为右值出现在表达式的左边,并且将值赋值给i或者j。  
 
const int &a = 1;     //在 C++11 之前,右值是不能被引用的,最大限度就是用常量引用绑定一个右值
 
T().set().get();      //在这种情况下,右值是可以被修改的,T 是一个类,set 是一个函数为 T 中的一个变量赋值,get 用来取出这个变量的值。在这句中,T() 生成一个临时对象,就是右值,set() 修改了变量的值,也就修改了这个右值。

2、左值引用和右值引用

(1)左值引用就是最传统的引用 &。
如下:

int a = 10;
int& refA = a; // refA是a的别名, 修改refA就是修改a, a是左值,左移是左值引用
int& b = 1; //编译错误! 1是右值,不能够使用左值引用

(2)C++ Primer Plus 第6版18.1.9中说到,c++11中增加了右值引用,右值引用关联到右值时,右值被存储到特定位置,右值引用指向该特定位置,也就是说,右值虽然无法获取地址,但是右值引用是可以获取地址的,该地址表示临时对象的存储位置。具体语法如下:

int&& a = 1; // &&是右值引用的符号,实质上就是将不具名(匿名)变量取了个别名
int b = 1;
int && c = b; //编译错误! 不能将一个左值复制给一个右值引用

(3) 右值引用的三个特点:
a、通过右值引用的声明,右值又“重获新生”,其生命周期与右值引用类型变量的生命周期一样长,只要该变量还活着,该右值临时量将会一直存活下去。

class A {
  public:
    int a;
};
A getTemp()
{
    return A();
}
A && a = getTemp();   //getTemp()的返回值是右值(临时变量)getTemp()返回的右值本来在表达式语句结束后,其生命也就该终结了(因为是临时变量),
                      //而通过右值引用,该右值又重获新生,其生命期将与右值引用类型变量a的生命期一样,只要a还活着,该右值临时变量将会一直存活下去。

b、右值引用独立于左值和右值。意思是右值引用类型的变量可能是左值也可能是右值。
从下面的代码中可以看到,T&&表示的值类型不确定,可能是左值又可能是右值,这就是右值引用的一个特点。

template<typename T>
void f(T&& t){}
 
f(10); //t是右值
 
int x = 10;
f(x); //t是左值

c、T&& t在发生自动类型推断的时候,它是未定的引用类型(universal references),如果被一个左值初始化,它就是一个左值;如果它被一个右值初始化,它就是一个右值,它是左值还是右值取决于它的初始化。

template<typename T>
void f(T&& t){}
f(10); //t是右值   //这里发生自动类型推断
 
int x = 10;
f(x); //t是左值    //发生自动类型推断
 
template<typename T>
class Test {
    Test(Test&& rhs); //这里不会发生类型推断,因为已经是确定的Tset &&类型的
};

3、const左值引用

从上面的例子可以看到,
左值引用只能绑定左值,右值引用只能绑定右值,如果绑定的不对,编译就会失败。但是,const左值引用却是个奇葩,它可以算是一个“万能”的引用类型,它可以绑定非常量左值、常量左值、右值,而且在绑定右值的时候,常量左值引用还可以像右值引用一样将右值的生命期延长,缺点是,只能读不能改。

const int & a = 1; //常量左值引用绑定 右值, 不会报错
 
class A {
  public:
    int a;
};
A getTemp()
{
    return A();
}
const A & a = getTemp();   //不会报错 而 A& a 会报错

总结如下(其中T是一个类型):
左值引用, 使用 T&, 只能绑定左值
右值引用, 使用 T&&, 只能绑定右值
常量左值, 使用 const T&, 既可以绑定左值又可以绑定右值
已命名的右值引用,编译器会认为是个左值

4、移动构造函数和移动赋值函数

首先,移动构造函数是一个构造函数,他是用来构造一个对象的,和拷贝构造函数、构造函数等价。但与默认构造函数不同,编译器不提供默认移动构造函数。移动构造函数和移动赋值函数所执行的是同样的操作,只不过情况不一样,一种是直接构造一个对象,另一种是利用“=”运算符把一个对象赋值给另一个对象的时候。
移动构造函数和移动赋值函数与拷贝构造函数所执行的作用的是一样的,都是通过一个对象去构造一个新对象。但有时候我们会遇到这样一种情况,我们用对象a初始化对象b,后对象a我们就不在使用了,但是对象a掌握的内存资源仍然存在(在析构之前),既然拷贝构造函数,实际上就是把a对象的内容复制一份到b中,那么为什么我们不能直接使用a对象掌握的内存空间?这样就避免了新的空间的分配,大大降低了构造的成本。这就是移动构造函数设计的初衷。
总的来说,就是类中有指针类型的成员变量时,当遇到对象构造对象时,需要使用拷贝构造函数中的深拷贝的方式把该指针成员赋值,这种深拷贝的方法会重新在堆上分配成员指针分配内存,当出现多次上述对象a初始化对象b之后,对象a不在存在的情况是,程序就会在堆上分配多次内存,大大降低程序运行效率,所以移动构造函数就将即将放弃的对象的内存空间直接给新对象使用,就能避免许多临时对象的创建,也能避免程序多次在堆上申请空间,从而大大的提高了执行效率。他的原型如下:

 A & operator=(A &&); //右值引用
  //正确的移动构造函数的写法
  A(A && a):p(a.p)
  {
        std::cout<<"move c"<<std::endl;
        //如果调用A的移动构造函数,则打印 "move c"
        a.p=nullptr;     //移动构造函数的灵魂在与使临时对象的指针为空,从而占据临时对象的内存空间,在临时对象析构的时候也不会造成指针悬挂。
  }
  //正确的移动赋值函数写法
  A & operator=(A && a){
    if(&a == this)
      return *this;
      p=a.p;
      a.p=nullptr;
      return *this;
  }

对于移动构造函数,需要以下几点需要注意:
参数(右值)的符号必须是右值引用符号,即“&&”,因为右值是即将析构的对象,将它的内存空间变废为用,可以提高效率。
参数(右值)不可以是常量,因为我们需要修改右值。
参数(右值)的资源链接和标记必须修改。否则,右值的析构函数就会释放资源。转移到新对象的资源也就无效了。
移动构造函数的原理如下图:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/42877.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

<Android开发> Android vold - 第一篇 vold前言简介

本次主要讲解存储模块如U盘等设备在android设备中的管理和使用的模块。本次主要基于android 8.1版本进行解析。不同android版本 vold的内容可能会有所差异。读者可对比阅读解析。 1 Vold介绍 Android中Vold是volume Daemon,即Volume守护进程;Android没有使用Linux平台下的ud…

泛型的介绍和使用方法

目录 一、泛型概述 二、泛型类 三、泛型接口 1.直接在实现类中确定好类型 2.实现类也写成泛型类 四、泛型方法 五、泛型好处 六、泛型集合 1.概念 2.特点 一、泛型概述 1. 本质是参数化类型&#xff0c;把类型作为参数传递。 2. 常见的形式有泛型类、泛型接口、泛型…

【虚幻引擎】UE4/UE5 后期处理盒子(PostProcessVolume)

一、简介 PostProcessVolume&#xff08;后期处理盒子&#xff09;&#xff1a;UE4非常强大的一个后期处理&#xff0c;可以调节画面的色彩&#xff0c;相机的景深&#xff0c;视频的输出效果&#xff0c;环境的光线构造&#xff0c;电影级的氛围感。 二、参数介绍 一、场景中…

驱动开发 Linux按键中断点灯

华清远见上海中心22071班 三个按键实现按键中断&#xff0c; key1->led1 key2->led2 key3->led3 按键按一下灯亮&#xff0c;再按一下灯灭 #include <linux/module.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/f…

外贸供应链ERP怎么选?全流程综合管理解析

随着外贸体制深入改革、进出口权放开等&#xff0c;以往处于垄断地位&#xff0c;享有种种优惠政策的外贸企业&#xff0c;将面临越来越严峻的国内外市场的竞争及各种挑战。长期以来形成的相对落后的管理体制和经营模式&#xff0c;严重地影响外贸企业在新形势下的生存和发展。…

m基于GA遗传优化+SA模拟退火的混合改进算法的多产品多机器生产优化matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 这里&#xff0c;我们首先介绍一下改进算法的基本原理&#xff0c;按照前面说的&#xff0c;这里我们主要将GA和SA进行合并。 这里&#xff0c;我研究了下&#xff0c;将两种算法做如下方法的结合…

Microcorruption 第一关 Tutorial

Microcorruptioin 第一关 Tutorial 首先进入Tutorial这一关&#xff0c;这是闯关页面。 在主函数设置断点&#xff0c;控制台输入"break main"或"b main"或手动点击反汇编栏中main函数的第一行设置断点。 该闯关游戏主要是破解密码&#xff0c;查看主函数…

前端开发免费资源分享

Fancy-Border-Radius 地址&#xff1a;https://9elements.github.io/fancy-border-radius/ 简介&#xff1a;在线编辑border-radius的可视化工具&#xff0c;通过调节可以很方便地帮我们生成想要的形状&#xff0c;然后直接复制下面的css代码&#xff0c;即可使用 Make some …

sCrypt 合约中的椭圆曲线算法:第二部分

我们在脚本中实现了椭圆曲线 (EC) 算法。在之前的实现中&#xff0c;我们进行链下计算并在脚本中验证结果。我们这里直接用脚本计算。 基于EC的应用非常多&#xff0c;特别是在密码学领域&#xff0c;如数字签名、加密、承诺方案等。作为具体示例&#xff0c;我们重新实现了 E…

11.28作业

实现对点灯所涉及函数的封装 1.头文件 #ifndef __GPIO_H__ #define __GPIO_H__ //结构体封装 typedef struct{volatile unsigned int MODER;volatile unsigned int OTYPER;volatile unsigned int OSPEEDR;volatile unsigned int PUPDR;volatile unsigned int IDR;volatile un…

Kotlin进阶指南 - 单元测试

为了减少一些功能繁琐的测试流程&#xff0c;单元测试是提升开发效率的有效方式之一 在早些年的时候我有记录过一篇 Android 使用单元测试&#xff0c;只不过当时更多的针对 Java 方面的单元测试&#xff1b;在使用 Kotlin 后&#xff0c;我发现单元测试有点不同&#xff0c;好…

Nacos注册中心和服务消费方式

目录 一&#xff0c;服务治理介绍 什么是服务治理&#xff1f; 常见的注册中心 二&#xff0c;nacos简介 三&#xff0c;搭建nacos环境 四&#xff0c;代码演示 五&#xff0c;基于Feign实现服务调用 什么是Feign Feign的使用 Feign参数传递 一&#xff0c;服务治理介…

全国心力衰竭日:重症心衰的黑科技——永久型人工心脏

今天是第8个“全国心力衰竭日”。近几年&#xff0c;中国逐渐老龄化&#xff0c;心衰则是老龄化面临的严峻问题。我国心衰患病率估计已达1.3%&#xff0c;至少有1000万心力衰竭患者。中国已成为世界上拥有最大心衰患者群的国家之一。心力衰竭作为大多数心血管疾病的终末阶段&am…

如何在 docker 容器使用 nginx 实现反向代理统一站点入口

在微服务架构下&#xff0c;我们会部署很多微服务来实现我们的系统。每个微服务会有不同的端口。而用户在访问我们的站点时希望通过统一的端口来访问所有的服务&#xff0c;因为在很多情况下用户只能通过 80 或者 443 端口访问外界服务。 这个时候我们就可以使用反向代理来实现…

云上“两地三中心”,中小企业都用得起的多保险灾备方案

在云时代&#xff0c;大部分中小型企业都奔跑在云上或是服务器托管公司。任何规模的数据中心服务中断都会让你的企业踩雷。据统计&#xff0c;80%的数据中心服务中断都是由服务器硬件造成的。 据万博智云不完全统计&#xff1a; 2021年3月&#xff0c;一场大火完全摧毁了OVH在…

[附源码]计算机毕业设计SpringBoot蛋糕购物商城

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

如何将pdf转word?这几个软件可以做到文档格式转换

我们在日常办公中使用较为频繁的就是pdf和word两种文件格式&#xff0c;pdf的兼容性较好&#xff0c;就算跨设备传输也不会出现乱码的情况&#xff1b;word文档可以直接进行编辑修改&#xff0c;各有各的好处。如果我们想对pdf的内容进行修改的话&#xff0c;就需要把pdf文件转…

优维低代码:Redirect 路由重定向If 条件渲染

优维低代码技术专栏&#xff0c;是一个全新的、技术为主的专栏&#xff0c;由优维技术委员会成员执笔&#xff0c;基于优维7年低代码技术研发及运维成果&#xff0c;主要介绍低代码相关的技术原理及架构逻辑&#xff0c;目的是给广大运维人提供一个技术交流与学习的平台。 连载…

Centos7扩容root分区

Background 安装centos7系统时&#xff0c;如果没有自定义分区&#xff0c;系统默认给root分配的空间只有50G&#xff0c;其他空间都分配给了你创建的普通用户/home目录。这里我们把home的空间匀出一部分给root。 1、首先要注意数据安全 备份home目录的数据 tar zcf /tmp/hom…

vue学习1~18

1. vue基础知识和原理 1.1 初识Vue 想让Vue工作&#xff0c;就必须创建一个Vue实例&#xff0c;且要传入一个配置对象demo容器里的代码依然符合html规范&#xff0c;只不过混入了一些特殊的Vue语法demo容器里的代码被称为【Vue模板】Vue实例和容器是一一对应的真实开发中只有…