智能指针+拷贝构造+vector容器+多态引起的bug

news2024/12/22 20:32:55

今天在调试一段代码的时候,VC编译提示:

error C2280: “T485CommCtrlPara::T485CommCtrlPara(const T485CommCtrlPara &)”: 尝试引用已删除的函数

函数执行部分如下:

 看意思是这个pComm485Pro已经消亡了,后续push_back到vec485DevCommPara有问题,但智能指针已经move了,这样new出来资源的所有权应该已经转移了,为啥还会有问题呢?

找了下chatgpt和newbing查了下,他提供了一个馊主意:

 这个语义上说不通啊,显然不是这个问题导致。

本文就针对这个问题,详细分析下,先把有问题代码精简下,贴出来:

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

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

class CComm
{
public:
    config(){;};

class CMeterProto
{
;
}

class cModbusProto:public CMeterProto
{
public:
    CModbusProto(const unsigned char bBAPn, const TCommPara tCommPara)
    : m_bBAPn(bBAPn), m_tCommPara(tCommPara) 
    {
    }
private:
    unsigned char m_bBAPn;
    TCommPara m_tCommPara;
}

typedef struct {
    std::unique_ptr<CMeterProto> pComm485Pro{nullptr};
    CComm cMterCom;
}T485CommCtrlPara;

int LoadBA485CommPara(TCommPara &tCommPara, T485CommCtrlPara &t485CommPara, 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;
    vector<T485CommCtrlPara> vec485DevCommPara;
    
    int nBaNum = LoadBA485CommPara(tCommPara, t485CommPara, vec485DevCommPara);
    
    std::count<<nBaNum<<std::endl;
}
 

通过将如上代码交给chatgpt分析,开始他巴拉巴拉说没啥问题,后来我把VC的编译信息交给他

如上代码,在vs 2015上编译,提示:error C2280: “T485CommCtrlPara::T485CommCtrlPara(const T485CommCtrlPara &)”: 尝试引用已删除的函数

这个时候它开始分析代码了,经过几轮的修改,他终于还是发现了是push_back的问题。

主要原因就是push_back本质上是调用拷贝构造函数,将资源拷贝到vector的堆空间上,如果使用默认拷贝函数,就是浅拷贝,不会将pComm485Pro的资源拷贝过去,所以push_back拷贝的只是一个地址,实际资源并没有拷贝。

原因知道后,修改就很简单了,实现拷贝构造函数即可,如上的

typedef struct {
    std::unique_ptr<CMeterProto> pComm485Pro{nullptr};
    CComm cMterCom;
}T485CommCtrlPara;

要定义下拷贝构造函数,按照chatgpt的实现,

T485CommCtrlPara(const T485CommCtrlPara& other)
        : pComm485Pro(other.pComm485Pro ? std::make_unique<CMeterProto>(*other.pComm485Pro) : nullptr),
          cMterCom(other.cMterCom)
    {
        // 在自定义拷贝构造函数中,正确拷贝智能指针成员
    }

编译不了,因为直接在struct中定义拷贝构造,vc编译还有问题,既然如此,修改成类方式好了:

  如上,编译后的时候提示了一个问题:

error C2512: “T485CommCtrlPara”: 没有合适的默认构造函数可用

这个就是常见的三法则了,如果手动定义了拷贝构造,编译器将不再自动生成默认构造,此处需要添加上:

 就可以正常运行了。

按照如上方式,修改实际工程中的代码,发现一个问题:

error C2259: “CMeterProto”: 不能实例化抽象类

这个就是CMeterProto中有一个纯虚函数:

virtual int GetData() = 0;

因为抽象类在new的时候无法指向具体的位置,导致实例化类失败,修改为普通虚函数就可以了:

virtual int GetData() {return 0};

如上,修改完成后就都编译,运行成功了。

将全部代码贴下来吧:

