c++移动构造函数

左值与右值

  • 左值:能用取址符号 & 取出地址的值
  • 右值:不能用取值符合取出地址的值,如临时对象
int i = 1; // i 是左值,1 是右值

int GetZero {
    int zero = 0;
    return zero;
}
//j 是左值,GetZero() 是右值,因为返回值存在于寄存器中
int j = GetZero();

//s 是左值,string("no name") 是匿名变量,是右值
string s = string("no name");

左值引用与右值引用

左值引用就是最传统的引用 &,如下:

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

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

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

右值引用的三个特点:

  • 通过右值引用的声明,右值又“重获新生”,其生命周期与右值引用类型变量的生命周期一样长,只要该变量还活着,该右值临时量将会一直存活下去
class A {
  public:
    int a;
};
A getTemp()
{
    return A();
}

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

  • 右值引用独立于左值和右值。意思是右值引用类型的变量可能是左值也可能是右值。

从下面的代码中可以看到,T&&表示的值类型不确定,可能是左值又可能是右值,这就是右值引用的一个特点

template<typename T>
void f(T&& t){}
 
f(10); //t是右值
 
int x = 10;
f(x); //t是左值
  • 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 &&类型的
};

     

const左值引用

左值引用只能绑定左值,右值引用只能绑定右值,如果绑定的不对,编译就会失败。

但是const左值引用可以绑定非常量左值、常量左值、右值,而且在绑定右值的时候,常量左值引用还可以像右值引用一样将右值的生命期延长,缺点是,只能读不能改。

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

左值引用, 使用 T&, 只能绑定左值
右值引用, 使用 T&&, 只能绑定右值
常量左值, 使用 const T&, 既可以绑定左值又可以绑定右值
已命名的右值引用,编译器会认为是个左值

std::move

std::move() 能把左值强制转换为右值

移动构造函数的出现原因

首先,移动构造函数是一个构造函数,他是用来构造一个对象的,和拷贝构造函数、构造函数等价。但与默认构造函数不同,编译器不提供默认移动构造函数。移动构造函数和移动赋值函数所执行的是同样的操作,只不过情况不一样,一种是直接构造一个对象,另一种是利用“=”运算符把一个对象赋值给另一个对象的时候。


移动构造函数和移动赋值函数与拷贝构造函数所执行的作用的是一样的,都是通过一个对象去构造一个新对象。但有时候我们会遇到这样一种情况,我们用对象a初始化对象b,后对象a我们就不在使用了,但是对象a掌握的内存资源仍然存在(在析构之前),既然拷贝构造函数,实际上就是把a对象的内容复制一份到b中,那么为什么我们不能直接使用a对象掌握的内存空间?这样就避免了新的空间的分配,大大降低了构造的成本。这就是移动构造函数设计的初衷。


总的来说,就是类中有指针类型的成员变量时,当遇到对象构造对象时,需要使用拷贝构造函数中的深拷贝的方式把该指针成员赋值,这种深拷贝的方法会重新在堆上分配成员指针分配内存,当出现多次上述对象a初始化对象b之后,对象a不在存在的情况是,程序就会在堆上分配多次内存,大大降低程序运行效率,所以移动构造函数就将即将放弃的对象的内存空间直接给新对象使用,就能避免许多临时对象的创建,也能避免程序多次在堆上申请空间,从而大大的提高了执行效率
 

移动构造函数的实现

移动构造函数的参数和拷贝构造函数不同,拷贝构造函数的参数是一个左值引用,但是移动构造函数的初值是一个右值引用。这意味着,移动构造函数的参数是一个右值或者将亡值的引用。也就是说,只有当用一个右值,或者将亡值初始化另一个对象的时候,才会调用移动构造函数。移动构造函数的例子如下:

#include<iostream>
using namespace std;

class myobj {
public:
	myobj() :pdata(nullptr) {
		cout << "默认构造函数..." << endl;
	}
	myobj(int data) :pdata(new int(data)) {
		cout << "有参构造函数..." << endl;
	}

	myobj(myobj& md) :pdata(new int(*(md.pdata))) {
		cout << "深拷贝构造函数..." << endl;
	}

	myobj(myobj&& md) :pdata(md.pdata) {
		md.pdata = nullptr;
		cout << "移动构造函数..." << endl;
	}

	~myobj()
	{
		delete this->pdata;
		cout << "析构函数..." << endl;
	}

