vetcor使用移动构造取代拷贝构造实现push_back

news2024/11/15 11:02:26

昨天说到:vector变量push_back一个对象或变量的时候,本质上是执行拷贝构造,但我想使用移动构造,而不是拷贝构造,本文就修改调试过程,详细分析如何实现移动构造。

昨天的代码如下:(如果有人想测试,可以直接copy)

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <utility>

typedef struct {
	std::string strPortName;
	unsigned int dwBaudRate;
	unsigned char bByteSize;
	unsigned char bStopBits;
	unsigned char bParity;
} TCommPara;

class CComm {
public:
	void config() {
		// 配置串口
	}
};

class CMeterProto {
public:
	virtual bool Init(unsigned char bProp, unsigned char bPn, CComm *pComm) { return false; };
	virtual bool Run() { return false; };
	unsigned char  m_bPn;	//当前操作的测量点
	unsigned char m_bProp;	//设备类型
	TCommPara m_tCommPara;	//通讯参数

protected:
	virtual int GetData() { return -1; };
	virtual int SetData() { return -1; };
};

class CModbusProto : public CMeterProto {
public:
	CModbusProto(const unsigned char bBAPn, const TCommPara tCommPara)
		: m_bBAPn(bBAPn), m_tCommPara(tCommPara) {
		// 构造函数实现
	}

protected:
	virtual int GetData() override;

private:
	unsigned char m_bBAPn;
	TCommPara m_tCommPara;
};

int CModbusProto::GetData()
{
	return 0;
}

class T485CommCtrlPara
{
public:
	T485CommCtrlPara() = default;
	T485CommCtrlPara(const T485CommCtrlPara& other);
	virtual ~T485CommCtrlPara() = default;

	std::unique_ptr<CMeterProto> pComm485Pro;
	CComm cMterCom;
};

T485CommCtrlPara::T485CommCtrlPara(const T485CommCtrlPara& other)
{
	pComm485Pro = nullptr;
	if (other.pComm485Pro)
	{
//		pComm485Pro = std::make_unique<CMeterProto>(*other.pComm485Pro);
		pComm485Pro = std::unique_ptr<CMeterProto>(new CMeterProto(*other.pComm485Pro));
	}
	cMterCom = other.cMterCom;
	std::cout << "copy-ctor" << std::endl;
}

int LoadBA485CommPara(TCommPara& tCommPara, T485CommCtrlPara& t485CommPara, std::vector<T485CommCtrlPara>& vec485DevCommPara) {
	tCommPara.strPortName = "com1";
	tCommPara.dwBaudRate = 9600;
	tCommPara.bByteSize = 8;
	tCommPara.bStopBits = 1;

	t485CommPara.cMterCom.config();
	std::unique_ptr<CMeterProto> pComm485Pro(new CModbusProto(1, tCommPara));
	t485CommPara.pComm485Pro = std::move(pComm485Pro);
	vec485DevCommPara.push_back(t485CommPara);

	return 1;
}

int main() {
	TCommPara tCommPara;
	T485CommCtrlPara t485CommPara;
	std::vector<T485CommCtrlPara> vec485DevCommPara;

	int nBaNum = LoadBA485CommPara(tCommPara, t485CommPara, vec485DevCommPara);
vec485DevCommPara[0].pComm485Pro->Run();
std::cout << vec485DevCommPara.size() << std::endl;
	std::getchar();
	return 0;
}

为了使用移动构造,先修改class T485CommCtrlPara构造函数如下:

 注意,开始的时候,编写移动赋值,在operator=之前,没有添加T485CommCtrlPara::

结果一直就提示:“ error C2801: “operator =”必须是非静态成员

(之前代码是chatgpt写的,有语法错误,提示后,chatgpt更正了错误)

如上代码修改完成后,可以编译和运行,但push_back还是提示使用了拷贝构造函数(查看调试信息),如果要用移动,需要修改为:

如上,显然就调用了移动,查看运行信息,确实调用了move-ctor。

但这只是一个示例程序,实际程序中,不可能在main中定义很多变量执行的,因此,考虑在main之外定义一个函数处理vec485DevCommPara,增加一个接口如下:

 代码编译ok,也能运行,但查看内存,发现异常了。

内存异常!!

原因分析: 

Load485CommPara函数定义了局部变量,

