C++:当vector中存储的是自定义类型对象时注意事项

看一段代码,相关:构造函数、拷贝/移动构造函数、vector扩容、智能指针

示例

如果 std::vector 中存储的是自定义类型对象,以下代码可能会导致问题:

#include <vector>
#include <iostream>

class MyClass {
public:
    int* data;

    MyClass(int value) : data(new int(value)) {}

    ~MyClass() {
        delete data;
    }

    // 忘记实现拷贝构造函数或移动构造函数
};

int main() {
    std::vector<MyClass> vec;
    vec.reserve(10);  // 预分配空间
    for (int i = 0; i < 5; ++i) {
        vec.emplace_back(i);  // 可能会导致问题
    }
    return 0;
}

原因:

  • MyClass 的析构函数释放了动态内存,但拷贝构造函数和移动构造函数未正确实现,导致扩容时拷贝或移动对象后,多个对象共享同一块内存。
  • 在释放旧空间时,会多次释放同一块内存,导致崩溃。

解决方案:

  • 正确实现拷贝构造函数和移动构造函数,或者使用智能指针。

在上述代码中,std::vector<MyClass> vec; 是一个以 MyClass 为元素类型的容器,在执行 vec.emplace_back(i); 时是否调用拷贝构造函数还是移动构造函数取决于 MyClass 是否支持移动语义编译器的优化行为


emplace_back(i) 的行为

vec.emplace_back(i) 会直接在 std::vector 已分配的内存中构造一个新的 MyClass 对象,而不会调用拷贝构造函数或移动构造函数。它通过传递 i 调用适配的构造函数。在你的例子中,它调用了 MyClass(int value) 构造函数。


当扩容发生时

std::vector 需要扩容时(例如,超过当前容量),会分配一块新的更大的连续内存,并将旧内存中的元素移动到新内存中。这时会发生拷贝或移动构造。

  • 如果 MyClass 支持移动构造函数,则会优先调用移动构造函数。
  • 如果 MyClass 没有定义移动构造函数,但支持拷贝构造函数,则会调用拷贝构造函数。
  • 如果 MyClass 同时不支持拷贝构造和移动构造,则会报编译错误。

编译器如何选择

1. 移动构造函数优先

如果 MyClass 定义了移动构造函数(或者编译器能够生成一个隐式移动构造函数),那么扩容时 std::vector 会调用移动构造函数。例如:

class MyClass {
public:
    int* data;

    MyClass(int value) : data(new int(value)) {}
    ~MyClass() { delete data; }

    // 移动构造函数
    MyClass(MyClass&& other) noexcept : data(other.data) {
        other.data = nullptr;
    }

    // 禁用拷贝构造函数
    MyClass(const MyClass&) = delete;
};

int main() {
    std::vector<MyClass> vec;
    vec.emplace_back(1);  // 调用构造函数
    vec.emplace_back(2);  // 可能扩容时调用移动构造函数
}
2. 没有移动构造函数时

如果没有定义移动构造函数但定义了拷贝构造函数,或者编译器无法隐式生成移动构造函数,则会调用拷贝构造函数。例如:

class MyClass {
public:
    int* data;

    MyClass(int value) : data(new int(value)) {}
    ~MyClass() { delete data; }

    // 拷贝构造函数
    MyClass(const MyClass& other) : data(new int(*other.data)) {}

    // 禁用移动构造函数
    MyClass(MyClass&&) = delete;
};

int main() {
    std::vector<MyClass> vec;
    vec.emplace_back(1);  // 调用构造函数
    vec.emplace_back(2);  // 可能扩容时调用拷贝构造函数
}
3. 没有拷贝或移动构造函数

如果既没有拷贝构造函数也没有移动构造函数,并且编译器无法生成隐式的构造函数,编译器会报错。


你的代码分析

对于你的 MyClass

class MyClass {
public:
    int* data;

    MyClass(int value) : data(new int(value)) {}
    ~MyClass() { delete data; }

    // 注意:未显式提供拷贝构造或移动构造
};
  • 如果没有显式定义拷贝和移动构造函数,编译器会隐式生成一个 拷贝构造函数 和一个 移动构造函数,扩容时优先调用隐式移动构造函数。
  • 但是,由于没有显式处理 data 的动态内存管理(例如,深拷贝或移动语义),可能会导致 重复释放内存 的问题,从而引发崩溃。