 总结:

1:使用智能指针可以通过std::move转移所有权。

2:使用vector变量push_back一个对象或变量的时候,本质上是执行拷贝构造。

3:如果对象中有申请资源时,如果new内存,句柄等,需要手动实现拷贝构造

4:抽象类不能new实例

5:碰到问题的时候,一心指望chatgpt是不够的,自己对于基本原理还是要门清,否则带去沟里也不知道

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

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

相关文章

高速电路设计系列分享-信号链精度分析(中)

目录 概要 整体架构流程 技术名词解释 技术细节 1.直流无源误差 小结 概要 提示&#xff1a;这里可以添加技术概要 在任何设计中&#xff0c;信号链精度分析都可能是一项非常重要的任务&#xff0c;必须充分了解。之前&#xff0c; 我们讨论了在整个信号链累积起来并且最终会…

统一日志处理----AOP/面向切面编程

AOP Aspect Oriented Programing&#xff1a;面向切面编程 AOP是对OOP的补充&#xff0c;进一步提高编程的效率 AOP的常见使用场景有&#xff1a;权限检查、记录日志、事务管理等 如下图所示结构&#xff0c;每个模块都含有相同的系统需求&#xff0c;而这些需求和模块本身的功…

Flutter进阶-动画详解

目录 动画类别 一、隐式&#xff08;全自动&#xff09;动画 二、显式动画&#xff08;手动控制&#xff09; 三、其他动画(CustomPainter) 动画类别 Flutter 中有多种类型的动画&#xff1a; 隐式动画&#xff1a;通过更改部件属性自动触发的预定义动画&#xff0c;例如 …

什么是cookie

1、cookie是什么 Cookie&#xff0c;有时也用其复数形式Cookies。类型为“小型文本文件”&#xff0c;是某些网站为了辨别用户身份&#xff0c;进行Session跟踪而储存在用户本地终端上的数据&#xff08;通常经过加密&#xff09;&#xff0c;由用户客户端计算机暂时或永久保存…

Python强类型编程

Python是一门强类型的动态类型语言&#xff0c;具体如下特性&#xff1a; 可以动态构造脚本执行、修改函数、对象类型结构、变量类型但不允许类型不匹配的操作 第一个例子体现动态性&#xff1a;用字符串直接执行代码&#xff0c;动态构建了一个函数并执行&#xff0c;甚至给…

力扣744.寻找比目标字母大的最小字符(java暴力查找法,二分查找法)

题目描述&#xff1a; 给你一个字符数组 letters&#xff0c;该数组按非递减顺序排序&#xff0c;以及一个字符 target。letters 里至少有两个不同的字符。 返回 letters 中大于 target 的最小的字符。如果不存在这样的字符&#xff0c;则返回 letters 的第一个字符。 [外链…

岭回归(Ridge)不同alpha值对归回结果的影响

对于有些矩阵&#xff0c;矩阵中某个元素的一个很小的变动&#xff0c;会引起最后计算结果误差很大&#xff0c;这种矩阵称为“病态矩阵”。有些时候不正确的计算方法也会使一个正常的矩阵在运算中表现出病态。对于高斯消去法来说&#xff0c;如果主元&#xff08;即对角线上的…

亚马逊测评:如何有效使用IP和养号设备环境

随着网络科技的崛起&#xff0c;越来越多的本土企业入驻亚马逊电子商务平台上&#xff0c;这导致了对产品评价需求的激增。然而&#xff0c;评价并非随意进行&#xff0c;它需要多方面的资源&#xff0c;并需要密切注意一些重要环节。以下是我分享给大家一些宝贵的知识&#xf…

如何实现敏捷交付中的自动化测试优化

在提及自动化测试的时候&#xff0c;很多人会把工具的使用等同于自动化测试。自动化测试应该是一个策略性的系统化工程&#xff0c;不只有自动化工具。自动化测试要发挥其频繁快速的质量反馈作用&#xff0c;还需要团队从文化和技术上去建设和学习。 提到敏捷交付&#xff0c;…

数据库监控与调优【十二】—— JOIN语句优化

JOIN语句优化-JOIN种类、算法与原理 JOIN的种类 笛卡尔连接&#xff08;cross join&#xff09; -- 举例&#xff1a;通过笛卡尔连接查询两张表的结果集和单查两张表的结果集对比 SELECT count( * ) FROM users a CROSS JOIN orders b; SELECT ( SELECT count( * ) FROM user…

SpringBoot + Vue前后端分离项目实战 || 四:用户管理功能实现

系列文章&#xff1a; SpringBoot Vue前后端分离项目实战 || 一&#xff1a;Vue前端设计 SpringBoot Vue前后端分离项目实战 || 二&#xff1a;Spring Boot后端与数据库连接 SpringBoot Vue前后端分离项目实战 || 三&#xff1a;Spring Boot后端与Vue前端连接 文章目录 前端…

微服务: sleuth和zipkin的用处与zipkin安装使用(下)

目录 0. 上篇传送门: 1. 前言简介 mq安装传送门: 微服务: 01-rabbitmq的应用场景及安装(docker) 1.1 Sleuth是一款分布式跟踪解决方案。 1.2 Zipkin是一个开源的分布式跟踪系统。 2. zipkin安装方式 2.1 windows下安装zipkin: 2.1.0 下载jar包位置 2.1.1 下载后,找…

数值计算例题整理

数值计算 一、误差的来源和分类二、有效数字第一个大题&#xff08;非线性方程组的迭代法&#xff09;第二个大题&#xff08;LU分解&#xff09;第三个大题&#xff08;牛顿插值法&#xff09;第四个大题&#xff08;直线拟合&#xff09; 一、误差的来源和分类 误差是描述数…

Git 原理和使用

Git 安装 Git是开放源代码的代码托管⼯具&#xff0c;最早是在Linux下开发的。开始也只能应⽤于Linux平台&#xff0c;后⾯慢慢的被移植到windows下&#xff0c;现在&#xff0c;Git可以在Linux、Unix、Mac和Windows这⼏⼤平台上正常运⾏了。 Linux-centos 安装git sudo yu…

8.3 PowerBI系列之DAX函数专题-矩阵Matrix中高亮显示最大最小值

需求 用颜色标量年度最大最小值 用颜色标示折线的最大值最小值 实现 在条件格式–规则–基于字段进行计算 度量值 is_max_min var displayed_data calculatetable( addcolumns( summarize(‘订单表’&#xff0c;‘产品表’[商品次级类别]&#xff0c;‘订单表’[订单日…

arcgis栅格影像裁剪--shp

1、打开软件&#xff0c;导入数据&#xff0c;如下&#xff1a; 2、裁剪面形状如下&#xff0c;为shp文件&#xff1a; 3、在arctoolbox中找到"数据管理工具"--"栅格"--"栅格处理"--"裁剪"工具&#xff0c;如下&#xff1a; 4、打开裁…

(ESP32)报错-portTICK_RATE_MS‘ undeclared

&#xff08;ESP32&#xff09;报错-portTICK_RATE_MS undeclared 问题详情ESP- IDF未正确设置 问题详情 报错提示 portTICK_RATE_MS undeclared (first use in this function); did you mean portTICK_PERIOD_MS?具体情况 已经引用相关头文件&#xff0c;并且右键后可以大概…

leetcode 2462. Total Cost to Hire K Workers(雇用 K 名员工的总成本)

每次从 开头candidates个 和 末尾candidates个 工人中选择一个cost最小的。 如果有2个工人cost相同&#xff0c;就选index较小的。 每个工人的cost在数组costs里。 直到雇够k个工人。 问雇k个工人需要多少cost. 思路&#xff1a; 可以考虑用一个优先队列&#xff0c;按cost排…

2023开放原子全球开源峰会——一场开发者的盛宴

文章目录 上午场下午场开发者之夜 #“2023我在开源峰会”特别征文# 2023开放原子全球开源峰会&#xff0c;6月11日-13日在北京盛大召开&#xff0c;开幕第一天正好是周六&#xff0c;没什么事情&#xff0c;一大早就过去了&#xff0c;早晨大概7点出发&#xff0c;公交、地铁一…

Docker Desktop 安装使用教程

一、前言 作为开发人员&#xff0c;在日常开发中&#xff0c;我们需要在本地去启动一些服务&#xff0c;如&#xff1a;redis、MySQL等&#xff0c;就需要去下载这些在本地去启动&#xff0c;操作较为繁琐。此时&#xff0c;我们可以使用Docker Desktop&#xff0c;来搭建我们需…