T485CommCtrlPara t485CommPara;

TCommPara tCommPara;

该函数执行完后,这两个变量会销毁,t485CommPara类如下:

class T485CommCtrlPara
{
public:
	T485CommCtrlPara() = default;
	T485CommCtrlPara(const T485CommCtrlPara& other);
	virtual ~T485CommCtrlPara() = default;

	std::unique_ptr<CMeterProto> pComm485Pro;
	CComm cMterCom;
};

这样,在vec485DevCommPara.emplace_back(std::move(t485CommPara))的时候,pComm485Pro移动了,而CComm cMterCom;只是成员变量,如此移动并不成功。

T485CommCtrlPara::T485CommCtrlPara(const T485CommCtrlPara& other)
{
	pComm485Pro = nullptr;
	if (other.pComm485Pro)
	{
		pComm485Pro = std::unique_ptr<CMeterProto>(new CMeterProto(*other.pComm485Pro));
	}
	cMterCom = other.cMterCom;
//	cMterCom = std::move(other.cMterCom);
	std::cout << "copy-ctor" << std::endl;
}

如上两种都不成功,最好办法就是CComm cMterCom也定义为智能指针,如下:

class T485CommCtrlPara
{
public:
	T485CommCtrlPara() = default;
	T485CommCtrlPara(const T485CommCtrlPara& other);
	T485CommCtrlPara(T485CommCtrlPara&& other) noexcept;
	T485CommCtrlPara& operator=(T485CommCtrlPara&& other) noexcept;
	virtual ~T485CommCtrlPara() = default;

	std::unique_ptr<CMeterProto> pComm485Pro;
	std::unique_ptr<CComm> pComm485;
};

T485CommCtrlPara::T485CommCtrlPara(const T485CommCtrlPara& other)
{
	pComm485Pro = nullptr;
	if (other.pComm485Pro)
	{
		pComm485Pro = std::unique_ptr<CMeterProto>(new CMeterProto(*other.pComm485Pro));
		pComm485 = std::unique_ptr<CComm>(new CComm(*other.pComm485));
	}
	std::cout << "copy-ctor" << std::endl;
}

T485CommCtrlPara::T485CommCtrlPara(T485CommCtrlPara&& other) noexcept
{
	pComm485Pro = std::move(other.pComm485Pro);
	pComm485 = std::move(other.pComm485);
	std::cout << "move-ctor" << std::endl;
}

完成后,运行基本正常了:

如上,已经可以正常运行了,但还是一点问题,就是pComm485Pro的基类成员m_tCOmmPara还是为空的。这是因为在new CModbusProto(1, tCommPara)的时候,调用了CModbusProto的构造,但基类CMeterProto并没有构造函数(或者只有默认构造,但并没有赋值),所以,虽然基类也定义了m_tCommPara,但实际并没有赋值。如何更完美呢?

增加基类构造,同时修改下继承类的构造,如下:

先修改基类,增加构造函数:

class CMeterProto {
public:
	CMeterProto(const unsigned char& bPn, const TCommPara& tCommPara) :m_bPn(bPn), m_tCommPara(tCommPara) {}
	virtual bool Init(unsigned char bProp, unsigned char bPn, CComm *pComm);
	virtual int Run();
	unsigned char m_bProp;	//设备类型


protected:
	virtual int GetData();
	virtual int SetData() { return -1; };

	unsigned char  m_bPn;	//当前操作的测量点
	TCommPara m_tCommPara;	//通讯参数
	CComm  *m_pComm;
};

class CModbusProto : public CMeterProto 
{
public:
	CModbusProto(const unsigned char bBAPn, const TCommPara tCommPara):
		CMeterProto {	bBAPn, tCommPara } {}

	//unsigned char  m_bBAPn;	//当前操作的测量点
	//TCommPara m_tCommPara;	//通讯参数

protected:
	virtual int GetData() override;
};

如上修改后,编译运行,数据正确,结果如下:

如上,运行正常了,我将完整代码贴到下面

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <utility>

typedef struct {
	std::string strPortName;
	unsigned int dwBaudRate;
	unsigned char bByteSize;
	unsigned char bStopBits;
	unsigned char bParity;
} TCommPara;

class CComm {
public:
	void config(const TCommPara&);

private:
	TCommPara m_tCommPara;
};