结论

  1. emplace_back(i) 时,不会调用拷贝构造或移动构造,而是直接调用匹配的构造函数。
  2. 在扩容时:
    • 如果 MyClass 支持移动构造函数,调用移动构造函数。
    • 如果不支持移动构造函数,但支持拷贝构造函数,调用拷贝构造函数。
    • 如果两者都不支持,则报错。
  3. 为了安全,建议显式定义拷贝构造和移动构造函数,并正确管理动态内存。

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

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

相关文章

Leetcode322.零钱兑换(HOT100)

链接 代码&#xff1a; class Solution { public:int coinChange(vector<int>& coins, int amount) {vector<int> dp(amount1,amount1);//要兑换amount元硬币&#xff0c;我们就算是全选择1元的硬币&#xff0c;也不过是amount个&#xff0c;所以初始化amoun…

网络安全期末复习

第1章 网络安全概括 &#xff08;1&#xff09;用户模式切换到系统配置模式&#xff08;enable&#xff09;。 &#xff08;2&#xff09;显示当前位置的设置信息&#xff0c;很方便了解系统设置&#xff08;show running-config&#xff09;。 &#xff08;3&#xff09;显…

鸿蒙进阶篇-自定义组件

大家好&#xff0c;我是鸿蒙开天组&#xff0c;今天咱们来学习自定义组件。 一、自定义组件定义 在ArkUI中&#xff0c;UI显示的内容均为组件&#xff0c;由框架直接提供的称为系统组件&#xff0c;由开发者定义的称为自定义组件。在进行 UI 界面开发时&#xff0c;通常不是简…

深入浅出 WebSocket:构建实时数据大屏的高级实践

简介 请参考下方&#xff0c;学习入门操作 基于 Flask 和 Socket.IO 的 WebSocket 实时数据更新实现 在当今数字化时代&#xff0c;实时性是衡量互联网应用的重要指标之一。无论是股票交易、在线游戏&#xff0c;还是实时监控大屏&#xff0c;WebSocket 已成为实现高效、双向…

一键AI换脸软件,支持表情控制,唇形同步Facefusion-3.0.0发布!支持N卡和CPU,一键启动包

嗨,小伙伴们!还记得小编之前介绍的FaceFusion 2.6.1吗?今天给大家带来超级exciting的消息 —— FaceFusion 3.0.0闪亮登场啦! &#x1f31f; 3.0.0版本更新 &#x1f3d7;️ 全面重构:修复了不少小虫子,运行更稳定,再也不怕突然罢工啦! &#x1f600; Live Portrait功能:新增…

spring boot框架漏洞复现

spring - java开源框架有五种 Spring MVC、SpringBoot、SpringFramework、SpringSecurity、SpringCloud spring boot版本 版本1: 直接就在根下 / 版本2:根下的必须目录 /actuator/ 端口:9093 spring boot搭建 1:直接下载源码打包 2:运行编译好的jar包:actuator-testb…

hhdb数据库介绍(10-8)

首页 管理平台通过数据可视方式在首页功能中实时展示计算节点集群的数据量、访问流量、集群组件状态、告警事件、安全防控等用户关心的信息。 集群安全 邮件通知&#xff1a;根据通知设置中监控开关是否打开判断&#xff0c;分为&#xff1a;全部开启、未开启、部分开启&…

Vue前端开发-slot传参

slot 又称插槽&#xff0c;它是在子组件中为父组件提供的一个占位符&#xff0c;使用来表示&#xff0c;通过这个占位符&#xff0c;父组件可以向中填充任意的内容代码&#xff0c;这些代码将自动替换占位符的位置&#xff0c;从而轻松实现在父组件中控制子组件内容的需求。 作…

18:(标准库)DMA二:DMA+串口收发数据

DMA串口收发数据 1、DMA串口发送数据2、DMA中断串口接收定长数据包3、串口空闲中断DMA接收不定长数据包 1、DMA串口发送数据 当串口的波特率大于115200时&#xff0c;可以通过DMA1进行数据搬运&#xff0c;以防止数据的丢失。如上图所示&#xff1a;UART1的Tx发送请求使用DMA1的…

2024 java大厂面试复习总结(一)(持续更新)