	friend ostream& operator<<(ostream& os, myobj& md);
	myobj& operator=(myobj&& md)
	{
		if (md.pdata != this->pdata)
		{
			delete pdata;
			this->pdata = md.pdata;
			pdata = nullptr;
		}
		cout << "移动赋值运算符..." << endl;
		return *this;
	}

private:
	int* pdata;
};
ostream& operator<<(ostream& os, myobj& md) {
	os << *(md.pdata);
	return os;
}

myobj getdata()
{
	myobj m(1);
	return m;
}

void test()
{
	myobj m1(10);
	myobj m2 = std::move(m1);//调用移动赋值运算符
	cout << m2 << endl;

	/*
	本来应该调用移动构造函数,因为getdata函数返回的是一个临时对象,是一个右值
	 但是编译器可以通过优化将对象 m 直接放置到 m3 中,
	 而不进行实际的移动构造。
	 这意味着 m3 实际上是 m,而不是通过移动构造函数从 m 移动过来的。

	 可以使用-fno-elide-constructors编译选项来禁用这些优化。
	*/
	myobj m3(getdata());
	cout << m3 << endl;
}

int main()
{
	test();
}

VS中的运行结果:

使用-fno-elide-constructors编译选项禁用优化后的结果:

对于第三次移动构造的发生,笔者查阅后有说可能是与编译器的特殊优化有关 ,目前暂时不清楚其出现的原因

参考:

C++之移动构造函数-CSDN博客

【精选】C++ 移动构造函数详解_吾爱技术圈的博客-CSDN博客 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

汇编与反汇编

程序处理的4个步骤 我们的第一个LED程序涉及两个文件&#xff1a;start.S、main.c&#xff0c;它们的处理过程如下&#xff1a; 对于汇编程序&#xff0c;经过汇编之后&#xff0c;转换成目标文件&#xff08;里面包含机器码&#xff09;。对于C程序&#xff0c;经过预处理之…

TypeScript学习Ts的类型声明,关于类

TypeScript是什么&#xff1f; 以JavaScript为基础构建的语言一个JavaScript的超集可以在任何支持JavaScript的平台上执行TypeScript扩展了JavaScript并添加了类型TS不能被JS解析器直接执行 TypeScript开发环境搭建 下载Node.js安装Node.js使用npm全局安装TypeScript&#x…

BAM(Bottleneck Attention Module)

BAM&#xff08;Bottleneck Attention Module&#xff09;是一种用于计算机视觉领域的深度学习模型结构&#xff0c;它旨在提高神经网络对图像的特征提取和感受野处理能力。BAM模块引入了通道注意力机制&#xff0c;能够自适应地加强或减弱不同通道的特征响应&#xff0c;从而提…

python用tkinter随机数猜数字大小

python用tkinter随机数猜数字大小 没事做&#xff0c;看到好多人用scratch做的猜大小的示例&#xff0c;也用python的tkinter搞一个猜大小的代码玩玩。 猜数字代码 from tkinter import * from random import randint# 定义确定按钮的点击事件 def hit(x,y):global s_Labprint(…

EPLAN中的电位,编号和报表

一、电位-eplan路由的理论基础 电位&#xff0c;信号和网络是eplan中的隐藏三君子。官网帮助中对电位和信号的解释如下&#xff1a; 在 EPLAN 中区分电位和信号。通过电位使连接属性的默认值和电位信息进入到项目中。 通过电位定义点或电位连接点定义一个电位或信号。此处录入…

玄子Share-Git 入门手册

玄子Share-Git 入门手册 简单介绍 Git Git 是一个自由和开源的分布式版本控制系统&#xff0c;旨在快速和高效地处理从小型到大型的所有项目 Git 简单易学&#xff0c;占用空间小&#xff0c;性能快如闪电。它比Subversion、CVS、Perforce和ClearCase等SCM工具更有优势&…

ASO优化之如何进行ios和Android关键词研究2

在关键词研究的这个阶段&#xff0c;提出尽可能多的想法非常重要。不要太在意我们所提出的关键词质量&#xff0c;首先要关注数量。为了最大限度地增加关键的数量&#xff0c;我们将向大家介绍拓展关键词的方法。 1、了解应用程序的作用。 关键词需要描述用户使用我们的应用所…

hub.docker访问不了的问题(一步解决)