void CComm::config(const TCommPara& tCommPara)
{
	m_tCommPara = tCommPara;
}

class CMeterProto {
public:
	CMeterProto(const unsigned char& bPn, const TCommPara& tCommPara) :m_bPn(bPn), m_tCommPara(tCommPara) {}
	virtual bool Init(unsigned char bProp, unsigned char bPn, CComm *pComm);
	virtual int Run();

protected:
	virtual int GetData();
	virtual int SetData() { return -1; };

	unsigned char  m_bPn;	//当前操作的测量点
	TCommPara m_tCommPara;	//通讯参数
	unsigned char m_bProp;	//设备类型
	CComm  *m_pComm;
};

bool CMeterProto::Init(unsigned char bProp, unsigned char bPn, CComm *pComm)
{
	m_bProp = bProp;
	m_bPn = bPn;
	m_pComm = pComm;
	return true;
}

int CMeterProto::GetData()
{
	std::cout << "CMeterProto-GetData" << std::endl;
	return -1;
}

int CMeterProto::Run()
{
	return GetData();
}

class CModbusProto : public CMeterProto 
{
public:
	CModbusProto(const unsigned char bBAPn, const TCommPara tCommPara):
		CMeterProto {	bBAPn, tCommPara } {}

	//unsigned char  m_bBAPn;	//当前操作的测量点
	//TCommPara m_tCommPara;	//通讯参数

protected:
	virtual int GetData() override;
};

int CModbusProto::GetData()
{
	std::cout << "CModbusProto-GetData" << std::endl;
	return 0;
}

class T485CommCtrlPara
{
public:
	T485CommCtrlPara() = default;
	T485CommCtrlPara(const T485CommCtrlPara& other);
	T485CommCtrlPara(T485CommCtrlPara&& other) noexcept;
	T485CommCtrlPara& operator=(T485CommCtrlPara&& other) noexcept;
	virtual ~T485CommCtrlPara() = default;

	std::unique_ptr<CMeterProto> pComm485Pro;
	std::unique_ptr<CComm> pComm485;
};

T485CommCtrlPara::T485CommCtrlPara(const T485CommCtrlPara& other)
{
	pComm485Pro = nullptr;
	if (other.pComm485Pro && other.pComm485)
	{
		pComm485Pro = std::unique_ptr<CMeterProto>(new CMeterProto(*other.pComm485Pro));
		pComm485 = std::unique_ptr<CComm>(new CComm(*other.pComm485));
	}
	std::cout << "copy-ctor" << std::endl;
}

T485CommCtrlPara::T485CommCtrlPara(T485CommCtrlPara&& other) noexcept
{
	if (this != &other && other.pComm485Pro && other.pComm485)
	{
		pComm485Pro = std::move(other.pComm485Pro);
		pComm485 = std::move(other.pComm485);
	}
	std::cout << "move-ctor" << std::endl;
}

T485CommCtrlPara& T485CommCtrlPara::operator=(T485CommCtrlPara&& other) noexcept
{
	if (this != &other)
	{
		pComm485Pro = std::move(other.pComm485Pro);
		pComm485 = std::move(other.pComm485);
	}

	std::cout << "move-operator-ctor" << std::endl;
	return *this;
}

int LoadBA485CommPara(TCommPara& tCommPara, T485CommCtrlPara& t485CommPara, std::vector<T485CommCtrlPara>& vec485DevCommPara) 
{
	tCommPara.strPortName = "com1";
	tCommPara.dwBaudRate = 9600;
	tCommPara.bByteSize = 8;
	tCommPara.bStopBits = 1;

	std::unique_ptr<CMeterProto> pComm485Pro(new CModbusProto(1, tCommPara));
	std::unique_ptr<CComm> pComm485(new CComm());
	pComm485->config(tCommPara);

	t485CommPara.pComm485Pro = std::move(pComm485Pro);
	t485CommPara.pComm485 = std::move(pComm485);
	t485CommPara.pComm485Pro->Init(1, 1, t485CommPara.pComm485.get());
	vec485DevCommPara.emplace_back(std::move(t485CommPara));

	return 1;
}