10年java程序员&#xff0c;2024年正好35岁&#xff0c;2024年11月公司裁员&#xff0c;记录自己找工作时候复习的一些要点。 java基础 hashCode()与equals()的相关规定 如果两个对象相等&#xff0c;则hashcode一定也是相同的两个对象相等&#xff0c;对两个对象分别调用eq…

深度学习5

一、模型保存与加载 1、序列化方式 保存方式&#xff1a;torch.save(model, "model.pkl") 打开方式&#xff1a;model torch.load("model.pkl", map_location"cpu") ​ import torch import torch.nn as nnclass MyModle(nn.Module):def __ini…

Redis五大基本类型——Zset有序集合命令详解(命令用法详解+思维导图详解)

目录 一、Zset有序集合类型介绍 二、常见命令 1、ZADD 2、ZCARD 3、ZCOUNT 4、ZRANGE 5、ZREVRANGE 6、ZRANGEBYSCORE 7、ZREVRANGEBYSCORE 8、ZPOPMAX 9、ZPOPMIN 10、ZRANK 11、ZREVRANK 12、ZSCORE 13、ZREM 14、ZREMRANGEBYRANK 15、ZREMRANGEBYSCORE 16…

ARM架构 AArch64 基础知识介绍

介绍 aarch64是 ARM 架构的 64 位版本&#xff0c;它是 ARMv8 架构的一部分&#xff0c;被设计用来提供更高的性能和更大的地址空间&#xff0c;同时保持与 32 位 ARM 架构的兼容性。AArch64 是 ARMv8 的 64 位指令集架构&#xff08;ISA&#xff09;&#xff0c;它提供了丰富的…

Rust中Tracing 应用指南

欢迎来到这篇全面的Rust跟踪入门指南。Rust 的tracing是一个用于应用程序级别的诊断和调试的库。它提供了一种结构化的、异步感知的方式来记录日志和跟踪事件。与传统的日志记录相比&#xff0c;tracing能够更好地处理复杂的异步系统和分布式系统中的事件跟踪&#xff0c;帮助开…

极狐GitLab 17.6 正式发布几十项与 DevSecOps 相关的功能【三】

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料&#xff1a; 极狐GitLab 官网极狐…

WinFrom调用webapi接口另一个方法及其应用实例

1.调用接口方法 代码如下&#xff1a; public class WebAPI{#region WebAPI调用 public async Task<string> Call_Webapi(string Url, string Json) //url传入的是接口名称&#xff0c;json传入的是接口参数{string responseBody string.Empty; //responseBod…

elasticsearch的索引模版使用方法

5 索引模版⭐️⭐️⭐️⭐️⭐️ 索引模板就是创建索引时要遵循的模板规则索引模板仅对新创建的索引有效&#xff0c;已经创建的索引并不受索引模板的影响 5.1 索引模版的基本使用 1.查看所有的索引模板 GET 10.0.0.91:9200/_index_template2.创建自定义索引模板 xixi &…

从零开始学GeoServer源码(二)添加支持arcgis切片功能

文章目录 参考文章环境背景1、配置打包好的程序1.1、下载GeoServer的war包1.2、下载GeoWebCache1.3、拷贝jar包1.4、修改配置文件1.4.1、拷贝geowebcache-arcgiscache-context.xml1.4.2、修改geowebcache-core-context.xml1.4.3、修改geowebcache-servlet.xml 1.5、配置切片信息…

Redis 可观测最佳实践

Redis 介绍 Redis 是一个开源的高性能键值对&#xff08;key-value&#xff09;数据库。它通常用作数据库、缓存和消息代理。Redis 支持多种类型的数据结构&#xff0c;Redis 通常用于需要快速访问的场景&#xff0c;如会话缓存、全页缓存、排行榜、实时分析等。由于其高性能和…

HarmonyOs鸿蒙开发实战(21)=>组件间通信@ohos/liveeventbus

1.简介 LiveEventBus是一款消息总线&#xff0c;具有生命周期感知能力&#xff0c;支持Sticky&#xff0c;支持跨进程&#xff0c;支持跨APP发送消息。 2.下载安装 ohpm install ohos/liveeventbus 3.订阅&#xff0c;注册监听 4.发送事件 5. 完成 > 记得关注博主&#xff…