暂时我也不清楚&#xff0c;但是下面这个网址可以用(可以先用着)Docker Hub Container Image Library | App Containerization (axlinux.top)https://hub.axlinux.top/

能谈一下 CAS 机制吗

&#xff08;本文摘自mic老师面试文档&#xff09; 一个小伙伴私信我&#xff0c;他说遇到了一个关于 CAS 机制的问题&#xff0c;他以为面试官问的是 CAS 实现单点登录。 心想&#xff0c;这个问题我熟啊&#xff0c;然后就按照单点登录的思路去回答&#xff0c;结果面试官一…

美格智能5G RedCap模组顺利完成中国联通5G物联网OPENLAB开放实验室认证

近日&#xff0c;美格智能5G RedCap模组SRM813Q顺利通过中国联通5G物联网OPENLAB开放实验室端到端的测试验收&#xff0c;并获得OPENLAB实验室的认证证书。这标志着该模组产品各项性能均已符合RedCap商用标准&#xff0c;为5G RedCap规模商用奠定了坚实基础。 中国联通5G物联网…

Element-Plus表单label实现两端对齐(左右对齐)

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 在使用Element-Plus的form的时候,label只有左右,居中对齐&#xff0c;缺少两端对齐的选项 故研究一下如何实现&#xff0c;其他方法也试过&#xff0c;都没效果&#xff0c;我在别人的基础上又研究了一…

【LeetCode: 54. 螺旋矩阵 | 模拟】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

前端学习基础知识

环境搭建 windows环境 nodejs版本管理工具NVM nvm全英文也叫node.js version management&#xff0c;是一个nodejs的版本管理工具。nvm和n都是node.js版本管理工具&#xff0c;为了解决node.js各种版本存在不兼容现象可以通过它可以安装和切换不同版本的node.js。 安装学习访…

Leetcode刷题详解——N 皇后

1. 题目链接&#xff1a;51. N 皇后 2. 题目描述&#xff1a; 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n …

qml编译多语言

qml编译多语言 windows下转换将qml需要转换内容提取转为.ts文件将.ts文件转换为.qm文件.qm文件可以用QTranslator::load进行使用 windows下转换 打开QT自带的 MinGW 控制台 将qml需要转换内容提取转为.ts文件 qml转换字段内容需要qsTr() 将qml转为 filename.ts 文件 转到程序…

电脑怎么录制视频,录制的视频怎么剪辑?

在现今数字化的时代&#xff0c;视频成为了人们日常生活中不可或缺的一部分。因此&#xff0c;对于一些需要制作视频教程、录制游戏或者是进行视频演示的人来说&#xff0c;电脑录屏已经成为了一个必不可少的工具。那么&#xff0c;对于这些人来说&#xff0c;如何选择一个好用…

Hikyuu 1.3.0 发布,高性能量化交易研究框架

Hikyuu 是一款基于 C/Python 的高性能开源量化交易研究框架&#xff0c;用于快速策略分析及回测。与其他量化平台或回测软件相比&#xff0c;具备&#xff1a; 超快的回测速度&#xff1b;对完整的系统交易理念进行抽象&#xff0c;并分解为不同的组件&#xff0c;通过重用不同…

C# Dictionary与List的用法区别与联系

C#是一门广泛应用于软件开发的编程语言&#xff0c;其中Dictionary和List是两种常用的集合类型。它们在存储和操作数据时有着不同的特点和用途。本文将详细探讨C# Dictionary和List的用法区别与联系&#xff0c;并通过代码示例进行对比&#xff0c;以帮助读者更好地选择适合自己…

React进阶之路(四)-- React-router-v6、Mobx

文章目录 ReactRouter前置基本使用核心内置组件说明编程式导航路由传参嵌套路由默认二级路由404路由配置集中式路由配置 Mobx什么是Mobx环境配置基础使用计算属性&#xff08;衍生状态&#xff09;异步数据处理模块化多组件数据共享 ReactRouter 前置 在一开始前端开发都是单…

电力输送、材料和互连领域即将发生巨大变化

在设备互连方面&#xff0c;铜无可匹敌。其低电阻率和高可靠性为业界提供了出色的片上互连和芯片间连线服务。但在逻辑芯片中&#xff0c;随着互连堆栈上升到14级范围&#xff0c;并且阻容(RC)延迟在总延迟中所占的比例越来越大&#xff0c;晶圆厂正在寻求替代金属来维持性能。…