int Load485CommPara(std::vector<T485CommCtrlPara>& vec485DevCommPara)
{
	T485CommCtrlPara t485CommPara;
	TCommPara tCommPara;

	return LoadBA485CommPara(tCommPara, t485CommPara, vec485DevCommPara);
}

int main() 
{
	std::vector<T485CommCtrlPara> vec485DevCommPara;

	Load485CommPara(vec485DevCommPara);
	vec485DevCommPara[0].pComm485Pro->Run();
	std::cout << vec485DevCommPara.size() << std::endl;
	std::getchar();
	return 0;
}

 总结:

1:智能指针方便内存管理,但复杂度提升了不少,光有理论不够,还需要经常练习总结啊

2:chatgpt或newbing会帮忙不少,但不能太依赖了,你需要问正确的问题,但有一个专家在身边,确实能提供很大帮助啊

3:容器,智能指针是必须要现代C++的基操了,赶紧都用上吧

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

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

相关文章

Keil5 创建工程

一、 在桌面新建一个 TEST 的文件夹&#xff0c;然后在 TEST 文件夹里面新建 USER 文件夹&#xff0c;将工程名 字设为 test&#xff0c;保存在这个 USER 文件夹里面,选择对应芯片的安装包 启动代码作用&#xff1a; 1、堆栈&#xff08;SP&#xff09;的初始化&#xff1b; 2…

java.util.concurrent.Executionexception 异常

报错截图&#xff1a; 今天运行时发生了如下报错。自己捣鼓半天也没发现问题出在哪儿&#xff0c;感谢大佬的帮助&#xff0c;记录下来防止再犯。。 caused by org.apache.flink.client.program.programInvocationException: Job failed。程序调用异常。网上找了很多解决方法…

Qt 中线程池的使用

1. 线程池的原理 我们使用线程的时候就去创建一个线程&#xff0c;这样实现起来非常简便&#xff0c;但是就会有一个问题&#xff1a;如果并发的线程数量很多&#xff0c;并且每个线程都是执行一个时间很短的任务就结束了&#xff0c;这样频繁创建线程就会大大降低系统的效率&…

第14章-Python-人工智能-语言识别-调用百度语音识别

百度语音识别API是可以免费试用的&#xff0c;通过百度账号登录到百度智能云&#xff0c;在语音技术页面创建的应用&#xff0c;生成一个语音识别的应用&#xff0c;这个应用会给你一个APIKey和一个Secret Key&#xff0c;如图14.1所示。 我们在自己的程序中用 API Key 和 Secr…

轻松搞定 Git

目录 前言 一、下载 二、安装 三、基本使用 四、git的基本原理 五、通过案例学习git 5.1 创建空的项目文件夹 5.2 初始化git 5.3 创建项目文件 5.4 查看git状态 5.5 添加到暂存区 5.6 提交到本地仓库 5.7 查看git提交到本地仓库的记录 5.8 .gitignore文件 六、分…

mysql基础2——增、删、改、查

文章目录 一、DDL操作1.1 数据库操作1.2 表操作1.3 用户操作1.4 查看命令show1.5 获取帮助 二、DCL操作2.1 用户授权2.2 查看授权2.3 取消授权 三、DML操作3.1 插入insert3.2 查询select3.2.1 常规查询3.2.2 条件查询3.2.3 order by用法3.2.4 group by用法3.2.5 内连接&左连…

输入年月日,日期; 求这个日期在这一年中是第几天

输入年月日,日期; 求这个日期在这一年中是第几天 1.问题 输入年月日,日期; 求这个日期在这一年中是第几天 2.代码 利用Java中的库函数 LocalDate 是Java 8引入的一个日期类&#xff0c;用于表示日期&#xff0c;不包含时间和时区信息 到时候直接调用方法可以获取对应的天数 p…

javaee ajax请求后台 不刷新页面

jsp页面 Reg.jsp <% page language"java" contentType"text/html; charsetUTF-8"pageEncoding"UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd&qu…

【Python】经过一个点P3的一条直线垂直于已知直线,求交点坐标

一个高中数学题目&#xff0c;已经点P1和点P2构成直线&#xff0c;经过P3点做已知直线的垂线&#xff0c;求垂线与已知直线的交点坐标。 p1 [100, 15] p2 [16, 85] p3 [-50, 100] if p2[0] - p1[0] 0:# p1 p2 构成垂线&#xff0c;那么垂直线就是一条水平线x p1[0]y p3…

kafka3.x 入门 常用命令(二)

创建主题 kafka-topics.sh --bootstrap-server hadoop100:9092 --create --partitions 1 --replication-factor 3 --topic first查看主题列表 kafka-topics.sh --bootstrap-server hadoop100:9092 --list查看主题详情 kafka-topics.sh --bootstrap-server hadoop100:9092 --…

element-ui—textarea多行输入框—字数限制及优化

属性作用 show-word-limit &#xff1a;是否显示数字显示 maxlength“300”&#xff1a;设置最大值 class“public-showWordLimit”&#xff1a; 优化数字的显示的位置 :autosize “{ minRows: 2, maxRows: 8 }” &#xff1a;根据输入字符长度设置动态高度 2.代码案例 <…

vue3+vite安装配置element-plus

配置 element-plus 1. 安装 yarn add element-plus element-plus/icons-vue2. 按需引入插件 yarn add unplugin-vue-components unplugin-auto-import -D3. 配置vite.config.ts // vite.config.ts import AutoImport from unplugin-auto-import/vite import Components fro…

MySQL数据库的主从复制与读写分离

MySQL数据库的主从复制与读写分离 一、主从复制原理1、MySQL支持主从复制类型2、主从复制的原理3、主从复制的架构4、mysql主从复制延迟4、slave从服务器的配置5、验证主从复制的效果6、从服务器的故障问题解决1、遇到Slave_IO_Running:NO的情况2、遇到Slave_SQL_Running&#…

C++ - 哈希的应用

前面的文章中我们讲解了如何进行哈希表的构建以及使用实现的哈希表来模拟实现unordered_map&#xff0c;在本文中我们将继续来讲解一下哈希的应用。 位图 问题引入 首先我们来引入一个问题&#xff1a;给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&am…

Pyside6-第十三篇-布局(最后一章废话-理论篇)

本篇Pyside6的第十三篇&#xff0c;新知识点&#xff0c;布局。 布局的方式有5种。着重挑选几种将 QVBoxLayout&#xff08;垂直布局&#xff09;&#xff1a;按垂直方向排列小部件。 QHBoxLayout&#xff08;水平布局&#xff09;&#xff1a;按水平方向排列小部件。 QGridLay…

关于函数和变量命名

标识符命名基本要求 标识符是指用来识别某个实体的一个符号&#xff0c;在不同的应用环境下有不同的含义。 在计算机编程语言中&#xff0c;标识符是用户编程时使用的名字&#xff0c;用于给变量、常量、函数、语句块等命名&#xff0c;以建立起名称与使用之间的关系。 C语言…

jdk代理和cglib代理(实例推导)

目录 jdk代理和cglib代理&#xff08;实例推导&#xff09;jdk动态代理Cglib动态代理总结 jdk代理和cglib代理&#xff08;实例推导&#xff09; 更深层的探究jdk和cglib动态代理的原理 jdk动态代理 jdk动态代理&#xff08;简单实现&#xff09; 定义一个House的房源类型接口…

05 2024考研408-计算机组成原理第五章-中央处理器学习笔记

文章目录 前言一、CPU的功能与基本结构1.1、CPU的功能1.2、运算器与控制器需要实现功能1.3、运算器的基本结构1.3.1、基本结构构成&#xff08;七个部分&#xff09;1.3.2、各个部件详细介绍①算数逻辑运算单元②通用寄存器组&#xff08;介绍数据通路的基本结构2个&#xff09…

Python教程(1)——python环境的下载与安装

Python教程(1)——python环境的下载与安装 下面是下载并安装Python解释器的具体步骤&#xff0c;非常详细&#xff0c;保姆级别的教程&#xff0c;初学者一步一步的按照操作。 下载python运行环境 访问官方网站 在浏览器中打开Python的官方网站&#xff0c;网址为 https://…

【PyTest】玩转HTML报告:修改、汉化和优化

前言 Pytest框架可以使用两种测试报告&#xff0c;其中一种就是使用pytest-html插件生成的测试报告&#xff0c;但是报告中有一些信息没有什么用途或者显示的不太好看&#xff0c;还有一些我们想要在报告中展示的信息却没有&#xff0c;最近又有人问我pytest-html生成的报告&a…